GCC Code Coverage Report


Directory: ./
File: libs/io/vtk/vtk.cpp
Date: 2024-04-19 18:48:28
Exec Total Coverage
Lines: 112 113 99.1%
Branches: 79 210 37.6%

Line Branch Exec Source
1 /************************************************************************
2 *
3 * Copyright (C) 2009-2023 IRCAD France
4 * Copyright (C) 2012-2020 IHU Strasbourg
5 *
6 * This file is part of Sight.
7 *
8 * Sight is free software: you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Sight is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
20 *
21 ***********************************************************************/
22
23 // cspell:ignore NOLINTNEXTLINE
24
25 #include "io/vtk/vtk.hpp"
26
27 #include <core/runtime/profile.hpp>
28 #include <core/tools/os.hpp>
29
30 #include <data/helper/medical_image.hpp>
31 #include <data/image.hpp>
32
33 #include <geometry/data/mesh_functions.hpp>
34
35 #include <vtkCell.h>
36 #include <vtkCellType.h>
37 #include <vtkDataArray.h>
38 #include <vtkDataSetAttributes.h>
39 #include <vtkFileOutputWindow.h>
40 #include <vtkImageData.h>
41 #include <vtkImageExport.h>
42 #include <vtkImageImport.h>
43 #include <vtkLookupTable.h>
44 #include <vtkMatrix4x4.h>
45 #include <vtkPointData.h>
46 #include <vtkPoints.h>
47 #include <vtkPolyData.h>
48 #include <vtkPolyDataNormals.h>
49 #include <vtkPolyDataWriter.h>
50 #include <vtkSetGet.h>
51 #include <vtkSmartPointer.h>
52 #include <vtkType.h>
53 #include <vtkUnstructuredGrid.h>
54
55 #include <chrono>
56 #include <cstring>
57 #include <ctime>
58 #include <numeric>
59 #include <stdexcept>
60
61 namespace sight::io::vtk
62 {
63
64 //------------------------------------------------------------------------------
65
66 // Function to initialize VTK Log file, where VTK message will be printed in.
67 20 static bool init_vtk_log_file()
68 {
69
4/8
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 20 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 20 times.
✗ Branch 12 not taken.
40 SIGHT_INFO("Creating VTK Log file");
70
71 // Get current date.
72 20 const auto timenow =
73 20 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
74
75 // Create VTK.log in user cache folder.
76 // Find log dir
77 20 const auto& current_profile = core::runtime::get_current_profile();
78
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
20 const auto& profile_name = current_profile ? current_profile->name() : std::string();
79
3/6
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
60 const auto& vtk_log = core::tools::os::get_user_cache_dir(profile_name) / "VTK.log";
80
81 // TODO: Gather all .log file together in Session.
82
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 vtkSmartPointer<vtkFileOutputWindow> outwin = vtkFileOutputWindow::New();
83
84 // MSVC doesn't have std::filesystem::path::c_str()
85
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 const auto& vtk_log_string = vtk_log.string();
86
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 outwin->SetFileName(vtk_log_string.c_str());
87
88 // Print header.
89
2/4
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
20 std::string header = "# VTK LOG File " + std::string(ctime(&timenow));
90
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 outwin->DisplayText(header.c_str());
91
92
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 vtkFileOutputWindow::SetInstance(outwin);
93
94 20 return true;
95
2/2
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 5 times.
55 }
96
97 // static variable to call initVTKLogFile();
98 static bool s_enable_log = init_vtk_log_file();
99
100 // ------------------------------------------------------------------------------
101
102 816 type_translator::to_vtk_map_t::mapped_type type_translator::translate(
103 const type_translator::to_vtk_map_t::key_type& _key
104 )
105 {
106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 816 times.
816 auto it = TO_VTK.find(_key);
107
1/34
✗ Branch 0 not taken.
✓ Branch 1 taken 816 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
816 SIGHT_THROW_IF("Unknown Type: " << _key, it == TO_VTK.end());
108 816 return it->second;
109 }
110
111 // ------------------------------------------------------------------------------
112
113 822 type_translator::from_vtk_map_t::mapped_type type_translator::translate(
114 const type_translator::from_vtk_map_t::key_type& _key
115 )
116 {
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 822 times.
822 auto it = FROM_VTK.find(_key);
118
1/34
✗ Branch 0 not taken.
✓ Branch 1 taken 822 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
822 SIGHT_THROW_IF("Unknown Type: " << _key, it == FROM_VTK.end());
119 822 return it->second;
120 }
121
122 const type_translator::to_vtk_map_t type_translator::TO_VTK = {
123 // char and signed char are treated as the same type.
124 // and plain char is used when writing an int8 image
125 {core::type::INT8, VTK_CHAR},
126 {core::type::UINT8, VTK_UNSIGNED_CHAR},
127 {core::type::INT16, VTK_SHORT},
128 {core::type::UINT16, VTK_UNSIGNED_SHORT},
129 {core::type::INT32, VTK_INT},
130 {core::type::UINT32, VTK_UNSIGNED_INT},
131 {core::type::INT64, VTK_LONG_LONG},
132 {core::type::UINT64, VTK_UNSIGNED_LONG_LONG},
133 {core::type::FLOAT, VTK_FLOAT},
134 {core::type::DOUBLE, VTK_DOUBLE}
135 };
136
137 const type_translator::from_vtk_map_t type_translator::FROM_VTK = {
138 // char and signed char are treated as the same type.
139 // and plain char is used when writing an int8 image
140 {VTK_SIGNED_CHAR, core::type::INT8},
141 {VTK_CHAR, core::type::INT8},
142 {VTK_UNSIGNED_CHAR, core::type::UINT8},
143 {VTK_SHORT, core::type::INT16},
144 {VTK_UNSIGNED_SHORT, core::type::UINT16},
145 {VTK_INT, core::type::INT32},
146 {VTK_UNSIGNED_INT, core::type::UINT32},
147 {VTK_FLOAT, core::type::FLOAT},
148 {VTK_DOUBLE, core::type::DOUBLE},
149 {VTK_LONG_LONG, core::type::INT64},
150 {VTK_UNSIGNED_LONG_LONG, core::type::UINT64},
151
152 #if (INT_MAX < LONG_MAX)
153 {
154 VTK_LONG, core::type::INT64
155 },
156 {VTK_UNSIGNED_LONG, core::type::UINT64}
157 #else
158 {
159 VTK_LONG, core::type::INT32
160 },
161 {VTK_UNSIGNED_LONG, core::type::UINT32}
162 #endif
163 };
164
165 // -----------------------------------------------------------------------------
166
167 815 void to_vtk_image(data::image::csptr _data, vtkImageData* _dst)
168 {
169 815 vtkSmartPointer<vtkImageImport> importer = vtkSmartPointer<vtkImageImport>::New();
170
171
2/4
✓ Branch 0 taken 815 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 815 times.
✗ Branch 4 not taken.
1630 configure_vtk_image_import(importer, _data);
172
173
1/2
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
815 importer->Update();
174
175
2/4
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815 times.
✗ Branch 5 not taken.
815 _dst->ShallowCopy(importer->GetOutput());
176 815 }
177
178 // -----------------------------------------------------------------------------
179
180 template<typename IMAGETYPE>
181 void* new_buffer(std::size_t _size)
182 {
183 IMAGETYPE* dest_buffer = nullptr;
184 try
185 {
186 dest_buffer = new IMAGETYPE[_size];
187 }
188 catch(std::exception& e)
189 {
190 SIGHT_ERROR(
191 "No enough memory to allocate an image of type "
192 << core::type::get<IMAGETYPE>().name()
193 << " and of size " << _size << "." << std::endl
194 << e.what()
195 );
196 throw;
197 }
198 return dest_buffer;
199 }
200
201 // -----------------------------------------------------------------------------
202
203 template<typename IMAGETYPE>
204 void from_rgb_buffer(void* _input, std::size_t _size, void*& _dest_buffer)
205 {
206 if(_dest_buffer == nullptr)
207 {
208 _dest_buffer = newBuffer<IMAGETYPE>(_size);
209 }
210
211 auto* dest_buffer_typed = static_cast<IMAGETYPE*>(_dest_buffer);
212 auto* input_typed = static_cast<IMAGETYPE*>(_input);
213 IMAGETYPE* final_ptr = static_cast<IMAGETYPE*>(_dest_buffer) + _size;
214 IMAGETYPE val_r;
215 IMAGETYPE val_g;
216 IMAGETYPE val_b;
217
218 while(dest_buffer_typed < final_ptr)
219 {
220 val_r = static_cast<IMAGETYPE>(float((*(input_typed++)) * 0.30));
221 val_g = static_cast<IMAGETYPE>(float((*(input_typed++)) * 0.59));
222 val_b = static_cast<IMAGETYPE>(float((*(input_typed++)) * 0.11));
223 (*dest_buffer_typed++) = val_r + val_g + val_b;
224 }
225 }
226
227 // -----------------------------------------------------------------------------
228
229 template<typename IMAGETYPE>
230 void from_rgb_buffer_color(void* _input, std::size_t _size, void*& _dest_buffer)
231 {
232 if(_dest_buffer == nullptr)
233 {
234 _dest_buffer = newBuffer<IMAGETYPE>(_size);
235 }
236
237 auto* dest_buffer_typed = static_cast<IMAGETYPE*>(_dest_buffer);
238 auto* input_typed = static_cast<IMAGETYPE*>(_input);
239 IMAGETYPE* final_ptr = static_cast<IMAGETYPE*>(_dest_buffer) + _size;
240
241 while(dest_buffer_typed < final_ptr)
242 {
243 (*dest_buffer_typed++) = (*(input_typed++));
244 }
245 }
246
247 // -----------------------------------------------------------------------------
248
249 822 void from_vtk_image(vtkImageData* _source, data::image::sptr _destination)
250 {
251 822 SIGHT_ASSERT("vtkImageData source and/or data::image destination are not correct", _destination && _source);
252
253 // ensure image size correct
254 // source->UpdateInformation();
255 // source->PropagateUpdateExtent();
256
257 822 int dim = _source->GetDataDimension();
258 822 data::image::size_t image_size;
259
260
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 803 times.
822 if(dim == 2)
261 {
262 19 image_size = {static_cast<std::size_t>(_source->GetDimensions()[0]),
263 19 static_cast<std::size_t>(_source->GetDimensions()[1]), 0
264 };
265
266 19 const data::image::spacing_t spacing = {_source->GetSpacing()[0], _source->GetSpacing()[1], 0.
267 19 };
268 19 _destination->set_spacing(spacing);
269
270 19 const data::image::origin_t origin = {_source->GetOrigin()[0], _source->GetOrigin()[1], 0.
271 19 };
272 19 _destination->set_origin(origin);
273 }
274 else
275 {
276 803 image_size = {static_cast<std::size_t>(_source->GetDimensions()[0]),
277 803 static_cast<std::size_t>(_source->GetDimensions()[1]),
278 803 static_cast<std::size_t>(_source->GetDimensions()[2])
279 };
280
281 803 const data::image::spacing_t spacing =
282 803 {_source->GetSpacing()[0], _source->GetSpacing()[1], _source->GetSpacing()[2]
283 803 };
284 803 _destination->set_spacing(spacing);
285
286 803 const data::image::origin_t origin = {_source->GetOrigin()[0], _source->GetOrigin()[1], _source->GetOrigin()[2]
287 803 };
288 803 _destination->set_origin(origin);
289 }
290
291 822 const int nb_components = _source->GetNumberOfScalarComponents();
292 822 const std::size_t size = static_cast<std::size_t>(
293
2/2
✓ Branch 0 taken 2447 times.
✓ Branch 1 taken 822 times.
3269 std::accumulate(
294 822 _source->GetDimensions(),
295 822 _source->GetDimensions() + static_cast<std::size_t>(dim),
296
2/2
✓ Branch 0 taken 185 times.
✓ Branch 1 taken 637 times.
1007 std::max(static_cast<std::size_t>(3), static_cast<std::size_t>(nb_components)),
297 std::multiplies<>()
298 )
299 );
300 822 const void* input = _source->GetScalarPointer();
301
302
1/2
✓ Branch 0 taken 822 times.
✗ Branch 1 not taken.
822 if(size != 0)
303 {
304 822 void* dest_buffer = nullptr;
305
306 822 enum sight::data::image::pixel_format format = data::image::pixel_format::gray_scale;
307
1/2
✓ Branch 0 taken 822 times.
✗ Branch 1 not taken.
822 if(nb_components == 1)
308 {
309 format = data::image::pixel_format::gray_scale;
310 }
311 else if(nb_components == 2)
312 {
313 format = data::image::pixel_format::rg;
314 }
315 else if(nb_components == 3)
316 {
317 format = data::image::pixel_format::rgb;
318 }
319 else if(nb_components == 4)
320 {
321 format = data::image::pixel_format::rgba;
322 }
323 else
324 {
325 SIGHT_FATAL("Unhandled pixel format");
326 }
327
328 822 _destination->resize(image_size, type_translator::translate(_source->GetScalarType()), format);
329
330 822 const auto dump_lock = _destination->dump_lock();
331
332
1/2
✓ Branch 1 taken 822 times.
✗ Branch 2 not taken.
822 dest_buffer = _destination->buffer();
333
1/2
✓ Branch 1 taken 822 times.
✗ Branch 2 not taken.
822 const std::size_t size_in_bytes = _destination->size_in_bytes();
334
1/2
✓ Branch 0 taken 822 times.
✗ Branch 1 not taken.
822 std::memcpy(dest_buffer, input, size_in_bytes);
335
336
2/4
✓ Branch 0 taken 822 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 822 times.
✗ Branch 4 not taken.
1644 sight::data::helper::medical_image::check_image_slice_index(_destination);
337 822 }
338 822 }
339
340 // ------------------------------------------------------------------------------
341
342 815 void configure_vtk_image_import(vtkImageImport* _p_image_import, data::image::csptr _p_data_image)
343 {
344 815 const auto dump_lock = _p_data_image->dump_lock();
345
346
3/4
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 797 times.
815 if(_p_data_image->num_dimensions() == 2)
347 {
348
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_image_import->SetDataSpacing(
349
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_data_image->spacing()[0],
350
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_data_image->spacing()[1],
351 0
352 );
353
354
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_image_import->SetDataOrigin(
355
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_data_image->origin()[0],
356
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_data_image->origin()[1],
357 0
358 );
359
360
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 _p_image_import->SetWholeExtent(
361 0,
362
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 static_cast<int>(_p_data_image->size()[0]) - 1,
363 0,
364
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 static_cast<int>(_p_data_image->size()[1]) - 1,
365 0,
366 0
367 );
368 }
369 else
370 {
371
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_image_import->SetDataSpacing(
372 797 _p_data_image->spacing()[0],
373
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_data_image->spacing()[1],
374
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_data_image->spacing()[2]
375 );
376
377
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_image_import->SetDataOrigin(
378 797 _p_data_image->origin()[0],
379
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_data_image->origin()[1],
380
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_data_image->origin()[2]
381 );
382
383
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 _p_image_import->SetWholeExtent(
384 0,
385 797 static_cast<int>(_p_data_image->size()[0]) - 1,
386 0,
387
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 static_cast<int>(_p_data_image->size()[1]) - 1,
388 0,
389
1/2
✓ Branch 1 taken 797 times.
✗ Branch 2 not taken.
797 static_cast<int>(_p_data_image->size()[2]) - 1
390 );
391 }
392
393
1/2
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
815 _p_image_import->SetNumberOfScalarComponents(static_cast<int>(_p_data_image->num_components()));
394
395 // copy WholeExtent to DataExtent
396
1/2
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
815 _p_image_import->SetDataExtentToWholeExtent();
397
398 // no copy, no buffer destruction/management
399 // Remove const of the pointer.
400 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
401
2/4
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815 times.
✗ Branch 5 not taken.
815 _p_image_import->SetImportVoidPointer(const_cast<void*>(_p_data_image->buffer()));
402
403 // used to set correct pixeltype to VtkImage
404
3/6
✓ Branch 1 taken 815 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 815 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 815 times.
✗ Branch 8 not taken.
815 _p_image_import->SetDataScalarType(type_translator::translate(_p_data_image->type()));
405 815 }
406
407 // -----------------------------------------------------------------------------
408
409 1 vtkSmartPointer<vtkMatrix4x4> to_vtk_matrix(data::matrix4::csptr _transfo_matrix)
410 {
411 1 auto matrix = vtkSmartPointer<vtkMatrix4x4>::New();
412
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
5 for(std::uint8_t l = 0 ; l < 4 ; l++)
413 {
414
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 for(std::uint8_t c = 0 ; c < 4 ; c++)
415 {
416
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 matrix->SetElement(l, c, (*_transfo_matrix)(l, c));
417 }
418 }
419
420 1 return matrix;
421 }
422
423 // -----------------------------------------------------------------------------
424
425 1 bool from_vtk_matrix(vtkMatrix4x4* _matrix, data::matrix4::sptr _transfo_matrix)
426 {
427 1 bool res = true;
428
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for(std::uint8_t l = 0 ; l < 4 ; l++)
429 {
430
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 for(std::uint8_t c = 0 ; c < 4 ; c++)
431 {
432 16 (*_transfo_matrix)(l, c) = _matrix->GetElement(l, c);
433 }
434 }
435
436 1 return res;
437 }
438
439 // -----------------------------------------------------------------------------
440
441 } // namespace sight::io::vtk
442