finished abstracting image processing engine
all imagej related code has now been moved to ijimageengine.py
This commit is contained in:
		
							parent
							
								
									7994afd01a
								
							
						
					
					
						commit
						74d7f9e603
					
				| 
						 | 
					@ -39,7 +39,10 @@ class IImageEngine(ABC):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def get_instance():
 | 
					    def get_instance():
 | 
				
			||||||
        """Static access method."""
 | 
					        """Static access method.
 | 
				
			||||||
 | 
					        :rtype IImageEngine:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert IImageEngine.__instance is not None
 | 
					        assert IImageEngine.__instance is not None
 | 
				
			||||||
        return IImageEngine.__instance
 | 
					        return IImageEngine.__instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +53,6 @@ class IImageEngine(ABC):
 | 
				
			||||||
        :param str out_file_path: eg './white_estimate.tiff'
 | 
					        :param str out_file_path: eg './white_estimate.tiff'
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @abc.abstractmethod
 | 
					    @abc.abstractmethod
 | 
				
			||||||
    def compute_max(self, images_file_path):
 | 
					    def compute_max(self, images_file_path):
 | 
				
			||||||
        """Compute for each pixel position the maximum at this position in all input images.
 | 
					        """Compute for each pixel position the maximum at this position in all input images.
 | 
				
			||||||
| 
						 | 
					@ -59,3 +61,36 @@ class IImageEngine(ABC):
 | 
				
			||||||
        :rtype IImage:
 | 
					        :rtype IImage:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abc.abstractmethod
 | 
				
			||||||
 | 
					    def perform_gray_morphology(self, image, operator, structuring_element_shape, structuring_element_radius):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        :param IImage image:
 | 
				
			||||||
 | 
					        :param str operator: eg 'open'
 | 
				
			||||||
 | 
					        :param str structuring_element_shape: eg 'square'
 | 
				
			||||||
 | 
					        :param int structuring_element_radius:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abc.abstractmethod
 | 
				
			||||||
 | 
					    def replace_border(self, image, band_width):
 | 
				
			||||||
 | 
					        """Overwrites the outer band of the image by duplicating the value of the pixels that touch this outer band
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param IImage image:
 | 
				
			||||||
 | 
					        :param int band_width: width of the outer band, in pixels
 | 
				
			||||||
 | 
					        :rtype IImage:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        adaptation for the following code from matlab telemosToolbx's estimatedwhiteFluoImageTelemos function
 | 
				
			||||||
 | 
					            outer = white_estimate;
 | 
				
			||||||
 | 
					            white_estimate = white_estimate(4:(end-3),4:(end-3));
 | 
				
			||||||
 | 
					            outer(1:3,4:(end-3))=repmat(white_estimate(1,:),3,1);  # top
 | 
				
			||||||
 | 
					            outer((end-2):end,4:(end-3))=repmat(white_estimate(end,:),3,1); # bottom
 | 
				
			||||||
 | 
					            outer(:,1:3)=repmat(outer(:,4),1,3); # left
 | 
				
			||||||
 | 
					            outer(:,(end-2):end)=repmat(outer(:,end-3),1,3); # right
 | 
				
			||||||
 | 
					            white_estimate = outer;
 | 
				
			||||||
 | 
					            clear outer;
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError()
 | 
				
			||||||
 | 
					        return image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,23 @@ class IJImage(IImage):
 | 
				
			||||||
        raise NotImplementedError
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def imagej_run_image_command(image, command, options):
 | 
				
			||||||
 | 
					    """performs the given imagej command on the given image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param ImagePlus image:
 | 
				
			||||||
 | 
					    :param str command: imagej command (eg "Gray Morphology")
 | 
				
			||||||
 | 
					    :param str options: imagej options (eg "radius=1 type=square operator=open")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    wrapper around IJ.run (https://imagej.nih.gov/ij/developer/api/ij/IJ.html#run-ij.ImagePlus-java.lang.String-java.lang.String-) which raises an exception on error
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    IJ.run(image, command, options)
 | 
				
			||||||
 | 
					    error_message = IJ.getErrorMessage()
 | 
				
			||||||
 | 
					    if error_message is not None:
 | 
				
			||||||
 | 
					        raise Exception('The command "%s" with options "%s" failed because of the following error : %s' % (command, options, error_message))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IJImageEngine(IImageEngine):
 | 
					class IJImageEngine(IImageEngine):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def missing(self):
 | 
					    def missing(self):
 | 
				
			||||||
| 
						 | 
					@ -34,7 +51,7 @@ class IJImageEngine(IImageEngine):
 | 
				
			||||||
        """Computes for each pixel position the maximum at this position in all input images.
 | 
					        """Computes for each pixel position the maximum at this position in all input images.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param list(str) images_file_path:
 | 
					        :param list(str) images_file_path:
 | 
				
			||||||
        :rtype IJmage:
 | 
					        :rtype IJImage:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        assert len(images_file_path) > 1
 | 
					        assert len(images_file_path) > 1
 | 
				
			||||||
        max_image = IJ.openImage(images_file_path[0])
 | 
					        max_image = IJ.openImage(images_file_path[0])
 | 
				
			||||||
| 
						 | 
					@ -46,4 +63,35 @@ class IJImageEngine(IImageEngine):
 | 
				
			||||||
            ic = ImageCalculator()
 | 
					            ic = ImageCalculator()
 | 
				
			||||||
            ic.run("max", max_image, other_image)
 | 
					            ic.run("max", max_image, other_image)
 | 
				
			||||||
        print('max_image', max_image)
 | 
					        print('max_image', max_image)
 | 
				
			||||||
        return max_image
 | 
					        return IJImage(self, max_image)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def perform_gray_morphology(self, image, operator, structuring_element_shape, structuring_element_radius):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        :param IJImage image:
 | 
				
			||||||
 | 
					        :param str operator: eg 'open'
 | 
				
			||||||
 | 
					        :param str structuring_element_shape: eg 'square'
 | 
				
			||||||
 | 
					        :param int structuring_element_radius:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        assert operator not in ['fast open', 'fast erode'], "as of 13/09/2019, fast operators such as 'fast erode' seem broken in fiji (the resulting image is not at all similar to their slow equivalent)"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        processor = image.ij_image.getProcessor()
 | 
				
			||||||
 | 
					        convert_to_byte = False
 | 
				
			||||||
 | 
					        if processor.getBitDepth() != 8:
 | 
				
			||||||
 | 
					            convert_to_byte = True
 | 
				
			||||||
 | 
					            min_value = processor.getMin()
 | 
				
			||||||
 | 
					            max_value = processor.getMax()
 | 
				
			||||||
 | 
					            print("warning: downgrading image to byte as imagej's Gray Morphology processing doesn't handle 16bit images (range=[%d, %d])" % (min_value, max_value))
 | 
				
			||||||
 | 
					            do_scaling = True
 | 
				
			||||||
 | 
					            image.ij_image.setProcessor(processor.convertToByte(do_scaling))
 | 
				
			||||||
 | 
					        print("before gray morphology")
 | 
				
			||||||
 | 
					        assert structuring_element_radius < 11, "the radius of the structuring element is too big (%d); using it with Fiji's 'Gray Morphology' tool would result in very long computations." % structuring_element_radius
 | 
				
			||||||
 | 
					        imagej_run_image_command(image.ij_image, "Gray Morphology", "radius=%d type=%s operator=%s" % (structuring_element_radius, structuring_element_shape, operator))
 | 
				
			||||||
 | 
					        print("after gray morphology")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def replace_border(self, image, band_width):
 | 
				
			||||||
 | 
					        raise NotImplementedError()
 | 
				
			||||||
 | 
					        return image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,74 +1,9 @@
 | 
				
			||||||
"""preprocessing of synchrotron images based on telemosToolbox."""
 | 
					"""preprocessing of synchrotron images based on telemosToolbox."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ij import IJ
 | 
					 | 
				
			||||||
