Easy3D 2.5.3
Tutorial_207_RealCamera

The source file containing the main() function:

1/********************************************************************
2 * Copyright (C) 2015 Liangliang Nan <liangliang.nan@gmail.com>
3 * https://3d.bk.tudelft.nl/liangliang/
4 *
5 * This file is part of Easy3D. If it is useful in your research/work,
6 * I would be grateful if you show your appreciation by citing it:
7 * ------------------------------------------------------------------
8 * Liangliang Nan.
9 * Easy3D: a lightweight, easy-to-use, and efficient C++ library
10 * for processing and rendering 3D data.
11 * Journal of Open Source Software, 6(64), 3255, 2021.
12 * ------------------------------------------------------------------
13 *
14 * Easy3D is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License Version 3
16 * as published by the Free Software Foundation.
17 *
18 * Easy3D is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ********************************************************************/
26
27#include "viewer.h"
28#include <easy3d/util/resource.h>
29#include <easy3d/util/initializer.h>
30
31
38
39using namespace easy3d;
40
41int main(int argc, char **argv) {
42 // initialize Easy3D.
43 initialize();
44
45 // the bundler file (We use only the camera intrinsic and extrinsic parameters).
46 const std::string bundler_file = resource::directory() + "/data/fountain/bundle.out";
47 // the point cloud file.
48 const std::string cloud_file = resource::directory() + "/data/fountain/pointcloud.ply";
49
50 RealCamera viewer(EXAMPLE_TITLE, bundler_file, cloud_file);
51
52 // run the viewer
53 return viewer.run();
54}
Definition: collider.cpp:182
void initialize(bool use_log_file, bool use_setting_file, const std::string &resource_dir)
Initialization of Easy3D.
Definition: initializer.cpp:35

The header file of the viewer class:

1/********************************************************************
2 * Copyright (C) 2015 Liangliang Nan <liangliang.nan@gmail.com>
3 * https://3d.bk.tudelft.nl/liangliang/
4 *
5 * This file is part of Easy3D. If it is useful in your research/work,
6 * I would be grateful if you show your appreciation by citing it:
7 * ------------------------------------------------------------------
8 * Liangliang Nan.
9 * Easy3D: a lightweight, easy-to-use, and efficient C++ library
10 * for processing and rendering 3D data.
11 * Journal of Open Source Software, 6(64), 3255, 2021.
12 * ------------------------------------------------------------------
13 *
14 * Easy3D is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License Version 3
16 * as published by the Free Software Foundation.
17 *
18 * Easy3D is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ********************************************************************/
26
27#ifndef EASY3D_TUTORIAL_REAL_CAMERA_H
28#define EASY3D_TUTORIAL_REAL_CAMERA_H
29
30#include <easy3d/viewer/viewer.h>
31
32// This RealCamera class visualizes a model from the view point
33// given the camera's intrinsic and extrinsic parameters. These
34// camera parameters can (usually) be recovered using camera
35// calibration or structure from motion techniques.
36
37namespace easy3d {
38 class Camera;
39 class Texture;
40 class LinesDrawable;
41}
42
43class RealCamera : public easy3d::Viewer
44{
45public:
46 RealCamera(const std::string& title,
47 const std::string& bundler_file,
48 const std::string& cloud_file);
49
51
60 static easy3d::vec3 camera_pos(const easy3d::mat3 &R, const easy3d::vec3 &t);
61
83 static easy3d::vec3 pixel_to_ray(int img_x, int img_y,
84 float fx, float fy, float skew, float cx, float cy,
85 const easy3d::mat3 &R, const easy3d::vec3 &t, bool convert = true);
86
107 static easy3d::vec2 point_to_pixel(const easy3d::vec3 &p,
108 float fx, float fy, float skew, float cx, float cy,
109 const easy3d::mat3 &R, const easy3d::vec3 &t, bool convert = true);
110
111protected:
112 bool key_press_event(int key, int modifiers) override;
113 bool mouse_free_move_event(int x, int y, int dx, int dy, int modifiers) override;
114
115 easy3d::Rect calculate_image_rect() const;
116
117private:
118 struct CameraPara {
119 int w, h; // image size
120 float fx, fy; // focal length
121 float cx, cy; // principal point
122 easy3d::mat3 R; // rotation
123 easy3d::vec3 t; // the translation
124 };
125 std::vector<CameraPara> views_;
126 int current_view_;
127
128 bool read_bundler_file(const std::string& file_name);
129
130 // K [R t] -> easy3d camera representation
131 bool KRT_to_camera(int view_index, easy3d::Camera* c, bool ground_truth);
132
133 void update_cameras_drawable(bool ground_truth);
134
135 void load_image();
136
137 void post_draw() override;
138
139private:
140 easy3d::Texture* texture_;
141 easy3d::LinesDrawable* cameras_drawable_;
142 easy3d::LinesDrawable* ray_drawable_; // in 3D
143 easy3d::LinesDrawable* cross_drawable_; // in image
144};
145
146
147#endif // EASY3D_TUTORIAL_REAL_CAMERA_H
A perspective or orthographic camera.
Definition: camera.h:116
The GenericRect class defines a rectangle in the 2D space.
Definition: rect.h:42
The drawable for rendering a set of line segments, e.g., edges of a mesh, vector fields.
Definition: drawable_lines.h:40
3x3 matrix. Extends Mat with 3D-specific functionality and constructors.
Definition: mat.h:1620
OpenGL texture.
Definition: texture.h:44
The built-in Easy3D viewer.
Definition: viewer.h:61

