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:
Guillaume Raffy 2020-02-06 15:30:36 +01:00
parent 9ce4823e7a
commit 777db445be
5 changed files with 69 additions and 13 deletions

View File

@ -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:

View File

@ -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):

View File

@ -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;
# #
# /* # /*

View File

@ -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

View File

@ -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():