Commit 07f7b52a73260f1bb6a8247eec4644c3d768f2f0

Authored by Thiago Franco de Moraes
1 parent 61106a5f
Exists in master

iSort and Black to improve source code format in bitmap reader and imagedata_utils

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   -
... ...