The source file of the viewer class:

1/********************************************************************
2 * Copyright (C) 2015 Liangliang Nan <liangliang.nan@gmail.com>
3 * https://3d.bk.tudelft.nl/liangliang/
4 *
5 * This file is part of Easy3D. If it is useful in your research/work,
6 * I would be grateful if you show your appreciation by citing it:
7 * ------------------------------------------------------------------
8 * Liangliang Nan.
9 * Easy3D: a lightweight, easy-to-use, and efficient C++ library
10 * for processing and rendering 3D data.
11 * Journal of Open Source Software, 6(64), 3255, 2021.
12 * ------------------------------------------------------------------
13 *
14 * Easy3D is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License Version 3
16 * as published by the Free Software Foundation.
17 *
18 * Easy3D is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ********************************************************************/
26
27#include "viewer.h"
28
29#include <easy3d/core/point_cloud.h>
30#include <easy3d/renderer/opengl.h>
31#include <easy3d/renderer/drawable_points.h>
32#include <easy3d/renderer/drawable_lines.h>
33#include <easy3d/renderer/camera.h>
34#include <easy3d/renderer/manipulated_camera_frame.h>
35#include <easy3d/renderer/texture_manager.h>
36#include <easy3d/renderer/shape.h>
37#include <easy3d/renderer/renderer.h>
38#include <easy3d/renderer/shader_program.h>
39#include <easy3d/renderer/shader_manager.h>
40#include <easy3d/renderer/transform.h>
41#include <easy3d/util/string.h>
42#include <easy3d/util/file_system.h>
43#include <easy3d/util/resource.h>
44
45
46using namespace easy3d;
47
48
49const float scale = 0.3f;
50
51RealCamera::RealCamera(const std::string& title,
52 const std::string& bundler_file,
53 const std::string& cloud_file)
54 : Viewer(title, 4, 3, 2, false, false)
55 , current_view_(0)
56 , texture_(nullptr)
57 , cameras_drawable_(nullptr)
58 , ray_drawable_(nullptr)
59 , cross_drawable_(nullptr)
60{
61 // Read the point cloud
62 if (add_model(cloud_file)) {
63 auto drawable = current_model()->renderer()->get_points_drawable("vertices");
64 drawable->set_point_size(5.0f);
65
66 // Read the camera parameters from the bundler file.
67 if (read_bundler_file(bundler_file))
68 update_cameras_drawable(true);
69 else
70 LOG(ERROR) << "failed to load bundler file";
71
72 camera()->setUpVector(vec3(0, 1, 0));
73 camera()->setViewDirection(vec3(0, 0, -1));
74 camera_->showEntireScene();
75 }
76 else
77 LOG(ERROR) << "failed to load point cloud";
78
79 usage_string_ =
80 "---------------- Real Camera usage ------------------ \n"
81 "Press 'Space' to switch views \n"
82 "Press 'H' to show/hide the cameras \n"
83 "Move cursor on image to show corresponding 3D ray \n"
84 "Move cursor on scene to show corresponding image point\n"
85 "----------------------------------------------------- \n";
86}
87
88
89bool RealCamera::key_press_event(int key, int modifiers) {
90 if (ray_drawable_)
91 ray_drawable_->set_visible(false);
92 if (cross_drawable_)
93 cross_drawable_->set_visible(false);
94
95 if (key == KEY_SPACE) {
96 if (!views_.empty()) {
97 current_view_ = (current_view_ + 1) % static_cast<int>(views_.size());
98 const bool ground_truth = true;
99 if (KRT_to_camera(current_view_, camera(), ground_truth)) {
100 update_cameras_drawable(ground_truth);
101 std::cout << "----- view " << current_view_ << ": " << (ground_truth ? "ground truth view" : "calibration view") << std::endl;
102 set_title("RealCamera: View_" + std::to_string(current_view_));
103 const CameraPara &c = views_[current_view_];
104 // make sure the aspect ratio (actual size does not matter)
105 resize(static_cast<int>(static_cast<float>(c.w) * scale), static_cast<int>(static_cast<float>(c.h) * scale));
106 }
107 }
108 return true;
109 }
110 else if (key == KEY_1) {
111 if (!views_.empty()) {
112 const bool ground_truth = false;
113 if (KRT_to_camera(current_view_, camera(), ground_truth)) {
114 update_cameras_drawable(ground_truth);
115 std::cout << "----- view " << current_view_ << ": " << (ground_truth ? "ground truth view" : "calibration view") << std::endl;
116 set_title("RealCamera: View_" + std::to_string(current_view_));
117 const CameraPara &c = views_[current_view_];
118 // make sure the aspect ratio (actual size does not matter)
119 resize(static_cast<int>(static_cast<float>(c.w) * scale), static_cast<int>(static_cast<float>(c.h) * scale));
120 }
121 }
122 return true;
123 }
124 else if (key == KEY_2) {
125 if (!views_.empty()) {
126 const bool ground_truth = true;
127 if (KRT_to_camera(current_view_, camera(), ground_truth)) {
128 update_cameras_drawable(ground_truth);
129 std::cout << "----- view " << current_view_ << ": " << (ground_truth ? "ground truth view" : "calibration view") << std::endl;
130 set_title("RealCamera: View_" + std::to_string(current_view_));
131 const CameraPara &c = views_[current_view_];
132 // make sure the aspect ratio (actual size does not matter)
133 resize(static_cast<int>(static_cast<float>(c.w) * scale), static_cast<int>(static_cast<float>(c.h) * scale));
134 }
135 }
136 return true;
137 }
138 else if (key == KEY_H) {
139 if (cameras_drawable_) {
140 cameras_drawable_->set_visible(!cameras_drawable_->is_visible());
141 update();
142 }
143 return true;
144 }
145 else
146 return Viewer::key_press_event(key, modifiers);
147}
148
149
150void RealCamera::load_image() {
151 const std::string image_file = resource::directory() + "/data/fountain/images/" + string::to_string(current_view_, 4, '0') + ".jpg";
152 if (file_system::is_file(image_file)) {
153 texture_ = TextureManager::request(image_file);
154 }
155 update();
156}
157
158
159bool RealCamera::KRT_to_camera(int view_index, Camera* c, bool ground_truth) {
160 if (view_index < 0 || view_index >= views_.size()) {
161 std::cerr << "Error: invalid view index (" << view_index << ")" << std::endl;
162 return false;
163 }
164
165 const CameraPara& cam = views_[view_index];
166
167 if (ground_truth) {
168 // R: rotation matrix of world frame in camera frame
169 const quat q(inverse(cam.R)); // the inverse rotation represented by a quaternion
170 c->setOrientation(q);
171 c->setPosition(-q.rotate(cam.t)); // camera position: -inverse(rot) * t
172 const float proj11 = 2.0f * cam.fy / static_cast<float>(cam.h); // proj[1][1]
173 const float fov = 2.0f * std::atan(1.0f / proj11);
174 c->setFieldOfView(fov);
175 }
176 else {
178 cam.fx, cam.fy, 0.0f,
179 cam.cx, cam.cy,
180 cam.R, cam.t, true);
181 }
182
183 load_image();
184
185 return true;
186}
187
188
189void RealCamera::update_cameras_drawable(bool ground_truth)
190{
191 if (!cameras_drawable_) {
192 cameras_drawable_ = new LinesDrawable("cameras");
193 add_drawable(cameras_drawable_); // add the camera drawable to the viewer
194 cameras_drawable_->set_uniform_coloring(vec4(0, 0, 1, 1.0f));
195 cameras_drawable_->set_line_width(2.0f);
196 }
197
198 std::vector<vec3> vertices;
199 for (std::size_t i = 0; i < views_.size(); ++i) {
200 Camera c;
201 KRT_to_camera(static_cast<int>(i), &c, ground_truth);
202 std::vector<vec3> points;
203 shape::create_camera(points, c.sceneRadius() * 0.03f, c.fieldOfView(), static_cast<float>(views_[i].h)/static_cast<float>(views_[i].w));
204 const mat4& m = c.frame()->worldMatrix();
205 for (auto& p : points)
206 vertices.push_back(m * p);
207 }
208
209 cameras_drawable_->update_vertex_buffer(vertices);
210}
211
212
213Rect RealCamera::calculate_image_rect() const {
214 if (texture_ == nullptr) {
215 LOG_N_TIMES(3, ERROR) << "image not shown";
216 return {0, 0, 0, 0};
217 }
218
219 int tex_w = texture_->width();
220 int tex_h = texture_->height();
221 const float image_as = static_cast<float>(tex_w) / static_cast<float>(tex_h);
222 const float viewer_as = static_cast<float>(width()) / static_cast<float>(height());
223 if (image_as < viewer_as) {// thin
224 tex_h = static_cast<int>(static_cast<float>(height()) * scale);
225 tex_w = static_cast<int>(static_cast<float>(tex_h) * image_as);
226 }
227 else {
228 tex_w = static_cast<int>(static_cast<float>(width()) * scale);
229 tex_h = static_cast<int>(static_cast<float>(tex_w) / image_as);
230 }
231
232 return Rect(static_cast<float>(20), static_cast<float>(20 + tex_w), 40.0f, static_cast<float>(40 + tex_h));
233}
234
235
236void RealCamera::post_draw() {
237 Viewer::post_draw();
238
239 if (texture_ == nullptr)
240 return;
241
242 const Rect image_rect = calculate_image_rect();
243 const Rect quad(image_rect.x_min() * dpi_scaling(), image_rect.x_max() * dpi_scaling(),
244 image_rect.y_min() * dpi_scaling(), image_rect.y_max() * dpi_scaling());
245
246 const int w = static_cast<int>(static_cast<float>(width()) * dpi_scaling());
247 const int h = static_cast<int>(static_cast<float>(height()) * dpi_scaling());
248 shape::draw_quad_filled(quad, texture_->id(), w, h, -0.9f);
249 shape::draw_quad_wire(quad, vec4(1.0f, 0.0f, 0.0f, 1.0f), w, h, -0.99f);
250
251
252
253 if (cross_drawable_ && cross_drawable_->is_visible()) {
254 ShaderProgram *program = ShaderManager::get_program("lines/lines_plain_color");
255 if (!program) {
256 std::vector<ShaderProgram::Attribute> attributes;
257 attributes.emplace_back(ShaderProgram::Attribute(ShaderProgram::POSITION, "vtx_position"));
258 attributes.emplace_back(ShaderProgram::Attribute(ShaderProgram::COLOR, "vtx_color"));
259 program = ShaderManager::create_program_from_files("lines/lines_plain_color", attributes);
260 }
261 if (!program)
262 return;
263
264 const mat4 &proj = transform::ortho(0.0f, static_cast<float>(width()), static_cast<float>(height()), 0.0f,
265 0.0f, -1.0f);
266 glDisable(GL_DEPTH_TEST); // always on top
267 program->bind();
268 program->set_uniform("MVP", proj);
269 program->set_uniform("per_vertex_color", false);
270 program->set_uniform("default_color", vec4(0.0f, 1.0f, 0.0f, 1.0f));
271 cross_drawable_->gl_draw();
272 program->release();
273 glEnable(GL_DEPTH_TEST); // restore
274 }
275}
276
277
278bool RealCamera::mouse_free_move_event(int x, int y, int dx, int dy, int modifiers) {
279 (void) dx;
280 (void) dy;
281 (void) modifiers;
282
283 if (current_view_ < 0 || current_view_ >= views_.size()) {
284 std::cerr << "Error: invalid view index (" << current_view_ << ")" << std::endl;
285 return false;
286 }
287
288 const CameraPara &cam = views_[current_view_];
289 const Rect image_rect = calculate_image_rect();
290 // cursor is inside the image rectangle
291 if (static_cast<float>(x) >= image_rect.x_min() && static_cast<float>(x) <= image_rect.x_max() && static_cast<float>(y) >= image_rect.y_min() && static_cast<float>(y) <= image_rect.y_max()) {
292 const float image_x = (static_cast<float>(x) - image_rect.x_min()) / image_rect.width() * static_cast<float>(cam.w);
293 const float image_y = (static_cast<float>(y) - image_rect.y_min()) / image_rect.height() * static_cast<float>(cam.h);
294 if (!ray_drawable_) {
295 ray_drawable_ = new LinesDrawable("ray");
296 add_drawable(ray_drawable_); // add the ray drawable to the viewer
297 ray_drawable_->set_uniform_coloring(vec4(0, 1, 0, 1.0f));
298 ray_drawable_->set_line_width(3.0f);
299 ray_drawable_->set_impostor_type(easy3d::LinesDrawable::CYLINDER);
300 }
301 const vec3 pos = camera_pos(cam.R, cam.t);
302 const vec3 dir = pixel_to_ray(static_cast<int>(image_x), static_cast<int>(image_y), cam.fx, cam.fy, 0, cam.cx, cam.cy, cam.R, cam.t, true);
303 const std::vector<vec3> points = {pos, pos + dir};
304 ray_drawable_->update_vertex_buffer(points);
305 ray_drawable_->set_visible(true);
306 update();
307 } else {
308 if (ray_drawable_)
309 ray_drawable_->set_visible(false);
310
311 bool found(false);
312 const vec3 p = point_under_pixel(x, y, found);
313 if (found) {
314 const vec2 q = point_to_pixel(p, cam.fx, cam.fy, 0, cam.cx, cam.cy, cam.R, cam.t);
315
316 // visualize the image point (that must be within the image)
317 if (q.x >= 0 && q.x <= static_cast<float>(cam.w) && q.y >= 0 && q.y <= static_cast<float>(cam.h)) {
318 const float screen_x = q.x / static_cast<float>(cam.w) * image_rect.width() + image_rect.x_min();
319 const float screen_y = q.y / static_cast<float>(cam.h) * image_rect.height() + image_rect.y_min();
320 if (!cross_drawable_) {
321 cross_drawable_ = new LinesDrawable("cross");
322 add_drawable(cross_drawable_); // add the cross drawable to the viewer
323 cross_drawable_->set_line_width(3.0f);
324 }
325
326#if defined(__APPLE__)
327 const float size = 10;
328#else
329 const float size = static_cast<float>(10 * dpi_scaling());
330#endif
331 const std::vector<vec3> points = {
332 vec3(screen_x - size, screen_y, 0.5f), vec3(screen_x + size, screen_y, 0.5f),
333 vec3(screen_x, screen_y - size, 0.5f), vec3(screen_x, screen_y + size, 0.5f)
334 };
335 cross_drawable_->update_vertex_buffer(points);
336 cross_drawable_->set_visible(true);
337 }
338 } else {
339 if (cross_drawable_)
340 cross_drawable_->set_visible(false);
341 }
342
343 update();
344 }
345
346 return false;
347}
348
349
350vec3 RealCamera::camera_pos(const mat3 &R, const vec3 &t) {
351 return -inverse(R) * t; // inverse(R) * (vec3(0, 0, 0) - cam.t);
352}
353
354
355vec3 RealCamera::pixel_to_ray(int image_x, int image_y, float fx, float fy, float skew, float cx, float cy,
356 const mat3& R, const vec3& t, bool convert) {
357 mat3 K(fx, skew, cx,
358 0, fy, cy,
359 0, 0, 1);
360
361 // image point in the camera coordinate system (because p_image = K * p_cam).
362 vec3 P = inverse(K) * vec3(static_cast<float>(image_x), static_cast<float>(image_y), 1);
363 if (convert) {
366 P.y *= -1;
367 P.z *= -1;
368 }
369
370 // in the world coordinate system (because p_cam = R * p_world + t)
371 P = transpose(R) * (P - t);
372
373 return P - camera_pos(R, t);
374}
375
376
377vec2 RealCamera::point_to_pixel(const easy3d::vec3 &p,
378 float fx, float fy, float skew, float cx, float cy,
379 const easy3d::mat3 &R, const easy3d::vec3 &t, bool convert) {
380 mat3 K(fx, skew, cx,
381 0, fy, cy,
382 0, 0, 1);
383 mat34 Rt;
384 Rt.set_col(0, R.col(0));
385 Rt.set_col(1, R.col(1));
386 Rt.set_col(2, R.col(2));
387 Rt.set_col(3, t);
388
389 if (convert) {
392 mat3 flip(1.0);
393 flip(1, 1) = -1; // invert the Y axis
394 flip(2, 2) = -1; // invert the Z axis
395 Rt = flip * Rt;
396 }
397
398 vec3 q = K * (Rt * vec4(p, 1.0));
399 q /= q.z;
400
401 return vec2(q.x, q.y);
402}
void setOrientation(const quat &q) const
Definition: camera.cpp:869
float sceneRadius() const
Returns the radius of the scene observed by the Camera.
Definition: camera.h:408
void setFieldOfView(float fov)
Definition: camera.cpp:257
void set_from_calibration(float fx, float fy, float skew, float cx, float cy, const mat3 &R, const vec3 &t, bool convert=true)
Defines the position(), orientation() and fieldOfView() of the camera from calibrated camera intrinsi...
Definition: camera.cpp:995
float fieldOfView() const
Returns the vertical field of view of the Camera (in radians).
Definition: camera.h:278
void setPosition(const vec3 &pos) const
Definition: camera.cpp:950
ManipulatedCameraFrame * frame() const
Returns the ManipulatedFrame attached to the Camera.
Definition: camera.h:438
mat4 worldMatrix() const
Definition: frame.cpp:201
FT height() const
Returns the height of the rectangle.
Definition: rect.h:80
FT width() const
Returns the width of the rectangle.
Definition: rect.h:78
Base class for matrix types.
Definition: mat.h:66
Vec< N, T > col(size_t c) const
Return col c as a vector.
Definition: mat.h:509
void set_col(size_t c, const Vec< vN, T > &v)
Set col c from vector v. This copies the first N components from v, so vN must be >= N....
Definition: mat.h:532
OpenGL Shader Compilation.
Definition: shader_program.h:78
void bind() const
Starts using the program.
Definition: shader_program.cpp:678
void release() const
Ends using the program.
Definition: shader_program.cpp:689
virtual Model * add_model(const std::string &file_name, bool create_default_drawables=true)
Add a model from a file to the viewer to be visualized. On success, the viewer will be in charge of t...
Definition: viewer.cpp:1204
Mat< M, N, T > transpose(const Mat< N, M, T > &m)
Transpose m.
Definition: mat.h:907
Mat< N, N, T > inverse(const Mat< N, N, T > &m)
Return the inverse of N x N (square) matrix m.
Definition: mat.h:977