compute_traps_mask now returns a traps mask, as expected
- to do this I had to split test_template_matcher into 2 unit tests (one of them being a unit test just for template matching)
This commit is contained in:
parent
9ce4823e7a
commit
777db445be
|
@ -132,6 +132,8 @@ class Sequence(object):
|
||||||
""" returns a subset of the sequence as an hyperstack
|
""" returns a subset of the sequence as an hyperstack
|
||||||
|
|
||||||
:param list(str) selected_channel_ids:
|
:param list(str) selected_channel_ids:
|
||||||
|
:param list(int) selected_frames:
|
||||||
|
:param list(int) selected_slices:
|
||||||
:return IHyperStack: the resulting hyperstack
|
:return IHyperStack: the resulting hyperstack
|
||||||
"""
|
"""
|
||||||
if selected_frames is None:
|
if selected_frames is None:
|
||||||
|
|
|
@ -40,7 +40,10 @@ class PixelType:
|
||||||
class IImage(ABC):
|
class IImage(ABC):
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def clone(self):
|
def clone(self, clone_pixel_type=None):
|
||||||
|
"""
|
||||||
|
:param int clone_pixel_type: PixelType enum. If None, then the clone's pixel type is the same as the pixel type of the source image
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
|
@ -80,6 +83,12 @@ class IImage(ABC):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_mean_value(self):
|
||||||
|
"""
|
||||||
|
:rtype float: the mean pixel value in the image
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
class IHyperStack(ABC):
|
class IHyperStack(ABC):
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,18 @@ class IJImage(IImage):
|
||||||
stack_name = ''
|
stack_name = ''
|
||||||
self.ij_image = IJ.createHyperStack(stack_name, width, height, 1, 1, 1, PixelType.get_num_bits(pixel_type))
|
self.ij_image = IJ.createHyperStack(stack_name, width, height, 1, 1, 1, PixelType.get_num_bits(pixel_type))
|
||||||
|
|
||||||
def clone(self):
|
def clone(self, clone_pixel_type=None):
|
||||||
copy = IJImage(self.image_engine, width=self.width(), height=self.height(), pixel_type=self.get_pixel_type())
|
if clone_pixel_type == None:
|
||||||
copy.ij_image.setProcessor( self.ij_image.getProcessor().duplicate() )
|
clone_pixel_type = self.get_pixel_type()
|
||||||
|
copy = IJImage(self.image_engine, width=self.width(), height=self.height(), pixel_type=clone_pixel_type)
|
||||||
|
src_processor = self.ij_image.getProcessor()
|
||||||
|
clone_processor = {
|
||||||
|
PixelType.U8: src_processor.convertToByteProcessor(),
|
||||||
|
PixelType.U16: src_processor.convertToShortProcessor(),
|
||||||
|
PixelType.F32: src_processor.convertToFloatProcessor(),
|
||||||
|
}[clone_pixel_type]
|
||||||
|
copy.ij_image.setProcessor( clone_processor )
|
||||||
|
assert copy.get_pixel_type() == clone_pixel_type
|
||||||
return copy
|
return copy
|
||||||
|
|
||||||
def width(self):
|
def width(self):
|
||||||
|
@ -78,11 +87,15 @@ class IJImage(IImage):
|
||||||
processor = self.ij_image.getProcessor()
|
processor = self.ij_image.getProcessor()
|
||||||
# processor.getMin() and processor.getMax() give a higher min and a lower max, probably because they only consider a subpart of the image (this is badly documented)
|
# processor.getMin() and processor.getMax() give a higher min and a lower max, probably because they only consider a subpart of the image (this is badly documented)
|
||||||
# so we use processor.getStats instead
|
# so we use processor.getStats instead
|
||||||
image_stats = processor.getStats() # :type ImageStatistics is:
|
image_stats = processor.getStats() # :type ImageStatistics image_stats:
|
||||||
min_value = image_stats.min
|
min_value = image_stats.min
|
||||||
max_value = image_stats.max
|
max_value = image_stats.max
|
||||||
return (min_value, max_value)
|
return (min_value, max_value)
|
||||||
|
|
||||||
|
def get_mean_value(self):
|
||||||
|
processor = self.ij_image.getProcessor()
|
||||||
|
image_stats = processor.getStats() # :type ImageStatistics image_stats:
|
||||||
|
return image_stats.mean
|
||||||
|
|
||||||
class IJHyperStack(IHyperStack):
|
class IJHyperStack(IHyperStack):
|
||||||
|
|
||||||
|
@ -303,7 +316,13 @@ class IJImageEngine(IImageEngine):
|
||||||
return image # pylint: disable=unreachable
|
return image # pylint: disable=unreachable
|
||||||
|
|
||||||
def match_template(self, src_image, template_image):
|
def match_template(self, src_image, template_image):
|
||||||
|
cv_match_template_supported_pixel_types = [PixelType.U8, PixelType.F32]
|
||||||
|
if src_image.get_pixel_type() not in cv_match_template_supported_pixel_types:
|
||||||
|
print('converting src_image')
|
||||||
|
src_image = src_image.clone(PixelType.F32)
|
||||||
|
if template_image.get_pixel_type() not in cv_match_template_supported_pixel_types:
|
||||||
|
print('converting template_image')
|
||||||
|
template_image = template_image.clone(PixelType.F32)
|
||||||
# import org.opencv.imgproc.Imgproc;
|
# import org.opencv.imgproc.Imgproc;
|
||||||
#
|
#
|
||||||
# /*
|
# /*
|
||||||
|
|
|
@ -116,5 +116,5 @@ class TrapsDetector(object):
|
||||||
#non_uniform_stack = sequence.as_stack()
|
#non_uniform_stack = sequence.as_stack()
|
||||||
#uniform_stack = IImageEngine.get_instance().divide(non_uniform_stack, white_estimate)
|
#uniform_stack = IImageEngine.get_instance().divide(non_uniform_stack, white_estimate)
|
||||||
# IImageEngine.get_instance().save_as_tiff(white_estimate, './white_estimate.tiff')
|
# IImageEngine.get_instance().save_as_tiff(white_estimate, './white_estimate.tiff')
|
||||||
return matches
|
return traps_mask
|
||||||
|
|
|
@ -45,6 +45,30 @@ class TestLipase(unittest.TestCase):
|
||||||
non_uniform_sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0']
|
non_uniform_sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0']
|
||||||
uniform_sequence = correct_non_uniform_lighting(non_uniform_sequence, 'DM300_nofilter_vis', white_estimator=WhiteEstimator(open_size=75, close_size=75, average_size=75))
|
uniform_sequence = correct_non_uniform_lighting(non_uniform_sequence, 'DM300_nofilter_vis', white_estimator=WhiteEstimator(open_size=75, close_size=75, average_size=75))
|
||||||
|
|
||||||
|
def test_template_matcher(self):
|
||||||
|
sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0']
|
||||||
|
stack = sequence.as_hyperstack(['DM300_nofilter_vis'], selected_frames=[0])
|
||||||
|
first_image = stack.get_image(frame_index=0)
|
||||||
|
x_min = 423
|
||||||
|
x_max = 553
|
||||||
|
y_min = 419
|
||||||
|
y_max = 533
|
||||||
|
template_trap_aabb = Aabb(x_min, y_min, x_max, y_max)
|
||||||
|
template_trap_image = first_image.get_subimage(template_trap_aabb)
|
||||||
|
for image in [first_image, template_trap_image]:
|
||||||
|
print(image.get_pixel_type(), image.width(), image.height())
|
||||||
|
# the typical value of peaks is -2.e10 and the value between peaks is below -8.0e10
|
||||||
|
threshold = -3.0e10
|
||||||
|
tolerance = 1.0e10
|
||||||
|
maxima_finder = MaximaFinder(threshold, tolerance)
|
||||||
|
template_matcher = TemplateMatcher(maxima_finder)
|
||||||
|
matches = template_matcher.match_template(first_image, template_trap_image)
|
||||||
|
num_traps = len(matches)
|
||||||
|
print("number of traps found : %d" % num_traps)
|
||||||
|
num_expected_traps = 13 # 13 traps are completely visible in the first image
|
||||||
|
self.assertAlmostEqual(len(matches), num_expected_traps, delta=1.0)
|
||||||
|
|
||||||
|
|
||||||
def test_traps_detector(self):
|
def test_traps_detector(self):
|
||||||
# the typical value of peaks is -500 and the value between peaks is below -2500
|
# the typical value of peaks is -500 and the value between peaks is below -2500
|
||||||
threshold = -1500.0
|
threshold = -1500.0
|
||||||
|
@ -58,12 +82,14 @@ class TestLipase(unittest.TestCase):
|
||||||
y_min = 419
|
y_min = 419
|
||||||
y_max = 533
|
y_max = 533
|
||||||
trap_aabb = Aabb(x_min, y_min, x_max, y_max)
|
trap_aabb = Aabb(x_min, y_min, x_max, y_max)
|
||||||
matches = traps_detector.compute_traps_mask(sequence, 'DM300_nofilter_vis', trap_aabb)
|
traps_mask = traps_detector.compute_traps_mask(sequence, 'DM300_nofilter_vis', trap_aabb)
|
||||||
|
measured_mean_value = traps_mask.get_mean_value()
|
||||||
num_traps = len(matches)
|
expected_traps_coverage = 0.07909
|
||||||
print("number of traps found : %d" % num_traps)
|
traps_pixel_value = 255.0
|
||||||
num_expected_traps = 13 # 13 traps are completely visible in the
|
expected_mean_value = expected_traps_coverage * traps_pixel_value
|
||||||
self.assertAlmostEqual(len(matches), num_expected_traps, delta=1.0)
|
print("expected_mean_value: %f" % expected_mean_value)
|
||||||
|
print("measured_mean_value: %f" % measured_mean_value)
|
||||||
|
self.assertAlmostEqual(measured_mean_value, expected_mean_value, delta=0.01)
|
||||||
|
|
||||||
def run_script():
|
def run_script():
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue