# -*- coding: utf-8 -*- # This software and supporting documentation are distributed by # bioPICSEL # CEA/DSV/I²BM/MIRCen/LMN, Batiment 61, # 18, route du Panorama # 92265 Fontenay-aux-Roses # France # # This software is governed by the CeCILL license version 2 under # French law and abiding by the rules of distribution of free software. # You can use, modify and/or redistribute the software under the # terms of the CeCILL license version 2 as circulated by CEA, CNRS # and INRIA at the following URL "http://www.cecill.info". # # As a counterpart to the access to the source code and rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors have only limited # liability. # # In this respect, the user's attention is drawn to the risks associated # with loading, using, modifying and/or developing or reproducing the # software by the user in light of its specific status of free software, # that may mean that it is complicated to manipulate, and that also # therefore means that it is reserved for developers and experienced # professionals having in-depth computer knowledge. Users are therefore # encouraged to load and test the software's suitability as regards their # requirements in conditions enabling the security of their systems and/or # data to be ensured and, more generally, to use and operate it in the # same conditions as regards security. # # The fact that you are presently reading this means that you have had # knowledge of the CeCILL license version 2 and that you accept its terms. # Ligne obligatoire from brainvisa.processes import * import os, os.path, string, math, decimal # Pour les formats de fichier Aims import shfjGlobals # Import brainrat libraries import brainrat.iosupport as iosupport import brainrat.uicheck as uicheck name = 'IndividualizingAndStackingMultipleSlices' userLevel = 0 def getDimSizes( self, numberOfSlicesPerScan, columnsPerGlassSlides, linesPerGlassSlides ) : # Here are the 16 usable arrangements # dimorders : [0 1], dimsizes : [line, column, 1] => Top->Bottom, line first, Left-Right, Top->Bottom # dimorders : [0 1], dimsizes : [-line, column, 1] => Top->Bottom, line first, Right-Left, Top->Bottom # dimorders : [0 1], dimsizes : [line, -column, 1] => Top->Bottom, line first, Left-Right, Bottom->Top # dimorders : [0 1], dimsizes : [-line, -column, 1] => Top->Bottom, line first, Right-Left, Bottom->Top # dimorders : [0 1], dimsizes : [line, column, -1] => Bottom->Top, line first, Left-Right, Top->Bottom # dimorders : [0 1], dimsizes : [-line, column, -1] => Bottom->Top, line first, Right-Left, Top->Bottom # dimorders : [0 1], dimsizes : [line, -column, -1] => Bottom->Top, line first, Left-Right, Bottom->Top # dimorders : [0 1], dimsizes : [-line, -column, -1] => Bottom->Top, line first, Right-Left, Bottom->Top # dimorders : [1 0], dimsizes : [line, column, 1] => Top->Bottom, column first, Left-Right, Top->Bottom # dimorders : [1 0], dimsizes : [-line, column, 1] => Top->Bottom, column first, Right-Left, Top->Bottom # dimorders : [1 0], dimsizes : [line, -column, 1] => Top->Bottom, column first, Left-Right, Bottom->Top # dimorders : [1 0], dimsizes : [-line, -column, 1] => Top->Bottom, column first, Right-Left, Bottom->Top # dimorders : [1 0], dimsizes : [line, column, -1] => Bottom->Top, column first, Left-Right, Top->Bottom # dimorders : [1 0], dimsizes : [-line, column, -1] => Bottom->Top, column first, Right-Left, Top->Bottom # dimorders : [1 0], dimsizes : [line, -column, -1] => Bottom->Top, column first, Left-Right, Bottom->Top # dimorders : [1 0], dimsizes : [-line, -column, -1] => Bottom->Top, column first, Right-Left, Bottom->Top arrangements = [ [ 1, 1 ], [ -1, 1 ], [ 1, -1 ], [ -1, -1 ] ] selected = arrangements[ self.Local_Glass_Slide_Filling ] dimsizes = [ selected[ 0 ] * columnsPerGlassSlides ] dimsizes += [ selected[ 1 ] * linesPerGlassSlides ] dimsizes += [ int(math.ceil(decimal.Decimal(repr(numberOfSlicesPerScan)) / (linesPerGlassSlides * columnsPerGlassSlides)) * self.Glass_Slide_Order) ] return dimsizes def getNewSignature( self, Global_Glass_Slide_Filling, Threshold_Computation ) : # Create signature parameters paramSignature = [ 'Input_ListOf_2dImage', ListOf( ReadDiskItem( '2D Image', 'Biology 2D image formats' ) ) ] paramSignature += [ 'Input_ListOf_2dImage_Preprocessed', ListOf( ReadDiskItem( '2D Image', 'Biology 2D image formats' ) ) ] paramSignature += [ 'Scanner_Resolution', Integer() ] paramSignature += [ 'Threshold_Computation', Choice( ('Automated', 1), ('Manual', 2) ) ] if ( Threshold_Computation == 2 ) : paramSignature += [ 'Low_Threshold', Integer() ] paramSignature += [ 'High_Threshold', Integer() ] elif ( not self is None ) : self.Low_Threshold = None self.High_Threshold = None paramSignature += [ 'Glass_Slide_Order', Choice( ( 'Top -> Bottom', 1 ), ( 'Bottom -> Top', -1 ) ) ] paramSignature += [ 'Global_Glass_Slide_Filling', Choice( ( 'According to line first', 0 ), ( 'According to column first', 1 ) ) ] if ( ( Global_Glass_Slide_Filling is None ) or ( Global_Glass_Slide_Filling == 1 ) ) : paramSignature += [ 'Local_Glass_Slide_Filling', Choice( ( 'Top -> Bottom, Left -> Right', 0 ), ( 'Top -> Bottom, Right -> Left', 1 ), ( 'Bottom -> Top, Left -> Right', 2 ), ( 'Bottom -> Top, Right -> Left', 3 ) ) ] else : paramSignature += [ 'Local_Glass_Slide_Filling', Choice( ( 'Left -> Right, Top -> Bottom', 0 ), ( 'Right -> Left, Top -> Bottom', 1 ), ( 'Left -> Right, Bottom -> Top', 2 ), ( 'Right -> Left, Bottom -> Top', 3 ) ) ] paramSignature += [ 'ListOf_NumberOf_Slices_Per_Scan', ListOf( Integer() ) ] paramSignature += [ 'ListOf_NumberOf_Lines_Per_Glass_Slides', ListOf( Integer() ) ] paramSignature += [ 'ListOf_NumberOf_Columns_Per_Glass_Slides', ListOf( Integer() ) ] paramSignature += [ 'ListOf_Minimum_Slice_Surface', ListOf( Integer() ) ] paramSignature += [ 'Dimension_X', Integer() ] paramSignature += [ 'Dimension_Y', Integer() ] paramSignature += [ 'Inter_Slice_Thickness', Float()] paramSignature += [ 'Output_3dImage', WriteDiskItem( '3D Volume', 'Biology 3D volume formats' ) ] # Create signature object signature = Signature( *paramSignature ) # Sets optional parameters signature[ 'Input_ListOf_2dImage_Preprocessed' ].mandatory = False return signature # Signature is dynamically created signature = getNewSignature( None, None, None) def globalSlideFillingChanged( self, Global_Glass_Slide_Filling ) : signature = self.getNewSignature( Global_Glass_Slide_Filling, self.Threshold_Computation ) self.changeSignature( signature ) def thresholdComputationChanged( self, Threshold_Computation ) : signature = self.getNewSignature( self.Global_Glass_Slide_Filling, Threshold_Computation ) self.changeSignature( signature ) def initialization( self ): # Sets default parameters value self.Global_Glass_Slide_Filling = 1 self.Scanner_Resolution = 600 self.Threshold_Computation = 1 # Add links self.addLink(None, 'Global_Glass_Slide_Filling' , self.globalSlideFillingChanged ) self.addLink(None, 'Threshold_Computation' , self.thresholdComputationChanged ) def execution( self, context ): try : # Check parameters if not uicheck.objectattr( context, self, 'Scanner_Resolution', 0, operator.gt ) : return if not uicheck.objectattr( context, self, 'Dimension_X', 0, operator.gt ) : return if not uicheck.objectattr( context, self, 'Dimension_Y', 0, operator.gt ) : return if not uicheck.objectattr( context, self, 'Inter_Slice_Thickness', 0, operator.gt ) : return if not uicheck.objectlistattr( context, self, 'ListOf_NumberOf_Slices_Per_Scan', 0, operator.gt ) : return if not uicheck.objectlistattr( context, self, 'ListOf_NumberOf_Lines_Per_Glass_Slides', 0, operator.gt ) : return if not uicheck.objectlistattr( context, self, 'ListOf_NumberOf_Columns_Per_Glass_Slides', 0, operator.gt ) : return if not uicheck.objectlistattr( context, self, 'ListOf_Minimum_Slice_Surface', 0, operator.gt ) : return if not self.Low_Threshold is None and not uicheck.objectattr( context, self, 'Low_Threshold', 0, operator.ge ) : return if not self.High_Threshold is None and not uicheck.objectattr( context, self, 'High_Threshold', 0, operator.ge ) : return if len( self.Input_ListOf_2dImage ) != len( self.ListOf_NumberOf_Slices_Per_Scan ) : context.error( _t_( '\'%s\' and \'%s\' must have the same length. Number of slices must be specified for each scanned image.' ) % ( 'ListOf_NumberOf_Slices_Per_Scan', 'Input_ListOf_2dImage' ) ) return if len( self.Input_ListOf_2dImage ) != len( self.ListOf_NumberOf_Lines_Per_Glass_Slides ) : context.error( _t_( '\'%s\' and \'%s\' must have the same length. Number of lines per glass slide must be specified for each scanned image.' ) % ( 'ListOf_NumberOf_Lines_Per_Glass_Slides', 'Input_ListOf_2dImage' ) ) return if len( self.Input_ListOf_2dImage ) != len( self.ListOf_NumberOf_Columns_Per_Glass_Slides ) : context.error( _t_( '\'%s\' and \'%s\' must have the same length. Number of columns per glass slide must be specified for each scanned image.' ) % ( 'ListOf_NumberOf_Columns_Per_Glass_Slides', 'Input_ListOf_2dImage' ) ) return if len( self.Input_ListOf_2dImage ) != len( self.ListOf_Minimum_Slice_Surface ) : context.error( _t_( '\'%s\' and \'%s\' must have the same length. Minimum slice surface must be specified for each scanned image.' ) % ( 'ListOf_Minimum_Slice_Surface', 'Input_ListOf_2dImage' ) ) return if ( not self.Input_ListOf_2dImage_Preprocessed is None ) \ and ( len( self.Input_ListOf_2dImage_Preprocessed ) > 0 ) \ and ( len( self.Input_ListOf_2dImage ) != len( self.Input_ListOf_2dImage_Preprocessed ) ) : context.error( _t_( '\'%s\' and \'%s\' must have the same length. A matching preprocessed image must be specified for each scanned image.' ) % ( 'Input_ListOf_2dImage_Preprocessed', 'Input_ListOf_2dImage' ) ) return # Write start message context.write( _t_( 'Extracting slices from input images' ), ' ...' ) # Extract slices from images temporaryFiles = [] for i in xrange( len( self.Input_ListOf_2dImage ) ): # Get dimension orders and dimension sizes dimsizes = self.getDimSizes( self.ListOf_NumberOf_Slices_Per_Scan[i], self.ListOf_NumberOf_Columns_Per_Glass_Slides[ i ], self.ListOf_NumberOf_Lines_Per_Glass_Slides[ i ] ) if self.Global_Glass_Slide_Filling == 0 : dimorders = [ 0, 1 ] else : dimorders = [ 1, 0 ] # Define temporary files file = self.Input_ListOf_2dImage[i].fullPath() dirName = os.path.dirname( file ) temporary = context.temporary( 'GIS image' ) temporaryFiles.append( temporary ) # Convert input images to normalized S16 GIS image iosupport.normalizeFile( context, file, temporary.fullPath() ) # Individualizing and stacking slices command = [ 'AimsIndividStackSlices', '-i', temporary.fullPath(), '-o', temporary.fullPath(), '-dx', self.Dimension_X, '-dy', self.Dimension_Y, '-z', self.Inter_Slice_Thickness, '--dimsizes', dimsizes[0], dimsizes[1], dimsizes[2], '--dimorders', dimorders[0], dimorders[1], '-ms', self.ListOf_Minimum_Slice_Surface[i], '-res', self.Scanner_Resolution, '-n', self.ListOf_NumberOf_Slices_Per_Scan[i] ] if self.Threshold_Computation == 2 : command += [ '-lt', self.Low_Threshold, '-ht', self.High_Threshold ] if ( ( not self.Input_ListOf_2dImage_Preprocessed is None ) and ( len( self.Input_ListOf_2dImage_Preprocessed ) > 0 ) ) : temporaryPreProcessed = context.temporary( 'GIS image' ) preProcessedFile = self.Input_ListOf_2dImage_Preprocessed[ i ] # Convert preprocessed input images to normalized S16 GIS image iosupport.normalizeFile( context, preProcessedFile.fullPath(), temporaryPreProcessed.fullPath() ) command += [ '-pi', temporaryPreProcessed.fullPath() ] context.system( *command ) # Write column message context.write( _t_( 'Column' ), i+1, ' processed.' ) totalslices = 0 for value in self.ListOf_NumberOf_Slices_Per_Scan : totalslices += value context.write( _t_( 'Building output volume with extracted slices [ count : %s ]' ) % (totalslices, ), ' ...' ) # Concatenate extracted volumes to the output volume command = ['AimsZCat', '-o', self.Output_3dImage, '-i'] + temporaryFiles context.system( *command ) except Exception, error : context.error( _t_( uicheck.messages[ 'processingerrormessage' ] ) )