added a a method that estimates the global area of globules over time

This commit is contained in:
Guillaume Raffy 2020-03-18 16:33:14 +01:00
parent 57c594cf4d
commit 8f6d1188e5
5 changed files with 139 additions and 18 deletions

View File

@ -359,6 +359,36 @@ class IImageEngine(ABC):
:param str out_file_path: eg './white_estimate.tiff'
"""
@abc.abstractmethod
def subtract(self, image1, image2):
"""
computes the difference image1-image2
:param IImage image1:
:param IImage image2:
:rtype IImage: image1-image2
"""
@abc.abstractmethod
def divide(self, numerator_image, denominator_image):
"""
computes the division numerator_image/denominator_image
:param IImage numerator_image:
:param IImage denominator_image:
:rtype IImage: numerator_image/denominator_image
"""
@abc.abstractmethod
def abs(self, image):
"""
computes the absolute value of each pixel in the input image
:param IImage image:
:rtype IImage: an image containing the absolute values of the input image
"""
@abc.abstractmethod
def compute_edge_transform(self, image):
"""

View File

@ -261,11 +261,20 @@ class IJImageEngine(IImageEngine):
if not os.path.isfile(out_file_path):
assert False, 'failed to create %s' % out_file_path
def subtract(self, image1, image2):
ic = ImageCalculator()
result_image = ic.run("Subtract create float", image1.ij_image, image2.ij_image)
return IJImage(self, result_image)
def divide(self, numerator_image, denominator_image):
ic = ImageCalculator()
result_image = ic.run("Divide create float", numerator_image.ij_image, denominator_image.ij_image)
return IJImage(self, result_image)
def abs(self, image):
result_image = image.clone()
result_image.ij_image.getProcessor().abs()
return result_image
def compute_edge_transform(self, image):
result_image = image.clone()

View File

@ -13,9 +13,15 @@ from ij.plugin import ZProjector # pylint: disable=import-error
from ij import WindowManager # pylint: disable=import-error
from catalog import Sequence, ImageCatalog
import telemos
from imageengine import IImageProcessingDebugger, NullDebugger
from imageengine import IImageEngine, IImageProcessingDebugger, NullDebugger, StackImageFeeder, IHyperStack
# greeting = "Hello, " + name + "!"
import abc
# h5py is not available in fiji's jython
from ncsa.hdf.hdf5lib import HDFArray # found in FIJI_HOME/jars/jhdf5-14.12.6.jar
ABC = abc.ABCMeta('ABC', (object,), {})
class ImageLogger(IImageProcessingDebugger):
def __init__(self):
@ -111,23 +117,13 @@ class Lipase(object):
best_z_index = z_index
return best_z_index
def compute_sequence_median_image(self, sequence, channel_id='DM300_nofilter_vis'):
''' computes for each pixel its median value along time, so that in the end we have a single image that only shows structures (things that don't move such as the traps)
:param Sequence sequence:
:param str channel_id:
'''
stack = sequence.as_stack(channel_id)
projector = ZProjector()
median_image = projector.run(stack, 'median')
# pixel_value_along_time = ByteProcessor.createProcessor(stack.getDepth(), 1)
return median_image
def extract_background(self, sequence):
def estimate_background(self, sequence):
channel_id = 'DM300_nofilter_vis'
sequence.as_stack(channel_id).show()
background_image = self.compute_sequence_median_image(sequence, channel_id)
background_image.show()
# sequence.as_stack(channel_id).show()
visible_stack = sequence.as_hyperstack(selected_channel_ids=[channel_id])
visible_image_feeder = StackImageFeeder(visible_stack)
ie = IImageEngine.get_instance()
background_image = ie.compute_median(visible_image_feeder)
return background_image
def estimate_white(self, sequence, channel_id):
@ -142,6 +138,80 @@ class Lipase(object):
raise NotImplementedError()
class IBackgroundEstimator(ABC):
@abc.abstractmethod
def estimate_background(self, visible_traps_sequence):
"""
:param IHyperStack visible_traps_sequence:
:rtype IImage: the estimated background image
"""
pass
class EmptyFrameBackgroundEstimator(IBackgroundEstimator):
def __init__(self, empty_frame_index):
"""
:param int empty_frame_index: the index of the frame that is chosen as background (it is supposed to not contain any particle, only static objects (traps, lens spots, etc.))
"""
IBackgroundEstimator.__init__(self)
self.empty_frame_index = empty_frame_index
def estimate_background(self, visible_traps_sequence):
background_estimate = visible_traps_sequence.get_image(frame_index=self.empty_frame_index).clone()
return background_estimate
class GlobulesAreaEstimator(object):
""" An image processing suite targetted to visible images of traps with moving particles (globules)
"""
class FrameResult(object):
""" The results related to a frame
"""
def __init__(self, globules_area_ratio):
"""
:param int globules_area_ratio: area of globules in this frame, expressed as a ratio of the frame area
"""
self.globules_area_ratio = globules_area_ratio
class Results(object):
def __init__(self):
self.frames = {}
def __init__(self, background_estimator, particle_threshold):
"""
:param IBackgroundEstimator background_estimator: the method that is used to estimate the background image.
:param float particle_threshold: if the absolute difference between the pixel and its corresponding value in the background image exceeds this, value, then this pixel is considered as a part of a particle
"""
self.background_estimator = background_estimator
self.particle_threshold = particle_threshold
def detect_particles(self, visible_traps_sequence):
"""
:param IHyperStack visible_traps_sequence:
"""
background_image = self.background_estimator.estimate_background(visible_traps_sequence)
ie = IImageEngine.get_instance()
# particles_stack = ie.create_hyperstack(width=background_image.width, height=background_image.height, num_slices=1, num_frames=visible_traps_sequence.num_frames(), num_channels=1, pixel_type=PixelType.F32)
results = GlobulesAreaEstimator.Results()
frame_area = background_image.get_width() * background_image.get_height()
for frame_index in range(visible_traps_sequence.num_frames()):
particle_image = ie.subtract(visible_traps_sequence.get_image(frame_index=frame_index), background_image)
abs_particle_image = ie.abs( particle_image )
is_particle = ie.threshold(abs_particle_image, self.particle_threshold)
measured_mean_value = is_particle.get_mean_value()
particle_pixel_value = 255.0
num_pixels = is_particle.get_width() * is_particle.get_height()
num_particle_pixels = int(measured_mean_value * num_pixels / particle_pixel_value)
print("num_particle_pixels: %d " % num_particle_pixels)
globules_area_ratio = float(num_particle_pixels)/frame_area
results.frames[frame_index] = GlobulesAreaEstimator.FrameResult(globules_area_ratio=globules_area_ratio)
# particles_stack.set_image(frame_index=frame_index, image=particle_image)
def test_find_white():
raw_images_root = '/Users/graffy/ownCloud/ipr/lipase/raw-images'

