finished abstracting image processing engine

all imagej related code has now been moved to ijimageengine.py
This commit is contained in:
Guillaume Raffy 2019-09-16 16:31:33 +02:00
parent 7994afd01a
commit 74d7f9e603
3 changed files with 92 additions and 74 deletions

View File

@ -39,7 +39,10 @@ class IImageEngine(ABC):
@staticmethod
def get_instance():
"""Static access method."""
"""Static access method.
:rtype IImageEngine:
"""
assert IImageEngine.__instance is not None
return IImageEngine.__instance
@ -50,7 +53,6 @@ class IImageEngine(ABC):
:param str out_file_path: eg './white_estimate.tiff'
"""
@abc.abstractmethod
def compute_max(self, images_file_path):
"""Compute for each pixel position the maximum at this position in all input images.
@ -59,3 +61,36 @@ class IImageEngine(ABC):
: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

View File

@ -22,6 +22,23 @@ class IJImage(IImage):
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):
def missing(self):
@ -34,7 +51,7 @@ class IJImageEngine(IImageEngine):
"""Computes for each pixel position the maximum at this position in all input images.
:param list(str) images_file_path:
:rtype IJmage:
:rtype IJImage:
"""
assert len(images_file_path) > 1
max_image = IJ.openImage(images_file_path[0])
@ -46,4 +63,35 @@ class IJImageEngine(IImageEngine):
ic = ImageCalculator()
ic.run("max", max_image, other_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

View File

@ -1,74 +1,9 @@
"""preprocessing of synchrotron images based on telemosToolbox."""
from ij import IJ
from ij.plugin import ImageCalculator
from catalog import ImageCatalog, Sequence
from imageengine import IImageEngine
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):
@ -82,8 +17,8 @@ class WhiteEstimator(object):
self.average_size = average_size
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)
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='open', structuring_element_shape='square', structuring_element_radius=(self.open_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):
"""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):
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
try:
replace_border(white_estimate, 3)
IImageEngine.get_instance().replace_border(white_estimate, 3)
except NotImplementedError as error:
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'])
# find_white_reference_image(white_estimate, sequence.get_white())
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')
if __name__ == '__main__':