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
:param list(str) selected_channel_ids:
:param list(int) selected_frames:
:param list(int) selected_slices:
:return IHyperStack: the resulting hyperstack
"""
if selected_frames is None:

View File

@ -40,7 +40,10 @@ class PixelType:
class IImage(ABC):
@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
@abc.abstractmethod
@ -80,6 +83,12 @@ class IImage(ABC):
"""
pass
@abc.abstractmethod
def get_mean_value(self):
"""
:rtype float: the mean pixel value in the image
"""
pass
class IHyperStack(ABC):

View File

@ -43,9 +43,18 @@ class IJImage(IImage):
stack_name = ''
self.ij_image = IJ.createHyperStack(stack_name, width, height, 1, 1, 1, PixelType.get_num_bits(pixel_type))
def clone(self):
copy = IJImage(self.image_engine, width=self.width(), height=self.height(), pixel_type=self.get_pixel_type())
copy.ij_image.setProcessor( self.ij_image.getProcessor().duplicate() )
def clone(self, clone_pixel_type=None):
if clone_pixel_type == None:
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
def width(self):
@ -78,11 +87,15 @@ class IJImage(IImage):
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)
# 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
max_value = image_stats.max
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):
@ -303,7 +316,13 @@ class IJImageEngine(IImageEngine):
return image # pylint: disable=unreachable
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;
#
# /*

View File

@ -116,5 +116,5 @@ class TrapsDetector(object):
#non_uniform_stack = sequence.as_stack()
#uniform_stack = IImageEngine.get_instance().divide(non_uniform_stack, white_estimate)
# 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']
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):
# the typical value of peaks is -500 and the value between peaks is below -2500
threshold = -1500.0
@ -58,12 +82,14 @@ class TestLipase(unittest.TestCase):
y_min = 419
y_max = 533
trap_aabb = Aabb(x_min, y_min, x_max, y_max)
matches = traps_detector.compute_traps_mask(sequence, 'DM300_nofilter_vis', trap_aabb)
num_traps = len(matches)
print("number of traps found : %d" % num_traps)
num_expected_traps = 13 # 13 traps are completely visible in the
self.assertAlmostEqual(len(matches), num_expected_traps, delta=1.0)
traps_mask = traps_detector.compute_traps_mask(sequence, 'DM300_nofilter_vis', trap_aabb)
measured_mean_value = traps_mask.get_mean_value()
expected_traps_coverage = 0.07909
traps_pixel_value = 255.0
expected_mean_value = expected_traps_coverage * traps_pixel_value
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():