added a a method that estimates the global area of globules over time
This commit is contained in:
parent
57c594cf4d
commit
8f6d1188e5
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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__
|
||||
|
|
Loading…
Reference in New Issue