from ij.plugin import ImageCalculator
 | 
					 | 
				
			||||||
from catalog import ImageCatalog, Sequence
 | 
					from catalog import ImageCatalog, Sequence
 | 
				
			||||||
from imageengine import IImageEngine
 | 
					from imageengine import IImageEngine
 | 
				
			||||||
from imagej.ijimageengine import IJImageEngine, IJImage
 | 
					from imagej.ijimageengine import IJImageEngine, IJImage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def imagej_run_image_command(image, command, options):
 | 
					 | 
				
			||||||
    """performs the given imagej command on the given image
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    :param ImagePlus image:
 | 
					 | 
				
			||||||
    :param str command: imagej command (eg "Gray Morphology")
 | 
					 | 
				
			||||||
    :param str options: imagej options (eg "radius=1 type=square operator=open")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    wrapper around IJ.run (https://imagej.nih.gov/ij/developer/api/ij/IJ.html#run-ij.ImagePlus-java.lang.String-java.lang.String-) which raises an exception on error
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    IJ.run(image, command, options)
 | 
					 | 
				
			||||||
    error_message = IJ.getErrorMessage()
 | 
					 | 
				
			||||||
    if error_message is not None:
 | 
					 | 
				
			||||||
        raise Exception('The command "%s" with options "%s" failed because of the following error : %s' % (command, options, error_message))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def compute_max(images_file_path):
 | 
					 | 
				
			||||||
    image_engine = IJImageEngine()
 | 
					 | 
				
			||||||
    return image_engine.compute_max(images_file_path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def replace_border(image, band_width):
 | 
					 | 
				
			||||||
    """Overwrites the outer band of the image by duplicating the value of the pixels that touch this outer band
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    :param ImagePlus image:
 | 
					 | 
				
			||||||
    :param int band_width: width of the outer band, in pixels
 | 
					 | 
				
			||||||
    :rtype ImagePlus:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    adaptation for the following code from matlab telemosToolbx's estimatedwhiteFluoImageTelemos function
 | 
					 | 
				
			||||||
        outer = white_estimate;
 | 
					 | 
				
			||||||
        white_estimate = white_estimate(4:(end-3),4:(end-3));
 | 
					 | 
				
			||||||
        outer(1:3,4:(end-3))=repmat(white_estimate(1,:),3,1);  # top
 | 
					 | 
				
			||||||
        outer((end-2):end,4:(end-3))=repmat(white_estimate(end,:),3,1); # bottom
 | 
					 | 
				
			||||||
        outer(:,1:3)=repmat(outer(:,4),1,3); # left
 | 
					 | 
				
			||||||
        outer(:,(end-2):end)=repmat(outer(:,end-3),1,3); # right
 | 
					 | 
				
			||||||
        white_estimate = outer;
 | 
					 | 
				
			||||||
        clear outer;
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    raise NotImplementedError()
 | 
					 | 
				
			||||||
    return image
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def perform_gray_morphology(image, operator, structuring_element_shape, structuring_element_radius):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    :param ImagePlus image:
 | 
					 | 
				
			||||||
    :param str operator: eg 'open'
 | 
					 | 
				
			||||||
    :param str structuring_element_shape: eg 'square'
 | 
					 | 
				
			||||||
    :param int structuring_element_radius:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    assert operator not in ['fast open', 'fast erode'], "as of 13/09/2019, fast operators such as 'fast erode' seem broken in fiji (the resulting image is not at all similar to their slow equivalent)"
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    processor = image.getProcessor()
 | 
					 | 
				
			||||||
    convert_to_byte = False
 | 
					 | 
				
			||||||
    if processor.getBitDepth() != 8:
 | 
					 | 
				
			||||||
        convert_to_byte = True
 | 
					 | 
				
			||||||
        min_value = processor.getMin()
 | 
					 | 
				
			||||||
        max_value = processor.getMax()
 | 
					 | 
				
			||||||
        print("warning: downgrading image to byte as imagej's Gray Morphology processing doesn't handle 16bit images (range=[%d, %d])" % (min_value, max_value))
 | 
					 | 
				
			||||||
        do_scaling = True
 | 
					 | 
				
			||||||
        image.setProcessor(processor.convertToByte(do_scaling))
 | 
					 | 
				
			||||||
    print("before gray morphology")
 | 
					 | 
				
			||||||
    assert structuring_element_radius < 11, "the radius of the structuring element is too big (%d); using it with Fiji's 'Gray Morphology' tool would result in very long computations." % structuring_element_radius
 | 
					 | 
				
			||||||
    imagej_run_image_command(image, "Gray Morphology", "radius=%d type=%s operator=%s" % (structuring_element_radius, structuring_element_shape, operator))
 | 
					 | 
				
			||||||
    print("after gray morphology")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WhiteEstimator(object):
 | 
					class WhiteEstimator(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,8 +17,8 @@ class WhiteEstimator(object):
 | 
				
			||||||
        self.average_size = average_size
 | 
					        self.average_size = average_size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _remove_particles(self, white_estimate):
 | 
					    def _remove_particles(self, white_estimate):
 | 
				
			||||||
        perform_gray_morphology(white_estimate, operator='open', structuring_element_shape='square', structuring_element_radius=(self.open_size + 1) / 2)
 | 
					        IImageEngine.get_instance().perform_gray_morphology(white_estimate, operator='open', structuring_element_shape='square', structuring_element_radius=(self.open_size + 1) / 2)
 | 
				
			||||||
        perform_gray_morphology(white_estimate, operator='close', structuring_element_shape='square', structuring_element_radius=(self.close_size + 1) / 2)
 | 
					        IImageEngine.get_instance().perform_gray_morphology(white_estimate, operator='close', structuring_element_shape='square', structuring_element_radius=(self.close_size + 1) / 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def estimate_white(self, sequences, channel_ids, dark=None):
 | 
					    def estimate_white(self, sequences, channel_ids, dark=None):
 | 
				
			||||||
        """Estimation of the white fluorescence image shape of synchrotron light from experimental images of Telemos microscope.
 | 
					        """Estimation of the white fluorescence image shape of synchrotron light from experimental images of Telemos microscope.
 | 
				
			||||||
| 
						 | 
					@ -190,11 +125,11 @@ class WhiteEstimator(object):
 | 
				
			||||||
                    for z_index in range(sequence.num_slices):
 | 
					                    for z_index in range(sequence.num_slices):
 | 
				
			||||||
                        images_file_path.append(sequence.get_image_file_path(channel_index, frame_index, z_index))
 | 
					                        images_file_path.append(sequence.get_image_file_path(channel_index, frame_index, z_index))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        white_estimate = compute_max(images_file_path)
 | 
					        white_estimate = IImageEngine.get_instance().compute_max(images_file_path)
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # modify spurious pixels on the side of the images
 | 
					        # modify spurious pixels on the side of the images
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            replace_border(white_estimate, 3)
 | 
					            IImageEngine.get_instance().replace_border(white_estimate, 3)
 | 
				
			||||||
        except NotImplementedError as error:
 | 
					        except NotImplementedError as error:
 | 
				
			||||||
            print('warning: replace_outer_frame is not implemented yet')
 | 
					            print('warning: replace_outer_frame is not implemented yet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -333,7 +268,7 @@ def test_preprocessing(raw_images_root_path):
 | 
				
			||||||
    white_estimate = white_estimator.estimate_white([sequence], ['DM300_327-353_fluo'])
 | 
					    white_estimate = white_estimator.estimate_white([sequence], ['DM300_327-353_fluo'])
 | 
				
			||||||
    # find_white_reference_image(white_estimate, sequence.get_white())
 | 
					    # find_white_reference_image(white_estimate, sequence.get_white())
 | 
				
			||||||
    print(white_estimate)
 | 
					    print(white_estimate)
 | 
				
			||||||
    IImageEngine.get_instance().save_as_tiff( IJImage( IImageEngine.get_instance(), white_estimate ), './white_estimate.tiff' )
 | 
					    IImageEngine.get_instance().save_as_tiff( white_estimate, './white_estimate.tiff' )
 | 
				
			||||||
    print('end')
 | 
					    print('end')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue