Commit 07f7b52a73260f1bb6a8247eec4644c3d768f2f0
1 parent
61106a5f
Exists in
master
iSort and Black to improve source code format in bitmap reader and imagedata_utils
Showing
2 changed files
with
239 additions
and
196 deletions
Show diff stats
invesalius/data/imagedata_utils.py
1 | -#-------------------------------------------------------------------------- | |
1 | +# -------------------------------------------------------------------------- | |
2 | 2 | # Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas |
3 | 3 | # Copyright: (C) 2001 Centro de Pesquisas Renato Archer |
4 | 4 | # Homepage: http://www.softwarepublico.gov.br |
5 | 5 | # Contact: invesalius@cti.gov.br |
6 | 6 | # License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) |
7 | -#-------------------------------------------------------------------------- | |
7 | +# -------------------------------------------------------------------------- | |
8 | 8 | # Este programa e software livre; voce pode redistribui-lo e/ou |
9 | 9 | # modifica-lo sob os termos da Licenca Publica Geral GNU, conforme |
10 | 10 | # publicada pela Free Software Foundation; de acordo com a versao 2 |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | # COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM |
16 | 16 | # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais |
17 | 17 | # detalhes. |
18 | -#-------------------------------------------------------------------------- | |
18 | +# -------------------------------------------------------------------------- | |
19 | 19 | |
20 | 20 | import math |
21 | 21 | import os |
... | ... | @@ -28,21 +28,19 @@ import numpy |
28 | 28 | import numpy as np |
29 | 29 | import vtk |
30 | 30 | from pubsub import pub as Publisher |
31 | - | |
32 | 31 | from scipy.ndimage import shift, zoom |
33 | 32 | from vtk.util import numpy_support |
34 | 33 | |
35 | 34 | import invesalius.constants as const |
36 | -from invesalius.data import vtk_utils as vtk_utils | |
37 | -import invesalius.reader.bitmap_reader as bitmap_reader | |
38 | -import invesalius.utils as utils | |
39 | 35 | import invesalius.data.converters as converters |
40 | 36 | import invesalius.data.slice_ as sl |
41 | 37 | import invesalius.data.transformations as tr |
42 | - | |
38 | +import invesalius.reader.bitmap_reader as bitmap_reader | |
39 | +import invesalius.utils as utils | |
43 | 40 | from invesalius import inv_paths |
41 | +from invesalius.data import vtk_utils as vtk_utils | |
44 | 42 | |
45 | -if sys.platform == 'win32': | |
43 | +if sys.platform == "win32": | |
46 | 44 | try: |
47 | 45 | import win32api |
48 | 46 | _has_win32api = True |
... | ... | @@ -54,6 +52,7 @@ else: |
54 | 52 | # TODO: Test cases which are originally in sagittal/coronal orientation |
55 | 53 | # and have gantry |
56 | 54 | |
55 | + | |
57 | 56 | def ResampleImage3D(imagedata, value): |
58 | 57 | """ |
59 | 58 | Resample vtkImageData matrix. |
... | ... | @@ -63,9 +62,9 @@ def ResampleImage3D(imagedata, value): |
63 | 62 | size = imagedata.GetDimensions() |
64 | 63 | |
65 | 64 | width = float(size[0]) |
66 | - height = float(size[1]/value) | |
65 | + height = float(size[1] / value) | |
67 | 66 | |
68 | - resolution = (height/(extent[1]-extent[0])+1)*spacing[1] | |
67 | + resolution = (height / (extent[1] - extent[0]) + 1) * spacing[1] | |
69 | 68 | |
70 | 69 | resample = vtk.vtkImageResample() |
71 | 70 | resample.SetInput(imagedata) |
... | ... | @@ -74,8 +73,10 @@ def ResampleImage3D(imagedata, value): |
74 | 73 | |
75 | 74 | return resample.GetOutput() |
76 | 75 | |
77 | -def ResampleImage2D(imagedata, px=None, py=None, resolution_percentage = None, | |
78 | - update_progress = None): | |
76 | + | |
77 | +def ResampleImage2D( | |
78 | + imagedata, px=None, py=None, resolution_percentage=None, update_progress=None | |
79 | +): | |
79 | 80 | """ |
80 | 81 | Resample vtkImageData matrix. |
81 | 82 | """ |
... | ... | @@ -88,30 +89,30 @@ def ResampleImage2D(imagedata, px=None, py=None, resolution_percentage = None, |
88 | 89 | factor_x = resolution_percentage |
89 | 90 | factor_y = resolution_percentage |
90 | 91 | else: |
91 | - if abs(extent[1]-extent[3]) < abs(extent[3]-extent[5]): | |
92 | + if abs(extent[1] - extent[3]) < abs(extent[3] - extent[5]): | |
92 | 93 | f = extent[1] |
93 | - elif abs(extent[1]-extent[5]) < abs(extent[1] - extent[3]): | |
94 | + elif abs(extent[1] - extent[5]) < abs(extent[1] - extent[3]): | |
94 | 95 | f = extent[1] |
95 | - elif abs(extent[3]-extent[5]) < abs(extent[1] - extent[3]): | |
96 | + elif abs(extent[3] - extent[5]) < abs(extent[1] - extent[3]): | |
96 | 97 | f = extent[3] |
97 | 98 | else: |
98 | 99 | f = extent[1] |
99 | 100 | |
100 | - factor_x = px/float(f+1) | |
101 | - factor_y = py/float(f+1) | |
101 | + factor_x = px / float(f + 1) | |
102 | + factor_y = py / float(f + 1) | |
102 | 103 | |
103 | 104 | resample = vtk.vtkImageResample() |
104 | 105 | resample.SetInputData(imagedata) |
105 | 106 | resample.SetAxisMagnificationFactor(0, factor_x) |
106 | 107 | resample.SetAxisMagnificationFactor(1, factor_y) |
107 | 108 | # resample.SetOutputSpacing(spacing[0] * factor_x, spacing[1] * factor_y, spacing[2]) |
108 | - if (update_progress): | |
109 | + if update_progress: | |
109 | 110 | message = _("Generating multiplanar visualization...") |
110 | - resample.AddObserver("ProgressEvent", lambda obj, | |
111 | - evt:update_progress(resample,message)) | |
111 | + resample.AddObserver( | |
112 | + "ProgressEvent", lambda obj, evt: update_progress(resample, message) | |
113 | + ) | |
112 | 114 | resample.Update() |
113 | 115 | |
114 | - | |
115 | 116 | return resample.GetOutput() |
116 | 117 | |
117 | 118 | |
... | ... | @@ -131,7 +132,7 @@ def resize_image_array(image, resolution_percentage, as_mmap=False): |
131 | 132 | out = zoom(image, resolution_percentage, image.dtype, order=2) |
132 | 133 | if as_mmap: |
133 | 134 | fname = tempfile.mktemp(suffix="_resized") |
134 | - out_mmap = np.memmap(fname, shape=out.shape, dtype=out.dtype, mode='w+') | |
135 | + out_mmap = np.memmap(fname, shape=out.shape, dtype=out.dtype, mode="w+") | |
135 | 136 | out_mmap[:] = out |
136 | 137 | return out_mmap |
137 | 138 | return out |
... | ... | @@ -159,7 +160,7 @@ def FixGantryTilt(matrix, spacing, tilt): |
159 | 160 | |
160 | 161 | for n, slice_ in enumerate(matrix): |
161 | 162 | offset = gntan * n * spacing[2] |
162 | - matrix[n] = shift(slice_, (-offset/spacing[1], 0), cval=matrix.min()) | |
163 | + matrix[n] = shift(slice_, (-offset / spacing[1], 0), cval=matrix.min()) | |
163 | 164 | |
164 | 165 | |
165 | 166 | def BuildEditedImage(imagedata, points): |
... | ... | @@ -175,28 +176,28 @@ def BuildEditedImage(imagedata, points): |
175 | 176 | imagedata.SetScalarComponentFromDouble(x, y, z, 0, colour) |
176 | 177 | imagedata.Update() |
177 | 178 | |
178 | - if not(init_values): | |
179 | - xi = x | |
180 | - xf = x | |
181 | - yi = y | |
182 | - yf = y | |
183 | - zi = z | |
184 | - zf = z | |
185 | - init_values = 1 | |
179 | + if not (init_values): | |
180 | + xi = x | |
181 | + xf = x | |
182 | + yi = y | |
183 | + yf = y | |
184 | + zi = z | |
185 | + zf = z | |
186 | + init_values = 1 | |
186 | 187 | |
187 | - if (xi > x): | |
188 | + if xi > x: | |
188 | 189 | xi = x |
189 | - elif(xf < x): | |
190 | + elif xf < x: | |
190 | 191 | xf = x |
191 | 192 | |
192 | - if (yi > y): | |
193 | + if yi > y: | |
193 | 194 | yi = y |
194 | - elif(yf < y): | |
195 | + elif yf < y: | |
195 | 196 | yf = y |
196 | 197 | |
197 | - if (zi > z): | |
198 | + if zi > z: | |
198 | 199 | zi = z |
199 | - elif(zf < z): | |
200 | + elif zf < z: | |
200 | 201 | zf = z |
201 | 202 | |
202 | 203 | clip = vtk.vtkImageClip() |
... | ... | @@ -226,8 +227,9 @@ def Export(imagedata, filename, bin=False): |
226 | 227 | writer.SetDataModeToBinary() |
227 | 228 | else: |
228 | 229 | writer.SetDataModeToAscii() |
229 | - #writer.SetInput(imagedata) | |
230 | - #writer.Write() | |
230 | + # writer.SetInput(imagedata) | |
231 | + # writer.Write() | |
232 | + | |
231 | 233 | |
232 | 234 | def Import(filename): |
233 | 235 | reader = vtk.vtkXMLImageDataReader() |
... | ... | @@ -238,6 +240,7 @@ def Import(filename): |
238 | 240 | |
239 | 241 | return reader.GetOutput() |
240 | 242 | |
243 | + | |
241 | 244 | def View(imagedata): |
242 | 245 | viewer = vtk.vtkImageViewer() |
243 | 246 | viewer.SetInput(imagedata) |
... | ... | @@ -246,16 +249,17 @@ def View(imagedata): |
246 | 249 | viewer.Render() |
247 | 250 | |
248 | 251 | import time |
252 | + | |
249 | 253 | time.sleep(10) |
250 | 254 | |
251 | 255 | |
252 | -def ExtractVOI(imagedata,xi,xf,yi,yf,zi,zf): | |
256 | +def ExtractVOI(imagedata, xi, xf, yi, yf, zi, zf): | |
253 | 257 | """ |
254 | 258 | Cropping the vtkImagedata according |
255 | 259 | with values. |
256 | 260 | """ |
257 | 261 | voi = vtk.vtkExtractVOI() |
258 | - voi.SetVOI(xi,xf,yi,yf,zi,zf) | |
262 | + voi.SetVOI(xi, xf, yi, yf, zi, zf) | |
259 | 263 | voi.SetInputData(imagedata) |
260 | 264 | voi.SetSampleRate(1, 1, 1) |
261 | 265 | voi.Update() |
... | ... | @@ -274,27 +278,30 @@ def create_dicom_thumbnails(image, window=None, level=None): |
274 | 278 | thumbnail_paths = [] |
275 | 279 | for i in range(np_image.shape[0]): |
276 | 280 | thumb_image = zoom(np_image[i], 0.25) |
277 | - thumb_image = np.array(get_LUT_value_255(thumb_image, window, level), dtype=np.uint8) | |
278 | - thumbnail_path = tempfile.mktemp(prefix='thumb_', suffix='.png') | |
281 | + thumb_image = np.array( | |
282 | + get_LUT_value_255(thumb_image, window, level), dtype=np.uint8 | |
283 | + ) | |
284 | + thumbnail_path = tempfile.mktemp(prefix="thumb_", suffix=".png") | |
279 | 285 | imageio.imsave(thumbnail_path, thumb_image) |
280 | 286 | thumbnail_paths.append(thumbnail_path) |
281 | 287 | return thumbnail_paths |
282 | 288 | else: |
283 | - thumbnail_path = tempfile.mktemp(prefix='thumb_', suffix='.png') | |
289 | + thumbnail_path = tempfile.mktemp(prefix="thumb_", suffix=".png") | |
284 | 290 | if pf.GetSamplesPerPixel() == 1: |
285 | 291 | thumb_image = zoom(np_image, 0.25) |
286 | - thumb_image = np.array(get_LUT_value_255(thumb_image, window, level), dtype=np.uint8) | |
292 | + thumb_image = np.array( | |
293 | + get_LUT_value_255(thumb_image, window, level), dtype=np.uint8 | |
294 | + ) | |
287 | 295 | else: |
288 | 296 | thumb_image = zoom(np_image, (0.25, 0.25, 1)) |
289 | 297 | imageio.imsave(thumbnail_path, thumb_image) |
290 | 298 | return thumbnail_path |
291 | 299 | |
292 | 300 | |
293 | - | |
294 | 301 | def array2memmap(arr, filename=None): |
295 | 302 | if filename is None: |
296 | - filename = tempfile.mktemp(prefix='inv3_', suffix='.dat') | |
297 | - matrix = numpy.memmap(filename, mode='w+', dtype=arr.dtype, shape=arr.shape) | |
303 | + filename = tempfile.mktemp(prefix="inv3_", suffix=".dat") | |
304 | + matrix = numpy.memmap(filename, mode="w+", dtype=arr.dtype, shape=arr.shape) | |
298 | 305 | matrix[:] = arr[:] |
299 | 306 | matrix.flush() |
300 | 307 | return matrix |
... | ... | @@ -307,34 +314,44 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage |
307 | 314 | """ |
308 | 315 | message = _("Generating multiplanar visualization...") |
309 | 316 | if len(files) > 1: |
310 | - update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") | |
317 | + update_progress = vtk_utils.ShowProgress( | |
318 | + len(files) - 1, dialog_type="ProgressDialog" | |
319 | + ) | |
311 | 320 | |
312 | 321 | temp_file = tempfile.mktemp() |
313 | 322 | |
314 | - if orientation == 'SAGITTAL': | |
323 | + if orientation == "SAGITTAL": | |
315 | 324 | if resolution_percentage == 1.0: |
316 | 325 | shape = slice_size[1], slice_size[0], len(files) |
317 | 326 | else: |
318 | - shape = math.ceil(slice_size[1]*resolution_percentage),\ | |
319 | - math.ceil(slice_size[0]*resolution_percentage), len(files) | |
327 | + shape = ( | |
328 | + math.ceil(slice_size[1] * resolution_percentage), | |
329 | + math.ceil(slice_size[0] * resolution_percentage), | |
330 | + len(files), | |
331 | + ) | |
320 | 332 | |
321 | - elif orientation == 'CORONAL': | |
333 | + elif orientation == "CORONAL": | |
322 | 334 | if resolution_percentage == 1.0: |
323 | 335 | shape = slice_size[1], len(files), slice_size[0] |
324 | 336 | else: |
325 | - shape = math.ceil(slice_size[1]*resolution_percentage), len(files),\ | |
326 | - math.ceil(slice_size[0]*resolution_percentage) | |
337 | + shape = ( | |
338 | + math.ceil(slice_size[1] * resolution_percentage), | |
339 | + len(files), | |
340 | + math.ceil(slice_size[0] * resolution_percentage), | |
341 | + ) | |
327 | 342 | else: |
328 | 343 | if resolution_percentage == 1.0: |
329 | 344 | shape = len(files), slice_size[1], slice_size[0] |
330 | 345 | else: |
331 | - shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\ | |
332 | - math.ceil(slice_size[0]*resolution_percentage) | |
333 | - | |
346 | + shape = ( | |
347 | + len(files), | |
348 | + math.ceil(slice_size[1] * resolution_percentage), | |
349 | + math.ceil(slice_size[0] * resolution_percentage), | |
350 | + ) | |
334 | 351 | |
335 | 352 | if resolution_percentage == 1.0: |
336 | - matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) | |
337 | - | |
353 | + matrix = numpy.memmap(temp_file, mode="w+", dtype="int16", shape=shape) | |
354 | + | |
338 | 355 | cont = 0 |
339 | 356 | max_scalar = None |
340 | 357 | min_scalar = None |
... | ... | @@ -347,21 +364,31 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage |
347 | 364 | |
348 | 365 | print(image_as_array.dtype) |
349 | 366 | |
350 | - image = converters.to_vtk(image_as_array, spacing=spacing,\ | |
351 | - slice_number=1, orientation=orientation.upper()) | |
367 | + image = converters.to_vtk( | |
368 | + image_as_array, | |
369 | + spacing=spacing, | |
370 | + slice_number=1, | |
371 | + orientation=orientation.upper(), | |
372 | + ) | |
352 | 373 | |
353 | 374 | if resolution_percentage != 1.0: |
354 | - | |
355 | - | |
356 | - image_resized = ResampleImage2D(image, px=None, py=None,\ | |
357 | - resolution_percentage = resolution_percentage, update_progress = None) | |
358 | 375 | |
359 | - yx_shape = image_resized.GetDimensions()[1], image_resized.GetDimensions()[0] | |
360 | - | |
361 | - | |
362 | - if not(first_resample_entry): | |
363 | - shape = shape[0], yx_shape[0], yx_shape[1] | |
364 | - matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) | |
376 | + image_resized = ResampleImage2D( | |
377 | + image, | |
378 | + px=None, | |
379 | + py=None, | |
380 | + resolution_percentage=resolution_percentage, | |
381 | + update_progress=None, | |
382 | + ) | |
383 | + | |
384 | + yx_shape = ( | |
385 | + image_resized.GetDimensions()[1], | |
386 | + image_resized.GetDimensions()[0], | |
387 | + ) | |
388 | + | |
389 | + if not (first_resample_entry): | |
390 | + shape = shape[0], yx_shape[0], yx_shape[1] | |
391 | + matrix = numpy.memmap(temp_file, mode="w+", dtype="int16", shape=shape) | |
365 | 392 | first_resample_entry = True |
366 | 393 | |
367 | 394 | image = image_resized |
... | ... | @@ -375,26 +402,26 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage |
375 | 402 | |
376 | 403 | array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) |
377 | 404 | |
378 | - if array.dtype == 'uint16': | |
405 | + if array.dtype == "uint16": | |
379 | 406 | new_array = np.empty_like(array, dtype=np.int16) |
380 | 407 | new_array = array - 32768 |
381 | 408 | array = new_array |
382 | 409 | |
383 | - if orientation == 'CORONAL': | |
410 | + if orientation == "CORONAL": | |
384 | 411 | array.shape = matrix.shape[0], matrix.shape[2] |
385 | - matrix[:, n, :] = array[:,::-1] | |
386 | - elif orientation == 'SAGITTAL': | |
412 | + matrix[:, n, :] = array[:, ::-1] | |
413 | + elif orientation == "SAGITTAL": | |
387 | 414 | array.shape = matrix.shape[0], matrix.shape[1] |
388 | 415 | # TODO: Verify if it's necessary to add the slices swapped only in |
389 | 416 | # sagittal rmi or only in # Rasiane's case or is necessary in all |
390 | 417 | # sagittal cases. |
391 | - matrix[:, :, n] = array[:,::-1] | |
418 | + matrix[:, :, n] = array[:, ::-1] | |
392 | 419 | else: |
393 | 420 | array.shape = matrix.shape[1], matrix.shape[2] |
394 | 421 | matrix[n] = array |
395 | - | |
422 | + | |
396 | 423 | if len(files) > 1: |
397 | - update_progress(cont,message) | |
424 | + update_progress(cont, message) | |
398 | 425 | cont += 1 |
399 | 426 | |
400 | 427 | matrix.flush() |
... | ... | @@ -411,27 +438,29 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): |
411 | 438 | """ |
412 | 439 | if len(files) > 1: |
413 | 440 | message = _("Generating multiplanar visualization...") |
414 | - update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") | |
441 | + update_progress = vtk_utils.ShowProgress( | |
442 | + len(files) - 1, dialog_type="ProgressDialog" | |
443 | + ) | |
415 | 444 | |
416 | 445 | first_slice = read_dcm_slice_as_np2(files[0], resolution_percentage) |
417 | 446 | slice_size = first_slice.shape[::-1] |
418 | 447 | |
419 | 448 | temp_file = tempfile.mktemp() |
420 | 449 | |
421 | - if orientation == 'SAGITTAL': | |
450 | + if orientation == "SAGITTAL": | |
422 | 451 | shape = slice_size[0], slice_size[1], len(files) |
423 | - elif orientation == 'CORONAL': | |
452 | + elif orientation == "CORONAL": | |
424 | 453 | shape = slice_size[1], len(files), slice_size[0] |
425 | 454 | else: |
426 | 455 | shape = len(files), slice_size[1], slice_size[0] |
427 | 456 | |
428 | - matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) | |
457 | + matrix = numpy.memmap(temp_file, mode="w+", dtype="int16", shape=shape) | |
429 | 458 | for n, f in enumerate(files): |
430 | 459 | im_array = read_dcm_slice_as_np2(f, resolution_percentage)[::-1] |
431 | 460 | |
432 | - if orientation == 'CORONAL': | |
461 | + if orientation == "CORONAL": | |
433 | 462 | matrix[:, shape[1] - n - 1, :] = im_array |
434 | - elif orientation == 'SAGITTAL': | |
463 | + elif orientation == "SAGITTAL": | |
435 | 464 | # TODO: Verify if it's necessary to add the slices swapped only in |
436 | 465 | # sagittal rmi or only in # Rasiane's case or is necessary in all |
437 | 466 | # sagittal cases. |
... | ... | @@ -456,15 +485,15 @@ def dcmmf2memmap(dcm_file, orientation): |
456 | 485 | pf = image.GetPixelFormat() |
457 | 486 | np_image = converters.gdcm_to_numpy(image, pf.GetSamplesPerPixel() == 1) |
458 | 487 | temp_file = tempfile.mktemp() |
459 | - matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=np_image.shape) | |
488 | + matrix = numpy.memmap(temp_file, mode="w+", dtype="int16", shape=np_image.shape) | |
460 | 489 | print("Number of dimensions", np_image.shape) |
461 | 490 | z, y, x = np_image.shape |
462 | - if orientation == 'CORONAL': | |
491 | + if orientation == "CORONAL": | |
463 | 492 | spacing = xs, zs, ys |
464 | 493 | matrix.shape = y, z, x |
465 | 494 | for n in range(z): |
466 | 495 | matrix[:, n, :] = np_image[n][::-1] |
467 | - elif orientation == 'SAGITTAL': | |
496 | + elif orientation == "SAGITTAL": | |
468 | 497 | spacing = zs, ys, xs |
469 | 498 | matrix.shape = y, x, z |
470 | 499 | for n in range(z): |
... | ... | @@ -495,7 +524,7 @@ def img2memmap(group): |
495 | 524 | data = numpy.swapaxes(data, 0, 2) |
496 | 525 | data = numpy.fliplr(data) |
497 | 526 | |
498 | - matrix = numpy.memmap(temp_file, mode='w+', dtype=np.int16, shape=data.shape) | |
527 | + matrix = numpy.memmap(temp_file, mode="w+", dtype=np.int16, shape=data.shape) | |
499 | 528 | matrix[:] = data[:] |
500 | 529 | matrix.flush() |
501 | 530 | |
... | ... | @@ -507,10 +536,14 @@ def img2memmap(group): |
507 | 536 | def get_LUT_value_255(data, window, level): |
508 | 537 | shape = data.shape |
509 | 538 | data_ = data.ravel() |
510 | - data = np.piecewise(data_, | |
511 | - [data_ <= (level - 0.5 - (window-1)/2), | |
512 | - data_ > (level - 0.5 + (window-1)/2)], | |
513 | - [0, 255, lambda data_: ((data_ - (level - 0.5))/(window-1) + 0.5)*(255)]) | |
539 | + data = np.piecewise( | |
540 | + data_, | |
541 | + [ | |
542 | + data_ <= (level - 0.5 - (window - 1) / 2), | |
543 | + data_ > (level - 0.5 + (window - 1) / 2), | |
544 | + ], | |
545 | + [0, 255, lambda data_: ((data_ - (level - 0.5)) / (window - 1) + 0.5) * (255)], | |
546 | + ) | |
514 | 547 | data.shape = shape |
515 | 548 | return data |
516 | 549 | |
... | ... | @@ -534,7 +567,9 @@ def world2invspace(affine=None): |
534 | 567 | |
535 | 568 | # remove scaling factor for non-unitary voxel dimensions |
536 | 569 | scale, shear, angs, trans, persp = tr.decompose_matrix(affine) |
537 | - affine_noscale = tr.compose_matrix(scale=None, shear=shear, angles=angs, translate=trans, perspective=persp) | |
570 | + affine_noscale = tr.compose_matrix( | |
571 | + scale=None, shear=shear, angles=angs, translate=trans, perspective=persp | |
572 | + ) | |
538 | 573 | # repos_img = [0.] * 6 |
539 | 574 | # repos_img[1] = -float(shape[1]) |
540 | 575 | # |
... | ... | @@ -579,7 +614,7 @@ def convert_world_to_voxel(xyz, affine): |
579 | 614 | |
580 | 615 | # print("xyz: ", xyz, "\naffine", affine) |
581 | 616 | # convert xyz coordinate to 1x4 homogeneous coordinates array |
582 | - xyz_homo = np.hstack((xyz, 1.)).reshape([4, 1]) | |
617 | + xyz_homo = np.hstack((xyz, 1.0)).reshape([4, 1]) | |
583 | 618 | ijk_homo = np.linalg.inv(affine) @ xyz_homo |
584 | 619 | ijk = ijk_homo.T[np.newaxis, 0, :3] |
585 | 620 | |
... | ... | @@ -587,13 +622,13 @@ def convert_world_to_voxel(xyz, affine): |
587 | 622 | |
588 | 623 | |
589 | 624 | def create_grid(xy_range, z_range, z_offset, spacing): |
590 | - x = np.arange(xy_range[0], xy_range[1]+1, spacing) | |
591 | - y = np.arange(xy_range[0], xy_range[1]+1, spacing) | |
592 | - z = z_offset + np.arange(z_range[0], z_range[1]+1, spacing) | |
625 | + x = np.arange(xy_range[0], xy_range[1] + 1, spacing) | |
626 | + y = np.arange(xy_range[0], xy_range[1] + 1, spacing) | |
627 | + z = z_offset + np.arange(z_range[0], z_range[1] + 1, spacing) | |
593 | 628 | xv, yv, zv = np.meshgrid(x, y, -z) |
594 | 629 | coord_grid = np.array([xv, yv, zv]) |
595 | 630 | # create grid of points |
596 | - grid_number = x.shape[0]*y.shape[0]*z.shape[0] | |
631 | + grid_number = x.shape[0] * y.shape[0] * z.shape[0] | |
597 | 632 | coord_grid = coord_grid.reshape([3, grid_number]).T |
598 | 633 | # sort grid from distance to the origin/coil center |
599 | 634 | coord_list = coord_grid[np.argsort(np.linalg.norm(coord_grid, axis=1)), :] |
... | ... | @@ -604,11 +639,11 @@ def create_grid(xy_range, z_range, z_offset, spacing): |
604 | 639 | |
605 | 640 | |
606 | 641 | def create_spherical_grid(radius=10, subdivision=1): |
607 | - x = np.linspace(-radius, radius, int(2*radius/subdivision)+1) | |
642 | + x = np.linspace(-radius, radius, int(2 * radius / subdivision) + 1) | |
608 | 643 | xv, yv, zv = np.meshgrid(x, x, x) |
609 | 644 | coord_grid = np.array([xv, yv, zv]) |
610 | 645 | # create grid of points |
611 | - grid_number = x.shape[0]**3 | |
646 | + grid_number = x.shape[0] ** 3 | |
612 | 647 | coord_grid = coord_grid.reshape([3, grid_number]).T |
613 | 648 | |
614 | 649 | sph_grid = coord_grid[np.linalg.norm(coord_grid, axis=1) < radius, :] | ... | ... |
invesalius/reader/bitmap_reader.py
1 | -#-------------------------------------------------------------------------- | |
1 | +# -------------------------------------------------------------------------- | |
2 | 2 | # Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas |
3 | 3 | # Copyright: (C) 2001 Centro de Pesquisas Renato Archer |
4 | 4 | # Homepage: http://www.softwarepublico.gov.br |
5 | 5 | # Contact: invesalius@cti.gov.br |
6 | 6 | # License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) |
7 | -#-------------------------------------------------------------------------- | |
7 | +# -------------------------------------------------------------------------- | |
8 | 8 | # Este programa e software livre; voce pode redistribui-lo e/ou |
9 | 9 | # modifica-lo sob os termos da Licenca Publica Geral GNU, conforme |
10 | 10 | # publicada pela Free Software Foundation; de acordo com a versao 2 |
... | ... | @@ -15,56 +15,55 @@ |
15 | 15 | # COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM |
16 | 16 | # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais |
17 | 17 | # detalhes. |
18 | -#-------------------------------------------------------------------------- | |
18 | +# -------------------------------------------------------------------------- | |
19 | +import imghdr | |
19 | 20 | import os |
20 | -import threading | |
21 | -import tempfile | |
21 | +import re | |
22 | 22 | import sys |
23 | +import tempfile | |
24 | +import threading | |
25 | +from multiprocessing import cpu_count | |
26 | + | |
27 | +import numpy | |
23 | 28 | import vtk |
24 | -import re | |
25 | -import invesalius.constants as const | |
26 | 29 | import wx |
27 | - | |
30 | +from imageio import imread | |
28 | 31 | from pubsub import pub as Publisher |
29 | -from multiprocessing import cpu_count | |
30 | - | |
31 | 32 | from vtk.util import numpy_support |
32 | -from imageio import imread | |
33 | -import numpy | |
34 | -import imghdr | |
35 | 33 | |
36 | -import invesalius.utils as utils | |
34 | +import invesalius.constants as const | |
37 | 35 | import invesalius.data.converters as converters |
36 | +import invesalius.utils as utils | |
38 | 37 | from invesalius import inv_paths |
39 | 38 | |
40 | - | |
41 | -#flag to control vtk error in read files | |
42 | -no_error = True | |
39 | +# flag to control vtk error in read files | |
40 | +no_error = True | |
43 | 41 | vtk_error = False |
44 | 42 | |
45 | -if sys.platform == 'win32': | |
43 | +if sys.platform == "win32": | |
46 | 44 | try: |
47 | 45 | import win32api |
46 | + | |
48 | 47 | _has_win32api = True |
49 | 48 | except ImportError: |
50 | 49 | _has_win32api = False |
51 | 50 | else: |
52 | 51 | _has_win32api = False |
53 | 52 | |
54 | -class Singleton: | |
55 | 53 | |
56 | - def __init__(self,klass): | |
54 | +class Singleton: | |
55 | + def __init__(self, klass): | |
57 | 56 | self.klass = klass |
58 | 57 | self.instance = None |
59 | - | |
60 | - def __call__(self,*args,**kwds): | |
58 | + | |
59 | + def __call__(self, *args, **kwds): | |
61 | 60 | if self.instance == None: |
62 | - self.instance = self.klass(*args,**kwds) | |
61 | + self.instance = self.klass(*args, **kwds) | |
63 | 62 | return self.instance |
64 | 63 | |
64 | + | |
65 | 65 | @Singleton |
66 | 66 | class BitmapData: |
67 | - | |
68 | 67 | def __init__(self): |
69 | 68 | self.data = None |
70 | 69 | |
... | ... | @@ -86,7 +85,7 @@ class BitmapData: |
86 | 85 | |
87 | 86 | k = {} |
88 | 87 | for v in sizes: |
89 | - k[v] = '' | |
88 | + k[v] = "" | |
90 | 89 | |
91 | 90 | if len(k.keys()) > 1: |
92 | 91 | return False |
... | ... | @@ -94,10 +93,10 @@ class BitmapData: |
94 | 93 | return True |
95 | 94 | |
96 | 95 | def GetFirstPixelSize(self): |
97 | - | |
98 | - path = self.data[0][0] | |
96 | + | |
97 | + path = self.data[0][0] | |
99 | 98 | size = ReadBitmap(path).dtype.itemsize * 8 |
100 | - | |
99 | + | |
101 | 100 | return size |
102 | 101 | |
103 | 102 | def RemoveFileByPath(self, path): |
... | ... | @@ -110,8 +109,8 @@ class BitmapData: |
110 | 109 | if path.encode(const.FS_ENCODE) in v: |
111 | 110 | return i |
112 | 111 | |
113 | -class BitmapFiles: | |
114 | 112 | |
113 | +class BitmapFiles: | |
115 | 114 | def __init__(self): |
116 | 115 | self.bitmapfiles = [] |
117 | 116 | |
... | ... | @@ -119,7 +118,7 @@ class BitmapFiles: |
119 | 118 | self.bitmapfiles.append(bmp) |
120 | 119 | |
121 | 120 | def Sort(self, x): |
122 | - c_re = re.compile('\d+') | |
121 | + c_re = re.compile("\d+") | |
123 | 122 | if len(c_re.findall(x[6])) > 0: |
124 | 123 | return [int(i) for i in c_re.findall(x[6])] |
125 | 124 | else: |
... | ... | @@ -127,37 +126,37 @@ class BitmapFiles: |
127 | 126 | |
128 | 127 | def GetValues(self): |
129 | 128 | bmpfile = self.bitmapfiles |
130 | - bmpfile.sort(key = self.Sort) | |
129 | + bmpfile.sort(key=self.Sort) | |
131 | 130 | |
132 | 131 | bmp_data = BitmapData() |
133 | 132 | bmp_data.data = bmpfile |
134 | 133 | |
135 | 134 | return bmpfile |
136 | 135 | |
137 | -class LoadBitmap: | |
138 | 136 | |
137 | +class LoadBitmap: | |
139 | 138 | def __init__(self, bmp_file, filepath): |
140 | 139 | self.bmp_file = bmp_file |
141 | 140 | self.filepath = utils.decode(filepath, const.FS_ENCODE) |
142 | - | |
141 | + | |
143 | 142 | self.run() |
144 | - | |
143 | + | |
145 | 144 | def run(self): |
146 | 145 | global vtk_error |
147 | 146 | |
148 | - #----- verify extension ------------------ | |
147 | + # ----- verify extension ------------------ | |
149 | 148 | extension = VerifyDataType(self.filepath) |
150 | 149 | |
151 | 150 | file_name = self.filepath.split(os.path.sep)[-1] |
152 | 151 | |
153 | 152 | n_array = ReadBitmap(self.filepath) |
154 | - | |
155 | - if not(isinstance(n_array, numpy.ndarray)): | |
153 | + | |
154 | + if not (isinstance(n_array, numpy.ndarray)): | |
156 | 155 | return False |
157 | - | |
158 | - image = converters.to_vtk(n_array, spacing=(1,1,1),\ | |
159 | - slice_number=1, orientation="AXIAL") | |
160 | 156 | |
157 | + image = converters.to_vtk( | |
158 | + n_array, spacing=(1, 1, 1), slice_number=1, orientation="AXIAL" | |
159 | + ) | |
161 | 160 | |
162 | 161 | dim = image.GetDimensions() |
163 | 162 | x = dim[0] |
... | ... | @@ -165,16 +164,16 @@ class LoadBitmap: |
165 | 164 | |
166 | 165 | img = vtk.vtkImageResample() |
167 | 166 | img.SetInputData(image) |
168 | - img.SetAxisMagnificationFactor ( 0, 0.25 ) | |
169 | - img.SetAxisMagnificationFactor ( 1, 0.25 ) | |
170 | - img.SetAxisMagnificationFactor ( 2, 1 ) | |
167 | + img.SetAxisMagnificationFactor(0, 0.25) | |
168 | + img.SetAxisMagnificationFactor(1, 0.25) | |
169 | + img.SetAxisMagnificationFactor(2, 1) | |
171 | 170 | img.Update() |
172 | 171 | |
173 | 172 | tp = img.GetOutput().GetScalarTypeAsString() |
174 | 173 | |
175 | 174 | image_copy = vtk.vtkImageData() |
176 | 175 | image_copy.DeepCopy(img.GetOutput()) |
177 | - | |
176 | + | |
178 | 177 | thumbnail_path = tempfile.mktemp() |
179 | 178 | |
180 | 179 | write_png = vtk.vtkPNGWriter() |
... | ... | @@ -187,20 +186,28 @@ class LoadBitmap: |
187 | 186 | img = vtk.vtkImageCast() |
188 | 187 | img.SetInputData(image_copy) |
189 | 188 | img.SetOutputScalarTypeToUnsignedShort() |
190 | - #img.SetClampOverflow(1) | |
189 | + # img.SetClampOverflow(1) | |
191 | 190 | img.Update() |
192 | 191 | |
193 | 192 | write_png = vtk.vtkPNGWriter() |
194 | 193 | write_png.SetInputConnection(img.GetOutputPort()) |
195 | 194 | write_png.SetFileName(thumbnail_path) |
196 | 195 | write_png.Write() |
197 | - | |
196 | + | |
198 | 197 | vtk_error = False |
199 | 198 | |
200 | 199 | id = wx.NewId() |
201 | 200 | |
202 | - bmp_item = [self.filepath, thumbnail_path, extension, x, y,\ | |
203 | - str(x) + ' x ' + str(y), file_name, id] | |
201 | + bmp_item = [ | |
202 | + self.filepath, | |
203 | + thumbnail_path, | |
204 | + extension, | |
205 | + x, | |
206 | + y, | |
207 | + str(x) + " x " + str(y), | |
208 | + file_name, | |
209 | + id, | |
210 | + ] | |
204 | 211 | self.bmp_file.Add(bmp_item) |
205 | 212 | |
206 | 213 | |
... | ... | @@ -217,7 +224,6 @@ def yGetBitmaps(directory, recursive=True, gui=True): |
217 | 224 | dirpath, dirnames, filenames = os.walk(directory) |
218 | 225 | nfiles = len(filenames) |
219 | 226 | |
220 | - | |
221 | 227 | counter = 0 |
222 | 228 | bmp_file = BitmapFiles() |
223 | 229 | |
... | ... | @@ -228,7 +234,7 @@ def yGetBitmaps(directory, recursive=True, gui=True): |
228 | 234 | filepath = os.path.join(dirpath, name).encode(const.FS_ENCODE) |
229 | 235 | counter += 1 |
230 | 236 | if gui: |
231 | - yield (counter,nfiles) | |
237 | + yield (counter, nfiles) | |
232 | 238 | LoadBitmap(bmp_file, filepath) |
233 | 239 | else: |
234 | 240 | dirpath, dirnames, filenames = os.walk(directory) |
... | ... | @@ -236,7 +242,7 @@ def yGetBitmaps(directory, recursive=True, gui=True): |
236 | 242 | filepath = str(os.path.join(dirpath, name)).encode(const.FS_ENCODE) |
237 | 243 | counter += 1 |
238 | 244 | if gui: |
239 | - yield (counter,nfiles) | |
245 | + yield (counter, nfiles) | |
240 | 246 | |
241 | 247 | yield bmp_file.GetValues() |
242 | 248 | |
... | ... | @@ -252,12 +258,12 @@ class ProgressBitmapReader: |
252 | 258 | def SetWindowEvent(self, frame): |
253 | 259 | self.frame = frame |
254 | 260 | |
255 | - def SetDirectoryPath(self, path,recursive=True): | |
261 | + def SetDirectoryPath(self, path, recursive=True): | |
256 | 262 | self.running = True |
257 | 263 | self.stoped = False |
258 | - self.GetBitmaps(path,recursive) | |
264 | + self.GetBitmaps(path, recursive) | |
259 | 265 | |
260 | - def UpdateLoadFileProgress(self,cont_progress): | |
266 | + def UpdateLoadFileProgress(self, cont_progress): | |
261 | 267 | Publisher.sendMessage("Update bitmap load", data=cont_progress) |
262 | 268 | |
263 | 269 | def EndLoadFile(self, bitmap_list): |
... | ... | @@ -279,29 +285,31 @@ class ProgressBitmapReader: |
279 | 285 | self.UpdateLoadFileProgress(None) |
280 | 286 | self.stoped = False |
281 | 287 | |
288 | + | |
282 | 289 | def VtkErrorPNGWriter(obj, f): |
283 | 290 | global vtk_error |
284 | 291 | vtk_error = True |
285 | 292 | |
293 | + | |
286 | 294 | def ScipyRead(filepath): |
287 | 295 | try: |
288 | 296 | r = imread(filepath, flatten=True) |
289 | - dt = r.dtype | |
290 | - if dt == "float" or dt == "float16"\ | |
291 | - or dt == "float32" or dt == "float64": | |
292 | - shift=-r.max()/2 | |
293 | - simage = numpy.zeros_like(r, dtype='int16') | |
294 | - simage[:] = r.astype('int32') + shift | |
297 | + dt = r.dtype | |
298 | + if dt == "float" or dt == "float16" or dt == "float32" or dt == "float64": | |
299 | + shift = -r.max() / 2 | |
300 | + simage = numpy.zeros_like(r, dtype="int16") | |
301 | + simage[:] = r.astype("int32") + shift | |
295 | 302 | |
296 | 303 | return simage |
297 | 304 | else: |
298 | 305 | return r |
299 | - except(IOError): | |
306 | + except (IOError): | |
300 | 307 | return False |
301 | 308 | |
309 | + | |
302 | 310 | def VtkRead(filepath, t): |
303 | 311 | if not const.VTK_WARNING: |
304 | - log_path = os.path.join(inv_paths.USER_LOG_DIR, 'vtkoutput.txt') | |
312 | + log_path = os.path.join(inv_paths.USER_LOG_DIR, "vtkoutput.txt") | |
305 | 313 | fow = vtk.vtkFileOutputWindow() |
306 | 314 | fow.SetFileName(log_path.encode(const.FS_ENCODE)) |
307 | 315 | ow = vtk.vtkOutputWindow() |
... | ... | @@ -317,7 +325,7 @@ def VtkRead(filepath, t): |
317 | 325 | |
318 | 326 | elif t == "png": |
319 | 327 | reader = vtk.vtkPNGReader() |
320 | - | |
328 | + | |
321 | 329 | elif t == "jpeg" or t == "jpg": |
322 | 330 | reader = vtk.vtkJPEGReader() |
323 | 331 | |
... | ... | @@ -327,11 +335,11 @@ def VtkRead(filepath, t): |
327 | 335 | reader.AddObserver("ErrorEvent", VtkErrorToPy) |
328 | 336 | reader.SetFileName(filepath) |
329 | 337 | reader.Update() |
330 | - | |
338 | + | |
331 | 339 | if no_error: |
332 | 340 | image = reader.GetOutput() |
333 | 341 | dim = image.GetDimensions() |
334 | - | |
342 | + | |
335 | 343 | if reader.GetNumberOfScalarComponents() > 1: |
336 | 344 | luminanceFilter = vtk.vtkImageLuminance() |
337 | 345 | luminanceFilter.SetInputData(image) |
... | ... | @@ -349,7 +357,7 @@ def VtkRead(filepath, t): |
349 | 357 | return False |
350 | 358 | |
351 | 359 | |
352 | -def ReadBitmap(filepath): | |
360 | +def ReadBitmap(filepath): | |
353 | 361 | t = VerifyDataType(filepath) |
354 | 362 | |
355 | 363 | if _has_win32api: |
... | ... | @@ -361,65 +369,65 @@ def ReadBitmap(filepath): |
361 | 369 | except UnicodeDecodeError: |
362 | 370 | measures_info = False |
363 | 371 | if measures_info: |
364 | - Publisher.sendMessage('Set bitmap spacing', spacing=measures_info) | |
372 | + Publisher.sendMessage("Set bitmap spacing", spacing=measures_info) | |
365 | 373 | |
366 | 374 | return False |
367 | 375 | |
368 | 376 | img_array = VtkRead(filepath, t) |
369 | - | |
370 | - if not(isinstance(img_array, numpy.ndarray)): | |
371 | - | |
377 | + | |
378 | + if not (isinstance(img_array, numpy.ndarray)): | |
379 | + | |
372 | 380 | no_error = True |
373 | - | |
381 | + | |
374 | 382 | img_array = ScipyRead(filepath) |
375 | - | |
376 | - if not(isinstance(img_array, numpy.ndarray)): | |
383 | + | |
384 | + if not (isinstance(img_array, numpy.ndarray)): | |
377 | 385 | return False |
378 | 386 | |
379 | 387 | return img_array |
380 | - | |
388 | + | |
381 | 389 | |
382 | 390 | def GetPixelSpacingFromInfoFile(filepath): |
383 | 391 | filepath = utils.decode(filepath, const.FS_ENCODE) |
384 | - if filepath.endswith('.DS_Store'): | |
392 | + if filepath.endswith(".DS_Store"): | |
385 | 393 | return False |
386 | - fi = open(filepath, 'r') | |
394 | + fi = open(filepath, "r") | |
387 | 395 | lines = fi.readlines() |
388 | - measure_scale = 'mm' | |
396 | + measure_scale = "mm" | |
389 | 397 | values = [] |
390 | 398 | |
391 | 399 | if len(lines) > 0: |
392 | - #info text from avizo | |
393 | - if '# Avizo Stacked Slices' in lines[0]: | |
394 | - value = lines[2].split(' ') | |
400 | + # info text from avizo | |
401 | + if "# Avizo Stacked Slices" in lines[0]: | |
402 | + value = lines[2].split(" ") | |
395 | 403 | spx = float(value[1]) |
396 | 404 | spy = float(value[2]) |
397 | - value = lines[5].split(' ') | |
405 | + value = lines[5].split(" ") | |
398 | 406 | spz = float(value[1]) |
399 | 407 | |
400 | 408 | return [spx * 0.001, spy * 0.001, spz * 0.001] |
401 | 409 | else: |
402 | - #info text from skyscan | |
410 | + # info text from skyscan | |
403 | 411 | for l in lines: |
404 | - if 'Pixel Size' in l: | |
405 | - if 'um' in l: | |
406 | - measure_scale = 'um' | |
407 | - | |
412 | + if "Pixel Size" in l: | |
413 | + if "um" in l: | |
414 | + measure_scale = "um" | |
415 | + | |
408 | 416 | value = l.split("=")[-1] |
409 | 417 | values.append(value) |
410 | 418 | |
411 | 419 | if len(values) > 0: |
412 | 420 | value = values[-1] |
413 | - | |
414 | - value = value.replace('\n','') | |
415 | - value = value.replace('\r','') | |
416 | 421 | |
417 | - #convert um to mm (InVesalius default) | |
418 | - if measure_scale == 'um': | |
422 | + value = value.replace("\n", "") | |
423 | + value = value.replace("\r", "") | |
424 | + | |
425 | + # convert um to mm (InVesalius default) | |
426 | + if measure_scale == "um": | |
419 | 427 | value = float(value) * 0.001 |
420 | - measure_scale = 'mm' | |
428 | + measure_scale = "mm" | |
421 | 429 | |
422 | - elif measure_scale == 'nm': | |
430 | + elif measure_scale == "nm": | |
423 | 431 | value = float(value) * 0.000001 |
424 | 432 | |
425 | 433 | return [value, value, value] |
... | ... | @@ -428,6 +436,7 @@ def GetPixelSpacingFromInfoFile(filepath): |
428 | 436 | else: |
429 | 437 | return False |
430 | 438 | |
439 | + | |
431 | 440 | def VtkErrorToPy(obj, evt): |
432 | 441 | global no_error |
433 | 442 | no_error = False |
... | ... | @@ -442,4 +451,3 @@ def VerifyDataType(filepath): |
442 | 451 | return False |
443 | 452 | except IOError: |
444 | 453 | return False |
445 | - | ... | ... |