1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of the copyright holders may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 // Authors:
41 //  * Ozan Tonkal, ozantonkal@gmail.com
42 //  * Anatoly Baksheev, Itseez Inc.  myname.mysurname <> mycompany.com
43 //
44 //M*/
45 
46 #include "precomp.hpp"
47 
makeTransformToGlobal(const Vec3d & axis_x,const Vec3d & axis_y,const Vec3d & axis_z,const Vec3d & origin)48 cv::Affine3d cv::viz::makeTransformToGlobal(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin)
49 {
50     Affine3d::Mat3 R(axis_x[0], axis_y[0], axis_z[0],
51                      axis_x[1], axis_y[1], axis_z[1],
52                      axis_x[2], axis_y[2], axis_z[2]);
53 
54     return Affine3d(R, origin);
55 }
56 
makeCameraPose(const Vec3d & position,const Vec3d & focal_point,const Vec3d & y_dir)57 cv::Affine3d cv::viz::makeCameraPose(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir)
58 {
59     // Compute the transformation matrix for drawing the camera frame in a scene
60     Vec3d n = normalize(focal_point - position);
61     Vec3d u = normalize(y_dir.cross(n));
62     Vec3d v = n.cross(u);
63 
64     return makeTransformToGlobal(u, v, n, position);
65 }
66 
67 ///////////////////////////////////////////////////////////////////////////////////////////////
68 /// VizStorage implementation
69 
70 #if defined(_WIN32) && !defined(__CYGWIN__)
71 
72     #include <windows.h>
73 
ConsoleHandlerRoutine(DWORD)74     static BOOL WINAPI ConsoleHandlerRoutine(DWORD /*dwCtrlType*/)
75     {
76         vtkObject::GlobalWarningDisplayOff();
77         return FALSE;
78     }
79 
register_console_handler()80     static void register_console_handler()
81     {
82         HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
83         CONSOLE_SCREEN_BUFFER_INFO hOutInfo;
84         if (GetConsoleScreenBufferInfo(hOut, &hOutInfo))
85             SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE);
86     }
87 
88 #else
89 
90     void register_console_handler();
register_console_handler()91     void register_console_handler() {}
92 
93 #endif
94 
95 
96 cv::viz::VizStorage cv::viz::VizStorage::init;
97 cv::viz::VizMap cv::viz::VizStorage::storage;
98 
replace_clear()99 void cv::viz::VizMap::replace_clear() { type().swap(m); }
~VizMap()100 cv::viz::VizMap::~VizMap() { replace_clear(); }
101 
VizStorage()102 cv::viz::VizStorage::VizStorage()
103 {
104     register_console_handler();
105 }
unregisterAll()106 void cv::viz::VizStorage::unregisterAll() { storage.replace_clear(); }
107 
get(const String & window_name)108 cv::viz::Viz3d& cv::viz::VizStorage::get(const String &window_name)
109 {
110     String name = generateWindowName(window_name);
111     VizMap::iterator vm_itr = storage.m.find(name);
112     CV_Assert(vm_itr != storage.m.end());
113     return vm_itr->second;
114 }
115 
add(const Viz3d & window)116 void cv::viz::VizStorage::add(const Viz3d& window)
117 {
118     String window_name = window.getWindowName();
119     VizMap::iterator vm_itr = storage.m.find(window_name);
120     CV_Assert(vm_itr == storage.m.end());
121     storage.m.insert(std::make_pair(window_name, window));
122 }
123 
windowExists(const String & window_name)124 bool cv::viz::VizStorage::windowExists(const String &window_name)
125 {
126     String name = generateWindowName(window_name);
127     return storage.m.find(name) != storage.m.end();
128 }
129 
removeUnreferenced()130 void cv::viz::VizStorage::removeUnreferenced()
131 {
132     for(VizMap::iterator pos = storage.m.begin(); pos != storage.m.end();)
133         if(pos->second.impl_->ref_counter == 1)
134             storage.m.erase(pos++);
135         else
136             ++pos;
137 }
138 
generateWindowName(const String & window_name)139 cv::String cv::viz::VizStorage::generateWindowName(const String &window_name)
140 {
141     String output = "Viz";
142     // Already is Viz
143     if (window_name == output)
144         return output;
145 
146     String prefixed = output + " - ";
147     if (window_name.substr(0, prefixed.length()) == prefixed)
148         output = window_name; // Already has "Viz - "
149     else if (window_name.substr(0, output.length()) == output)
150         output = prefixed + window_name; // Doesn't have prefix
151     else
152         output = (window_name == "" ? output : prefixed + window_name);
153 
154     return output;
155 }
156 
getWindowByName(const String & window_name)157 cv::viz::Viz3d cv::viz::getWindowByName(const String &window_name) { return Viz3d (window_name); }
unregisterAllWindows()158 void cv::viz::unregisterAllWindows() { VizStorage::unregisterAll(); }
159 
imshow(const String & window_name,InputArray image,const Size & window_size)160 cv::viz::Viz3d cv::viz::imshow(const String& window_name, InputArray image, const Size& window_size)
161 {
162     Viz3d viz = getWindowByName(window_name);
163     viz.showImage(image, window_size);
164     return viz;
165 }
166 
167 ///////////////////////////////////////////////////////////////////////////////////////////////
168 /// Read/write clouds. Supported formats: ply, stl, xyz, obj
169 
writeCloud(const String & file,InputArray cloud,InputArray colors,InputArray normals,bool binary)170 void cv::viz::writeCloud(const String& file, InputArray cloud, InputArray colors, InputArray normals, bool binary)
171 {
172     CV_Assert(file.size() > 4 && "Extention is required");
173     String extention = file.substr(file.size()-4);
174 
175     vtkSmartPointer<vtkCloudMatSource> source = vtkSmartPointer<vtkCloudMatSource>::New();
176     source->SetColorCloudNormals(cloud, colors, normals);
177 
178     vtkSmartPointer<vtkWriter> writer;
179     if (extention == ".xyz")
180     {
181         writer = vtkSmartPointer<vtkXYZWriter>::New();
182         vtkXYZWriter::SafeDownCast(writer)->SetFileName(file.c_str());
183     }
184     else if (extention == ".ply")
185     {
186         writer = vtkSmartPointer<vtkPLYWriter>::New();
187         vtkPLYWriter::SafeDownCast(writer)->SetFileName(file.c_str());
188         vtkPLYWriter::SafeDownCast(writer)->SetFileType(binary ? VTK_BINARY : VTK_ASCII);
189         vtkPLYWriter::SafeDownCast(writer)->SetArrayName("Colors");
190     }
191     else if (extention == ".obj")
192     {
193         writer = vtkSmartPointer<vtkOBJWriter>::New();
194         vtkOBJWriter::SafeDownCast(writer)->SetFileName(file.c_str());
195     }
196     else
197         CV_Assert(!"Unsupported format");
198 
199     writer->SetInputConnection(source->GetOutputPort());
200     writer->Write();
201 }
202 
readCloud(const String & file,OutputArray colors,OutputArray normals)203 cv::Mat cv::viz::readCloud(const String& file, OutputArray colors, OutputArray normals)
204 {
205     CV_Assert(file.size() > 4 && "Extention is required");
206     String extention = file.substr(file.size()-4);
207 
208     vtkSmartPointer<vtkPolyDataAlgorithm> reader;
209     if (extention == ".xyz")
210     {
211         reader = vtkSmartPointer<vtkXYZReader>::New();
212         vtkXYZReader::SafeDownCast(reader)->SetFileName(file.c_str());
213     }
214     else if (extention == ".ply")
215     {
216         reader = vtkSmartPointer<vtkPLYReader>::New();
217         CV_Assert(vtkPLYReader::CanReadFile(file.c_str()));
218         vtkPLYReader::SafeDownCast(reader)->SetFileName(file.c_str());
219     }
220     else if (extention == ".obj")
221     {
222         reader = vtkSmartPointer<vtkOBJReader>::New();
223         vtkOBJReader::SafeDownCast(reader)->SetFileName(file.c_str());
224     }
225     else if (extention == ".stl")
226     {
227         reader = vtkSmartPointer<vtkSTLReader>::New();
228         vtkSTLReader::SafeDownCast(reader)->SetFileName(file.c_str());
229     }
230     else
231         CV_Assert(!"Unsupported format");
232 
233     cv::Mat cloud;
234 
235     vtkSmartPointer<vtkCloudMatSink> sink = vtkSmartPointer<vtkCloudMatSink>::New();
236     sink->SetInputConnection(reader->GetOutputPort());
237     sink->SetOutput(cloud, colors, normals);
238     sink->Write();
239 
240     return cloud;
241 }
242 
readMesh(const String & file)243 cv::viz::Mesh cv::viz::readMesh(const String& file) { return Mesh::load(file); }
244 
245 ///////////////////////////////////////////////////////////////////////////////////////////////
246 /// Read/write poses and trajectories
247 
readPose(const String & file,Affine3d & pose,const String & tag)248 bool cv::viz::readPose(const String& file, Affine3d& pose, const String& tag)
249 {
250     FileStorage fs(file, FileStorage::READ);
251     if (!fs.isOpened())
252         return false;
253 
254     Mat hdr(pose.matrix, false);
255     fs[tag] >> hdr;
256     if (hdr.empty() || hdr.cols != pose.matrix.cols || hdr.rows != pose.matrix.rows)
257         return false;
258 
259     hdr.convertTo(pose.matrix, CV_64F);
260     return true;
261 }
262 
writePose(const String & file,const Affine3d & pose,const String & tag)263 void cv::viz::writePose(const String& file, const Affine3d& pose, const String& tag)
264 {
265     FileStorage fs(file, FileStorage::WRITE);
266     fs << tag << Mat(pose.matrix, false);
267 }
268 
readTrajectory(OutputArray _traj,const String & files_format,int start,int end,const String & tag)269 void cv::viz::readTrajectory(OutputArray _traj, const String& files_format, int start, int end, const String& tag)
270 {
271     CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT);
272 
273     start = max(0, std::min(start, end));
274     end = std::max(start, end);
275 
276     std::vector<Affine3d> traj;
277 
278     for(int i = start; i < end; ++i)
279     {
280         Affine3d affine;
281         bool ok = readPose(cv::format(files_format.c_str(), i), affine, tag);
282         if (!ok)
283             break;
284 
285         traj.push_back(affine);
286     }
287 
288     Mat(traj).convertTo(_traj, _traj.depth());
289 }
290 
writeTrajectory(InputArray _traj,const String & files_format,int start,const String & tag)291 void cv::viz::writeTrajectory(InputArray _traj, const String& files_format, int start, const String& tag)
292 {
293     if (_traj.kind() == _InputArray::STD_VECTOR_MAT)
294     {
295 #if CV_MAJOR_VERSION < 3
296         std::vector<Mat>& v = *(std::vector<Mat>*)_traj.obj;
297 #else
298         std::vector<Mat>& v = *(std::vector<Mat>*)_traj.getObj();
299 #endif
300 
301         for(size_t i = 0, index = max(0, start); i < v.size(); ++i, ++index)
302         {
303             Affine3d affine;
304             Mat pose = v[i];
305             CV_Assert(pose.type() == CV_32FC(16) || pose.type() == CV_64FC(16));
306             pose.copyTo(affine.matrix);
307             writePose(cv::format(files_format.c_str(), index), affine, tag);
308         }
309         return;
310     }
311 
312     if (_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT)
313     {
314         CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16));
315 
316         Mat traj = _traj.getMat();
317 
318         if (traj.depth() == CV_32F)
319             for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index)
320                 writePose(cv::format(files_format.c_str(), index), traj.at<Affine3f>((int)i), tag);
321 
322         if (traj.depth() == CV_64F)
323             for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index)
324                 writePose(cv::format(files_format.c_str(), index), traj.at<Affine3d>((int)i), tag);
325         return;
326     }
327 
328     CV_Assert(!"Unsupported array kind");
329 }
330 
331 ///////////////////////////////////////////////////////////////////////////////////////////////
332 /// Computing normals for mesh
333 
computeNormals(const Mesh & mesh,OutputArray _normals)334 void cv::viz::computeNormals(const Mesh& mesh, OutputArray _normals)
335 {
336     vtkSmartPointer<vtkPolyData> polydata = getPolyData(WMesh(mesh));
337     vtkSmartPointer<vtkPolyData> with_normals = VtkUtils::ComputeNormals(polydata);
338 
339     vtkSmartPointer<vtkDataArray> generic_normals = with_normals->GetPointData()->GetNormals();
340     if(generic_normals)
341     {
342         Mat normals(1, generic_normals->GetNumberOfTuples(), CV_64FC3);
343         Vec3d *optr = normals.ptr<Vec3d>();
344 
345         for(int i = 0; i < generic_normals->GetNumberOfTuples(); ++i, ++optr)
346             generic_normals->GetTuple(i, optr->val);
347 
348         normals.convertTo(_normals, mesh.cloud.type());
349     }
350     else
351         _normals.release();
352 }
353