View File

@ -70,7 +70,7 @@ class TrapsDetector(object):
the idea of this method is as follows:
1. allow the user to provide an axis-aligned bounding box that contains a trap, and use this area as a pattern to be searched in all images of the sequence. Provided the tolerance is big enough, this should find all traps in the sequence.
2. compute a clean (without globules) trap, by computing for each pixel, the median value from all traps for that pixel.
3. For each trap location, remove the trap by substracting the clean trap. This should leave the globules only.
3. For each trap location, remove the trap by subtracting the clean trap. This should leave the globules only.
At the end of this image processing, we expect to only have the globules in the image.
"""

View File

@ -15,6 +15,9 @@ from lipase.template_matcher import TemplateMatcher # pylint: disable=import-er
from lipase.traps_detector import TrapsDetector # pylint: disable=import-error
from lipase.catalog import ImageCatalog, Sequence # pylint: disable=import-error
from lipase.lipase import Lipase, ImageLogger # pylint: disable=import-error
from lipase.lipase import GlobulesAreaEstimator, EmptyFrameBackgroundEstimator # pylint: disable=import-error
class TestLipase(unittest.TestCase):
@ -92,10 +95,19 @@ class TestLipase(unittest.TestCase):
print("measured_mean_value: %f" % measured_mean_value)
self.assertAlmostEqual(measured_mean_value, expected_mean_value, delta=0.01)
def test_visible_traps_sequence_processing(self):
traps_sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0']
visible_traps_sequence = traps_sequence.as_hyperstack(['DM300_nofilter_vis'])
background_estimator = EmptyFrameBackgroundEstimator(empty_frame_index=39)
processor = GlobulesAreaEstimator(background_estimator=background_estimator, particle_threshold=2000.0)
processor.detect_particles(visible_traps_sequence)
# def test_lipase_process(self):
# lipase = Lipase(self.catalog, debugger=NullDebugger())
def run_script():
# unittest.main() # this would result in : ImportError: No module named __main__