16
16
17
17
#Import external modules
18
18
from hdiutils .HDIimport import hdi_reader
19
+ from hdiutils .HDIexport import hdi_exporter
19
20
20
21
def GetCropCoords (coords_csv , correction = 80 ):
21
22
"""
@@ -300,22 +301,19 @@ def MultiTransformix(in_im, out_dir, tps):
300
301
return res_name
301
302
302
303
303
- #in_im="/Users/joshuahess/Desktop/ Test/ new_im_2_processed.nii"
304
- #out_dir="/Users/joshuahess/Desktop/ Test"
305
- #tps="/Users/joshuahess/Desktop/ Test/ TransformParameters.0.txt"
304
+ #in_im=r"D:\Josh_Hess\01_10_19_MSI_peak_pick_20191025\ Test\ new_im_2_processed.nii"
305
+ #out_dir=r"D:\Josh_Hess\01_10_19_MSI_peak_pick_20191025\ Test"
306
+ #tps=r"D:\Josh_Hess\01_10_19_MSI_peak_pick_20191025\ Test\ TransformParameters.0.txt"
306
307
#in_target_size = None
307
308
#crops = None
308
-
309
-
310
-
311
-
309
+ #Transformix(in_im, out_dir, tps, in_target_size, crops)
312
310
313
311
#Create class structure for transformix implementation
314
312
class Transformix ():
315
313
"""Python class for transformix
316
314
"""
317
315
318
- def __init__ (self , in_im , out_dir , tps , in_target_size = None , crops = None ):
316
+ def __init__ (self , in_im , out_dir , tps , target_size = None , pad = None , trim = None , crops = None , out_ext = ".nii" ):
319
317
"""
320
318
initialize class instance and run transformix with the input parameters
321
319
@@ -325,7 +323,7 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
325
323
326
324
tps: list of paths to transform parameters -- let them be in order!
327
325
328
- in_target_size : tuple indicating the target size for any rescaling applied
326
+ target_size : tuple indicating the target size for any rescaling applied
329
327
to input image prior to transforming
330
328
331
329
crops: None if no cropping and subsequent transforming to be done.
@@ -345,11 +343,18 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
345
343
self .out_dir = Path (out_dir )
346
344
self .tps = [Path (t ) for t in tps ]
347
345
self .command = "transformix"
348
- self .baseName = self .in_im .stem
346
+ self .baseName = self .in_im .stem . replace ( ".ome" , "" )
349
347
self .ext = "" .join (self .in_im .suffixes )
350
-
351
- #Load the images to check for dimension number
352
- print ('Loading images...' )
348
+ self .intermediate = False
349
+ self .out_ext = out_ext
350
+ # Check for input list or none
351
+ if target_size != None :
352
+ # convert it to tuple from list (command line parser)
353
+ target_size = tuple (target_size )
354
+ # Check for input extension or none
355
+ if self .out_ext == None :
356
+ # convert it to be whatever extension the input image contains as default
357
+ self .out_ext = self .ext
353
358
#Load images
354
359
niiIn = hdi_reader .HDIreader (
355
360
path_to_data = in_im ,
@@ -359,51 +364,54 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
359
364
mask = None ,
360
365
save_mem = False
361
366
)
362
- #Print update
363
- print ('Done loading' )
364
-
365
- # here we will get the extension of the image and will convert it to the nift-1
366
- # format if it is not already in that format. While users can supply their own
367
- # nifti formatted image to the pipeline, this ensures that other file formats
368
- # can be used, although, it creates additionally overhead
369
- # get the extension of the image
370
- # ensure the input to transformix is nifti
371
- if self .ext != ".nii" :
372
- # get the shape of the image
373
- shp = len (niiIn .hdi .data .image_shape )
374
- # create new name for the temporary image
375
- tmp_nm = os .path .join (out_dir , next (tempfile ._get_candidate_names ())+ ".nii" )
376
- # export nifti intermediate
377
- print ('Creating nifti-1 intermediate for registration' )
378
- # export nifti
379
- if shp > 2 :
380
- # Create nifti object -- transpose axes because of the transformation!
381
- nii_im = nib .Nifti1Image (niiIn .hdi .data .image .transpose (1 , 0 , 2 ), affine = np .eye (4 ))
382
- else :
383
- # Create nifti object -- transpose axes because of the transformation!
384
- nii_im = nib .Nifti1Image (niiIn .hdi .data .image .T , affine = np .eye (4 ))
385
- #Save the nifti image
386
- nib .save (nii_im ,str (tmp_nm ))
387
- # remove the nifit memory
388
- nii_im = None
389
- # update the image name
390
- print ('Using nifti-1 intermediate for registration' )
391
- # update the input image
392
- self .in_im = Path (tmp_nm )
393
-
394
- #Load the images to check for dimension number
395
- print ('Loading images...' )
396
- #Load images
397
- niiIn = nib .load (str (self .in_im ))
398
- #Print update
399
- print ('Done loading' )
400
-
401
367
#Check to see if there is single channel input (grayscale)
402
- if niiIn .ndim == 2 :
368
+ if len ( niiIn .hdi . data . image_shape ) > 2 :
403
369
#Update multichannel class option
370
+ self .multichannel = True
371
+ # otherwise this is a single channel image
372
+ else :
373
+ # multichannel is false
404
374
self .multichannel = False
405
- #Remove loaded image to clear memory
406
- niiIn = None
375
+
376
+ #Check to see if there is single channel input (grayscale)
377
+ if not self .multichannel :
378
+ # here we will get the extension of the image and will convert it to the nift-1
379
+ # format if it is not already in that format. While users can supply their own
380
+ # nifti formatted image to the pipeline, this ensures that other file formats
381
+ # can be used, although, it creates additionally overhead
382
+ # here we supply all preprocessing commands that were used to preprocess or morph
383
+ # the array size of the input image through the hdiprep workflow. Transformix
384
+ # must be run on images with the same size as the elastix registration
385
+ if ((self .out_ext != ".nii" ) or (target_size != None ) or (pad != None )):
386
+ # get the shape of the image
387
+ shp = len (niiIn .hdi .data .image_shape )
388
+ # create new name for the temporary image
389
+ tmp_nm = os .path .join (out_dir , next (tempfile ._get_candidate_names ())+ ".nii" )
390
+ # export nifti intermediate
391
+ print ('Creating nifti-1 intermediate for registration' )
392
+ # check for image resizing
393
+ if (target_size != None ) and (crops == None ):
394
+ # transform the image
395
+ niiIn .hdi .data .image = resize (niiIn .hdi .data .image ,target_size )
396
+ # check for padding
397
+ if pad != None :
398
+ # pad the single-channel
399
+ niiIn .hdi .data .image = np .pad (image ,[(pad [0 ], pad [0 ]), (pad [1 ], pad [1 ])],mode = 'constant' )
400
+ # Create nifti oject -- transpose axes because of the transformation!
401
+ nii_im = nib .Nifti1Image (niiIn .hdi .data .image .T , affine = np .eye (4 ))
402
+ #Save the nifti image
403
+ nib .save (nii_im ,str (tmp_nm ))
404
+ # remove the nifit memory
405
+ nii_im = None
406
+ # update the image name
407
+ print ('Using nifti-1 intermediate for registration' )
408
+ # update the input image
409
+ self .in_im = Path (tmp_nm )
410
+ # update the intermediate flag
411
+ self .intermediate = True
412
+ #Remove loaded image to clear memory
413
+ niiIn = None
414
+
407
415
#Print update
408
416
print ('Detected single channel input images...' )
409
417
#Update the fixed channels
@@ -428,47 +436,70 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
428
436
res_name = Path (os .path .join (self .out_dir ,"result" + self .in_im .suffix ))
429
437
430
438
#Create a new name
431
- new_name = Path (os .path .join (self .out_dir ,self .baseName + '_result' + self .in_im .suffix ))
432
- #Get the resulting image to rename (so we don't overwrite results)
433
- res_name .rename (new_name )
434
-
435
-
436
- #Otherwise there is multichannel input
439
+ new_name = Path (os .path .join (self .out_dir ,self .baseName + '_result' + self .out_ext ))
440
+
441
+ # check if the output format needs to be switched -- set by the user
442
+ if (self .out_ext != ".nii" ) or (trim != None ):
443
+ # use HDIreader for now to parse image and exporter to export
444
+ niiIn = hdi_reader .HDIreader (
445
+ path_to_data = in_im ,
446
+ path_to_markers = None ,
447
+ flatten = False ,
448
+ subsample = None ,
449
+ mask = None ,
450
+ save_mem = False
451
+ )
452
+ # check the trim
453
+ if trim != None :
454
+ # trim the image borders
455
+ niiIn .hdi .data .image = niiIn .hdi .data .image [trim :- trim ,trim :- trim ]
456
+ # export new data
457
+ hdi_exporter .HDIexporter (niiIn .hdi .data .image ,new_name )
458
+ else :
459
+ # simply rename the file that is already in the nifti format
460
+ res_name .rename (new_name )
461
+
462
+ # Otherwise there is multichannel input. here we run multichannel registration
463
+ # and all operations for preprocessing are performed on a per slice basis to
464
+ # save disk space (dont have to export the full z stack at once). For now
465
+ # all processing steps are tailored for nifti formats. In the future this
466
+ # should easily be changed to allow for any data format
437
467
else :
438
- #Update multichannel class option
439
- self .multichannel = True
440
468
# print update
441
469
print ('Detected multichannel input' )
442
470
443
- #Check to see if cropping the resulting image
444
- if crops is None :
445
-
446
- #create a temporary directory using the context manager for channel-wise images
471
+ # Check to see if cropping the resulting image
472
+ if crops == None :
473
+ # create a temporary directory using the context manager for channel-wise images
447
474
with tempfile .TemporaryDirectory (dir = self .out_dir ) as tmpdirname :
448
- #Print update
475
+ # Print update
449
476
print ('Created temporary directory' , tmpdirname )
450
- #Read the image
451
- niiIn = niiIn .get_fdata ()
452
-
453
- #Iterate through the channels
454
- for i in range (niiIn .shape [2 ]):
455
- #Print update
477
+ # Iterate through the channels
478
+ for i in range (niiIn .hdi .data .image .shape [2 ]):
479
+ # Print update
456
480
print ('Working on slice ' + str (i ))
457
- #Create a name for a temporary image
458
- im_name = Path (os .path .join (tmpdirname ,self .in_im .stem + str (i )+ self . in_im . suffix ))
459
- #Update the list of names for image channels
481
+ # Create a name for a temporary image
482
+ im_name = Path (os .path .join (tmpdirname ,self .in_im .stem + str (i )+ ".nii" ))
483
+ # Update the list of names for image channels
460
484
self .in_channels .append (im_name )
485
+ # set a temporary channel to work with throughout the data prep stage
486
+ slice_in = niiIn .hdi .data .image [:,:,i ]
461
487
462
- #Check to see if the path exists
488
+ # Check to see if the path exists
463
489
if not im_name .is_file ():
464
-
465
- #Check to see if there is a target size for the image
466
- if in_target_size is not None :
467
- #Resize the image
468
- niiIn = resize (niiIn ,in_target_size )
469
-
470
- #Create a nifti image from this slice
471
- nii_im = nib .Nifti1Image (niiIn [:,:,i ], affine = np .eye (4 ))
490
+ # Check to see if there is a target size for the image
491
+ if target_size != None :
492
+ # Resize the image
493
+ slice_in = resize (slice_in ,target_size )
494
+ # check for padding
495
+ if pad != None :
496
+ # pad the single-channel
497
+ slice_in = np .pad (slice_in ,[(pad [0 ], pad [0 ]), (pad [1 ], pad [1 ])],mode = 'constant' )
498
+
499
+ # Create a nifti image from this slice
500
+ nii_im = nib .Nifti1Image (slice_in .T , affine = np .eye (4 ))
501
+ # remove memory
502
+ slice_in = None
472
503
#Save the nifti image
473
504
nib .save (nii_im ,str (im_name ))
474
505
#Remove the nifti slice to clear memory
@@ -480,7 +511,6 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
480
511
res_name = MultiTransformix (in_im = im_name , out_dir = tmpdirname , tps = self .tps )
481
512
482
513
else :
483
-
484
514
#Create a temporary command to be sent to the shell
485
515
tmp_command = self .command + ' -in ' + str (im_name ) + ' -out ' + str (tmpdirname )
486
516
#Add full tissue transform paramaeters
@@ -489,10 +519,10 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
489
519
RunTransformix (tmp_command )
490
520
491
521
#Get a temporary result name for the output of transformix (assumes nifti for now)
492
- res_name = Path (os .path .join (tmpdirname ,"result" + self . in_im . suffix ))
522
+ res_name = Path (os .path .join (tmpdirname ,"result" + ".nii" ))
493
523
494
524
#Create a new name
495
- new_name = Path (os .path .join (tmpdirname ,self .in_im .stem + str (i )+ '_result' + self . in_im . suffix ))
525
+ new_name = Path (os .path .join (tmpdirname ,self .in_im .stem + str (i )+ '_result' + ".nii" ))
496
526
#Get the resulting image to rename (so we don't overwrite results)
497
527
res_name .rename (new_name )
498
528
#Update the list of output channel names
@@ -503,13 +533,33 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
503
533
#Concatenate the output channels into a single result file in the output directory
504
534
full_result = nib .concat_images ([str (i ) for i in self .out_channels ])
505
535
#create a filename for the full nifti results
506
- full_name = Path (os .path .join (self .out_dir ,self .in_im .stem + "_result.nii" ))
507
- #Write the results to a nifti file
508
- nib .save (full_result ,str (full_name ))
509
-
510
- #Cropping is true
536
+ full_name = Path (os .path .join (self .out_dir ,self .baseName + "_result" + self .out_ext ))
537
+
538
+ # check if the output format needs to be switched -- set by the user
539
+ if (self .out_ext != ".nii" ) or (trim != None ):
540
+ # check the trim
541
+ if trim != None :
542
+ # trim the image borders
543
+ full_result = full_result .get_fdata ()[trim :- trim ,trim :- trim ,:]
544
+ # export new data
545
+ hdi_exporter .HDIexporter (full_result .transpose (1 ,0 ,2 ),full_name )
546
+ else :
547
+ # export the non trimmed image
548
+ hdi_exporter .HDIexporter (full_result .get_fdata ().transpose (1 ,0 ,2 ),full_name )
549
+ else :
550
+ # export new data using the aggregated nifti objects
551
+ # doesnt need to be formally read in because it is memory
552
+ # mapped to the full_result object
553
+ hdi_exporter .HDIexporter (full_result .get_fdata ().transpose (1 ,0 ,2 ),full_name )
554
+
555
+ # Cropping is true
556
+ # for now this is not supported in the nextflow version of miaaim.
557
+ # TODO: add this to nextflow. This can be rin python and strings
558
+ # together multiple transformations from images and crops within
559
+ # those images so that very large arrays do not have to be fully
560
+ # exported (e.g. for MSI data that contains thousands of channels
561
+ # and low resolution on full tissues)
511
562
else :
512
-
513
563
#Read the image
514
564
niiIn = niiIn .get_fdata ()
515
565
@@ -547,10 +597,10 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
547
597
if not im_name .is_file ():
548
598
549
599
#Check to see if there is a target size for the image
550
- if in_target_size != None :
600
+ if target_size != None :
551
601
#print("Got the resize")
552
602
#Create a nifti image from this slice
553
- nii_im = nib .Nifti1Image (resize (niiIn [:,:,i ],in_target_size ), affine = np .eye (4 ))
603
+ nii_im = nib .Nifti1Image (resize (niiIn [:,:,i ],target_size ), affine = np .eye (4 ))
554
604
#print(" resized")
555
605
#No resize
556
606
else :
@@ -636,19 +686,21 @@ def __init__(self, in_im, out_dir, tps, in_target_size = None, crops = None):
636
686
pads = pars ['fixed_pad' ]
637
687
#Extract only the needed region
638
688
roi_results = roi_results .get_fdata ()[pads :- pads ,pads :- pads ,:]
639
- #Create nifti object
640
- roi_results = nib .Nifti1Image (roi_results [:,:,:], affine = np .eye (4 ))
641
689
#create a filename for the full nifti results
642
- roi_name = Path (os .path .join (str (self .out_dir ),str (roi )+ "_result.nii" ))
643
- #Write the results to a nifti file
644
- nib . save ( roi_results , str ( roi_name ) )
690
+ roi_name = Path (os .path .join (str (self .out_dir ),str (roi )+ "_result" + self . out_ext ))
691
+ # use the exported from hdiutils
692
+ hdi_exporter . HDIexporter ( full_result . get_fdata (). transpose ( 1 , 0 , 2 ), roi_name )
645
693
646
694
#Remove roi results from memory
647
695
roi_results = None
648
696
649
697
#Now delete the temporary folder stored for the single channel ROI results
650
698
shutil .rmtree (tmpfolds [roi ], ignore_errors = True )
651
699
700
+ # remove the temporary image if there was a nifti-1 intermediate
701
+ if self .intermediate :
702
+ # remove using pathlib
703
+ self .in_im .unlink ()
652
704
#Print update
653
705
print ("Finished" )
654
706
0 commit comments