started the abstraction of the image processing engine so that the lipase image processing can be executed without imagej if it's deemed to slow in the end.

This commit is contained in:
Guillaume Raffy 2019-09-16 15:53:27 +02:00
parent b7b1f81fa7
commit 7994afd01a
5 changed files with 131 additions and 34 deletions

61
src/imageengine.py Normal file
View File

@ -0,0 +1,61 @@
"""An image engine implements image processing operators and an abstract image format.
Using these abstract classes allows the user to write image processing code that can run with various different image engines (eg imagej or opencv).
"""
import abc
ABC = abc.ABCMeta('ABC', (object,), {})
class IImage(ABC):
@abc.abstractmethod
def width(self):
pass
@abc.abstractmethod
def height(self):
pass
class IImageEngine(ABC):
"""
The active image processor.
As there should only be one image engine instance at any given time, this class is implemented as a singleton
"""
# Here will be the instance stored.
__instance = None
@staticmethod
def set_instance(image_engine):
"""Change the active image_engine.
:param IImageEngine image_engine:
"""
IImageEngine.__instance = image_engine
@staticmethod
def get_instance():
"""Static access method."""
assert IImageEngine.__instance is not None
return IImageEngine.__instance
@abc.abstractmethod
def save_as_tiff(self, image, out_file_path):
"""
:param IImage image:
: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.
:param list(str) images_file_path:
:rtype IImage:
"""

0
src/imagej/__init__.py Normal file
View File

View File

@ -0,0 +1,49 @@
"""Image processing using imagej.
"""
from imageengine import IImage, IImageEngine
from ij import IJ
from ij.plugin import ImageCalculator
class IJImage(IImage):
def __init__(self, image_engine, ij_image):
"""
:param IJImageEngine image_engine:
:param ImagePlus ij_image:
"""
self.image_engine = image_engine
self.ij_image = ij_image
def width(self):
raise NotImplementedError
def height(self):
raise NotImplementedError
class IJImageEngine(IImageEngine):
def missing(self):
pass
def save_as_tiff(self, image, out_file_path):
IJ.saveAsTiff(image.ij_image, out_file_path)
def compute_max(self, images_file_path):
"""Computes for each pixel position the maximum at this position in all input images.
:param list(str) images_file_path:
:rtype IJmage:
"""
assert len(images_file_path) > 1
max_image = IJ.openImage(images_file_path[0])
print('max_image', max_image)
for image_file_path in images_file_path[2:-1]:
other_image = IJ.openImage(image_file_path)
print('other_image', other_image)
ic = ImageCalculator()
ic.run("max", max_image, other_image)
print('max_image', max_image)
return max_image

View File

@ -3,7 +3,8 @@
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
@ -21,23 +22,8 @@ def imagej_run_image_command(image, command, options):
def compute_max(images_file_path):
"""Computes for each pixel position the maximum at this position in all input images.
:param list(str) images_file_path:
:rtype ImagePlus:
"""
assert len(images_file_path) > 1
max_image = IJ.openImage(images_file_path[0])
print('max_image', max_image)
for image_file_path in images_file_path[2:-1]:
other_image = IJ.openImage(image_file_path)
print('other_image', other_image)
ic = ImageCalculator()
ic.run("max", max_image, other_image)
print('max_image', max_image)
return max_image
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
@ -87,6 +73,10 @@ def perform_gray_morphology(image, operator, structuring_element_shape, structur
class WhiteEstimator(object):
def __init__(self, open_size, close_size, average_size):
"""
:param int open_size: the diameter of the structuring element used to perform mathematical morphology's opening operator (in pixels)
:param int close_size: the diameter of the structuring element used to perform mathematical morphology's closing operator (in pixels)
"""
self.open_size = open_size
self.close_size = close_size
self.average_size = average_size
@ -238,8 +228,9 @@ class InteractiveWhiteEstimator(WhiteEstimator):
def find_white_reference_image(white_estimate, white_z_stack):
"""Find the white reference Image among a z stack of reference image the most correlated to the white image estimated from a serie of acquisition.
:param Image white_estimate:
:param IImage white_estimate:
:param Sequence white_z_stack:
:param IImageEngine image_engine: the image processor to use
% this function is specific of Telemos images (DISCO line SOLEIL)
% acquired using micromanager software linked to imageJ
@ -333,14 +324,17 @@ def find_white_reference_image(white_estimate, white_z_stack):
raise NotImplementedError()
def test_preprocessing():
def test_preprocessing(raw_images_root_path):
"""Test preprocessing."""
catalog = ImageCatalog('/Users/graffy/ownCloud/ipr/lipase/raw-images')
IImageEngine.set_instance(IJImageEngine())
catalog = ImageCatalog(raw_images_root_path)
sequence = catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2']
white_estimator = WhiteEstimator(open_size=75, close_size=75, average_size=75)
white_estimator = WhiteEstimator(open_size=3, close_size=3, average_size=3)
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)
IImageEngine.get_instance().save_as_tiff( IJImage( IImageEngine.get_instance(), white_estimate ), './white_estimate.tiff' )
print('end')
if __name__ == '__main__':
test_preprocessing()

View File

@ -14,20 +14,13 @@ print('python version %s' % sys.version) # prints python version
sys.path.append(lipase_src_root_path) # necessary if run from fiji as script otherwise jython fails to find lipase's modules such as catalog
from ij import IJ
from lipase import Lipase, ImageLogger
# from lipase import Lipase, ImageLogger
from catalog import ImageCatalog
from preprocessing import WhiteEstimator
from preprocessing import test_preprocessing
def run_script():
catalog = ImageCatalog(raw_images_root_path) # eg '/Users/graffy/ownCloud/ipr/lipase/raw-images'
lipase = Lipase(catalog, debugger=ImageLogger())
sequence = catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2']
# white_estimator = WhiteEstimator(open_size=75, close_size=75, average_size=75)
white_estimator = WhiteEstimator(open_size=3, close_size=3, average_size=3)
white_estimate = white_estimator.estimate_white([sequence], ['DM300_327-353_fluo'])
print(white_estimate)
IJ.saveAsTiff(white_estimate, './white_estimate.tiff')
print('end')
test_preprocessing(raw_images_root_path) # eg '/Users/graffy/ownCloud/ipr/lipase/raw-images'
# note : when launched from fiji, __name__ doesn't have the value "__main__", as when launched from python
run_script()