From 4daf2bfe91a69bfa702c111532c0f1fbdadc4bc2 Mon Sep 17 00:00:00 2001 From: Guillaume Raffy Date: Mon, 6 Apr 2020 16:35:28 +0200 Subject: [PATCH] now there is an image processing to compute the radial profile (needed for graffy/lipase#3). --- src/lipase/circsymdetector.py | 123 +++++++++++++++++++++++++ src/lipase/imageengine.py | 20 ++++ src/lipase/imagej/ijimageengine.py | 44 +++++++++ src/lipase/localprojector.py | 143 ----------------------------- tests/improc_tests.py | 2 +- tests/test0001.py | 139 +++++++++++++++------------- 6 files changed, 262 insertions(+), 209 deletions(-) create mode 100644 src/lipase/circsymdetector.py delete mode 100644 src/lipase/localprojector.py diff --git a/src/lipase/circsymdetector.py b/src/lipase/circsymdetector.py new file mode 100644 index 0000000..ab34ed4 --- /dev/null +++ b/src/lipase/circsymdetector.py @@ -0,0 +1,123 @@ +""" + an image processing technique to produce a 1D signal for each pixel : this 1D signal is obtained by projecting the neighborhood of this pixel on a set of bases (each base is used as a convilution kernel) + https://subversion.ipr.univ-rennes1.fr/repos/main/projects/antipode/src/python/antipode/texori.py +""" +from imageengine import IImageEngine, PixelType + +class IProjectorBase(object): + """ + a set of projectors + + a projector is a grey level image, in which each pixel value is acting as a weight + """ + def __init__(self): + pass + + def get_num_projectors(self): + pass + + def get_projector_kernel(self, projector_index): + + pass + +def create_circle_image(image_size, circle_radius, circle_pos, circle_thickness, background_value=0.0, circle_value=1.0): + """ + :param dict image_size: + """ + ie = IImageEngine.get_instance() + width = image_size['width'] + height = image_size['height'] + assert isinstance(width, int) + assert isinstance(height, int) + circle_pos_x = float(circle_pos['x']) + circle_pos_y = float(circle_pos['y']) + r_min = max(0.0, circle_radius - circle_thickness * 0.5) + r_max = circle_radius + circle_thickness * 0.5 + r_min_square = r_min * r_min + r_max_square = r_max * r_max + image = ie.create_image(width=width, height=height, pixel_type=PixelType.F32) + for y in range(height): + for x in range(width): + dx = x - circle_pos_x + dy = y - circle_pos_y + r_square = (dx * dx + dy * dy) + if r_min_square < r_square < r_max_square: + pixel_value = circle_value + else: + pixel_value = background_value + image.set_pixel(x, y, pixel_value) + return image + +class CircularSymmetryProjectorBase(IProjectorBase): + """ + generates a base of circles + """ + + def __init__(self, max_radius, oversampling_scale=2): + """ + :param int max_radius: the biggest circle radius in the set of projectors + :param int oversampling_scale: oversampling is used to generate antialased circles. The higher this value, the better the quality of antialiasing is + """ + super(CircularSymmetryProjectorBase, self).__init__() + assert max_radius > 0 + assert oversampling_scale > 0 + self.max_radius = max_radius + self.oversampling_scale = oversampling_scale + self.num_projectors = int(max_radius) + 1 + + def get_num_projectors(self): + return self.num_projectors + + def get_projector_kernel(self, projector_index): + assert projector_index < self.get_num_projectors() + oversampling_is_handled = False # TODO: handle oversampling to increase quality + if not oversampling_is_handled and self.oversampling_scale != 1 : + raise NotImplementedError("at the moment, oversampling is not yet implemented") + radius = projector_index + image_size = int(self.max_radius) * 2 + 1 + circle_pos = {'x': self.max_radius, 'y': self.max_radius} + oversampled_circle = create_circle_image( + image_size={'width': image_size * self.oversampling_scale, 'height': image_size*self.oversampling_scale}, + circle_radius=radius * self.oversampling_scale, + circle_pos={'x': self.max_radius* self.oversampling_scale, 'y': self.max_radius* self.oversampling_scale}, + circle_thickness=1 * self.oversampling_scale) + if self.oversampling_scale == 1: + circle_image = oversampled_circle + else: + if oversampling_is_handled: + circle_image = oversampled_circle.resample(width=image_size, height=image_size) + # mean_value = circle_image.get_mean_value() + # num_pixels = image_size * image_size + # sum_of_pixel_values = mean_value * num_pixels + # we want each circle to have a total weight of 1.0, regardless their radius + # circle_image.scale_values(1.0/sum_of_pixel_values) + + anchor_point = {'x': int(circle_pos['x']), 'y': int(circle_pos['y'])} + return circle_image, anchor_point + + +class CircularSymmetryDetector: + def __init__(self, max_radius): + """ + :para float max_radius: + """ + self.max_radius = max_radius + + def compute_radial_profiles(self, src_image): + """ Computes for each pixel the radial profile (with this pixel as center) + + :param IImage src_image: + :rtype IHyperstack: each radial profile is stored in the channel coordinate of the hyperstack + """ + ie = IImageEngine.get_instance() + ie.debugger.on_image(src_image, 'src_image') + projector_base = CircularSymmetryProjectorBase(self.max_radius, oversampling_scale=1) + radial_profile_image = ie.create_hyperstack(width=src_image.get_width(), height=src_image.get_height(), num_channels=projector_base.get_num_projectors(), num_slices=1, num_frames=1, pixel_type=PixelType.F32) + for projector_index in range(projector_base.get_num_projectors()): + projector, center_of_filter = projector_base.get_projector_kernel(projector_index) + ie.debugger.on_image(projector, 'projector_%d' % (projector_index)) + print(type(center_of_filter)) + projection = ie.filter2D(src_image, dst_type=PixelType.F32, kernel=projector, anchor=(center_of_filter['x'], center_of_filter['y'])) + ie.debugger.on_image(projection, 'projection_%d' % (projector_index)) + radial_profile_image.set_image(self, projection, frame_index=0, slice_index=0, channel_index=projector_index) + return radial_profile_image diff --git a/src/lipase/imageengine.py b/src/lipase/imageengine.py index 83f814b..5d05e62 100644 --- a/src/lipase/imageengine.py +++ b/src/lipase/imageengine.py @@ -97,6 +97,14 @@ class IImage(ABC): """ pass + @abc.abstractmethod + def scale_values(self, src_image, scale): + """ + multiply the value of each pixel by a the given scalar + + :param float scale: + """ + class IHyperStack(ABC): @abc.abstractmethod @@ -426,6 +434,17 @@ class IImageEngine(ABC): :param int radius: in pixels """ + @abc.abstractmethod + def filter2D(self, src_image, dst_type, kernel, anchor=(-1, -1)): + """ as cv2.filter2D + + :param IImage src_image: + :param PixelType or None dst_type: pixel type of the destination image (if None, the pixel type is the same as the source image type) + :param IImage kernel: convolution kernel (or rather a correlation kernel), a single-channel floating point matrix; if you want to apply different kernels to different channels, split the image into separate color planes using split() and process them individually. + :param tuple(int, int) angor: anchor of the kernel that indicates the relative position of a filtered point within the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor is at the kernel center. + :rtype IImage: + """ + @abc.abstractmethod def perform_gray_morphology(self, image, operator, structuring_element_shape, structuring_element_radius): """ @@ -515,3 +534,4 @@ class IImageEngine(ABC): :param IImage src_binary_image: :rtype IImage: binary image """ + diff --git a/src/lipase/imagej/ijimageengine.py b/src/lipase/imagej/ijimageengine.py index eb58a50..2d10acc 100644 --- a/src/lipase/imagej/ijimageengine.py +++ b/src/lipase/imagej/ijimageengine.py @@ -24,6 +24,12 @@ IJ_PIXELTYPE_TO_PIXEL_TYPE = { ImagePlus.GRAY32: PixelType.F32 } +PIXEL_TYPE_TO_CV_PIXEL_TYPE = { + PixelType.U8: opencv_core.CV_8U, + PixelType.U16: opencv_core.CV_16U, + PixelType.F32: opencv_core.CV_32F, +} + class IJImage(IImage): def __init__(self, image_engine, ij_image=None, width=None, height=None, pixel_type=None): @@ -101,6 +107,10 @@ class IJImage(IImage): image_stats = processor.getStats() # :type ImageStatistics image_stats: return image_stats.mean + def scale_values(self, scale): + raise NotImplementedError() + + class IJHyperStack(IHyperStack): def __init__(self, image_engine, width, height, num_channels, num_slices, num_frames, pixel_type): @@ -231,6 +241,7 @@ def perform_gray_morphology_with_ijopencv(image, operator, structuring_element_s mat2imp = MatImagePlusConverter() image.ij_image.setProcessor(mat2imp.toImageProcessor(cv_dst_image)) + class ImageLogger(IImageProcessingDebugger): def __init__(self): @@ -327,6 +338,39 @@ class IJImageEngine(IImageEngine): """Implement interface method.""" IJ.run(image.ij_image, "Mean...", "radius=%d" % radius) + def filter2D(self, src_image, dst_type, kernel, anchor=(-1, -1)): + print(type(anchor), anchor) + if anchor == (-1, -1): + assert kernel.get_width() % 2 == 1 and kernel.get_height() % 2 == 1, "kernel sizes are expected to be odd if you want the anchor to be at the center of the kernel" + + anchor = ((kernel.get_width() - 1) / 2, (kernel.get_height() - 1) / 2) + + imp2mat = ImagePlusMatConverter() + cv_src_image = imp2mat.toMat(src_image.ij_image.getProcessor()) + cv_kernel = imp2mat.toMat(kernel.ij_image.getProcessor()) + + cv_anchor = opencv_core.Point(anchor[0], anchor[1]) + ie = IImageEngine.get_instance() + dst_image = ie.create_image(width=src_image.get_width(), height=src_image.get_height(), pixel_type=PixelType.F32) + cv_dst_image = opencv_core.Mat(cv_src_image.size(), cv_src_image.type()) + + # warning ! trying to guess the arguments from opencv's documentation is time consuming as the error messages are misleading (in the following call, javacpp complains that the 1st argument is not a org.bytedeco.javacpp.opencv_core$Mat, while it is ! The problem comes from the order of the arguments). So, instead of guessing, the found accepted signatures of filter2D are in https://github.com/bytedeco/javacpp-presets/blob/master/opencv/src/gen/java/org/bytedeco/opencv/global/opencv_imgproc.java + # opencv_imgproc.morphologyEx(cv_src_image, opencv_imgproc.MORPH_OPEN, struct_element, dst_image) + # TypeError: morphologyEx(): 1st arg can't be coerced to org.bytedeco.javacpp.opencv_core$GpuMat, org.bytedeco.javacpp.opencv_core$Mat, org.bytedeco.javacpp.opencv_core$UMat + print('before opencv_imgproc.filter2D') + delta = 0 + border_type = opencv_core.BORDER_DEFAULT + if dst_type is None: + ddepth = -1 + else: + ddepth = PIXEL_TYPE_TO_CV_PIXEL_TYPE[dst_type] + opencv_imgproc.filter2D(cv_src_image, cv_dst_image, ddepth, cv_kernel, cv_anchor, delta, border_type) + print('after opencv_imgproc.filter2D') + + mat2imp = MatImagePlusConverter() + dst_image.ij_image.setProcessor(mat2imp.toImageProcessor(cv_dst_image)) + return dst_image + def perform_gray_morphology(self, image, operator, structuring_element_shape, structuring_element_radius): """ :param IJImage image: diff --git a/src/lipase/localprojector.py b/src/lipase/localprojector.py deleted file mode 100644 index 90eb4da..0000000 --- a/src/lipase/localprojector.py +++ /dev/null @@ -1,143 +0,0 @@ -""" - an image processing technique to produce a 1D signal for each pixel : this 1D signal is obtained by projecting the neighborhood of this pixel on a set of bases (each base is used as a convilution kernel) - https://subversion.ipr.univ-rennes1.fr/repos/main/projects/antipode/src/python/antipode/texori.py -""" -from imageengine import IImageEngine, PixelType - -class IProjectorBase(object): - """ - a set of projectors - - a projector is a grey level image, in which each pixel value is acting as a weight - """ - def __init__(self): - pass - - def get_num_projectors(self): - pass - - def get_projector_kernel(self, projector_index): - - pass - -def create_circle_image(image_size, circle_radius, circle_pos, circle_thickness, background_value=0.0, circle_value=1.0): - """ - :param dict image_size: - """ - ie = IImageEngine.get_instance() - width = image_size['width'] - height = image_size['height'] - circle_pos_x = float(circle_pos['x']) - circle_pos_y = float(circle_pos['y']) - r_min = circle_radius - circle_thickness * 0.5 - assert r_min >= 0.0 - r_max = circle_radius + circle_thickness * 0.5 - r_min_square = r_min * r_min - r_max_square = r_max * r_max - image = ie.create_image(width=width, height=height, pixel_type=PixelType.F32) - for y in range(height): - for x in range(width): - dx = x - circle_pos_x - dy = y - circle_pos_y - r_square = (dx * dx + dy * dy) - if r_min_square < r_square < r_max_square: - pixel_value = circle_value - else: - pixel_value = background_value - image.set_pixel(x, y, pixel_value) - return image - -class CircularSymetryProjectorBase(IProjectorBase): - """ - generates a base of circles - """ - - def __init__(self, max_radius, oversampling_scale=2): - """ - :param int max_radius: the biggest circle radius in the set of projectors - :param int oversampling_scale: oversampling is used to generate antialased circles. The higher this value, the better the quality of antialiasing is - """ - super().__init__() - assert max_radius > 0 - assert oversampling_scale > 0 - self.max_radius = max_radius - self.oversampling_scale = oversampling_scale - self.num_projectors = max_radius + 1 - - def get_num_projectors(self): - return self.num_projectors - - def get_projector_kernel(self, projector_index): - assert projector_index < self.get_num_projectors() - radius = projector_index - image_size = self.max_radius * 2 + 1 - circle_pos = {'x': self.max_radius, 'y': self.max_radius} - oversampled_circle = create_circle_image( - image_size={'width':image_size * self.oversampling_scale, 'height':image_size*self.oversampling_scale}, - circle_radius=radius * self.oversampling_scale, - circle_pos={'x': self.max_radius* self.oversampling_scale, 'y': self.max_radius* self.oversampling_scale}, - circle_thickness=1 * self.oversampling_scale) - circle_image = oversampled_circle.resample(width=image_size, height=image_size) - anchor_point = circle_pos - return circle_image, anchor_point - - -# class LocalProjector: -# """ This image processor computes the probability for each pixel to have a material oriented in the given direction - -# The technique is based on the notion of a projector. The role of the projector is to project (or cumulate) the 2D neighborhood of each pixel on a projection axis that passes through the pixel and that has an orientation chosen by the user. - -# """ - - -# def __init__(self, searched_orientation, image_process_listener=imageprocessing.NullImageProcessListener()): -# imageprocessing.IImageProcessor.__init__(self, image_process_listener) -# self.searched_orientation = searched_orientation -# self.orientation_amount_image = None - -# @staticmethod -# def compute_noise_in_perpendicular_orientation(src_image, orientation, half_window_width=5, image_process_listener=imageprocessing.NullImageProcessListener()): -# """ Computes an image that gives for each pixel, the amount of noise in a direction perpendicular to the given orientation - -# Because the projection has to be performed for each pixel, we encode the projector operator in the form of a set of num_projectors 2D kernels that are used as a filter. Each kernel cumulates the image signal (therefore computes the sum of the image signal) along a line perpendicular to the projection axis, but each at a different position (offset) on this projection axis. - -# :param src_image: the input image, the one we want to process -# :type src_image: numpy.array - -# :param orientation: the orientation along which the noise needs to be computed -# :type orientation: float (in radians) - -# :param half_window_width: half size of the neighborhood window, in pixels (the heighborood window is a window of size (2*half_window_width+1, 2*half_window_width+1) centered on each pixel). The bigger, the more precise the measurement but with less locality. -# """ -# projector_base = OrientationProbabilityEstimator.ProjectorBase2(orientation, searched_segment_size=10.0, cut_blur_stddev=3.0, image_process_listener=image_process_listener) -# projections = numpy.empty((src_image.shape[0], src_image.shape[1], projector_base.get_num_projectors()), dtype=numpy.float32) -# # print('projections.shape=%s' % str(projections.shape)) -# for projector_index in range(projector_base.get_num_projectors()): -# projector, center_of_filter = projector_base.get_projector_kernel(projector_index) - -# projection = cv2.filter2D(src_image.astype(numpy.float32), ddepth=-1, kernel=projector, anchor=tuple(center_of_filter)) -# image_process_listener.onImage(projection, 'projection_%f_%d' % (orientation, projector_index)) -# projections[:, :, projector_index] = projection -# # for each pixel, compute the gradient along its projection axis -# # swap axes because I don't think the filter2D only works along fist 2 axes -# projections = numpy.swapaxes(projections, 1, 2) -# grad_kernel = numpy.array((-0.5, 0.5), dtype=numpy.float32) -# gradients = cv2.filter2D(projections, ddepth=-1, kernel=grad_kernel, anchor=(0, 0)) - -# if image_process_listener is not None: -# # grab a slice of the gradient image for debugging purpose -# cut = gradients[:, :, gradients.shape[2] / 2] -# image_process_listener.onImage(cut, 'cut_%f' % (orientation)) - -# gradients = numpy.swapaxes(gradients, 1, 2) -# gradients *= gradients -# texture_amount = numpy.sum(gradients, axis=2) -# image_process_listener.onImage(texture_amount, 'texture_amount_%f' % (orientation)) -# return texture_amount - -# def processImage(self, image): -# self.orientation_amount_image = OrientationProbabilityEstimator.compute_noise_in_perpendicular_orientation(image, self.searched_orientation, self.get_image_process_listener()) - -# def get_result(self): -# return self.orientation_amount_image - \ No newline at end of file diff --git a/tests/improc_tests.py b/tests/improc_tests.py index f86ede8..7680db2 100644 --- a/tests/improc_tests.py +++ b/tests/improc_tests.py @@ -8,7 +8,7 @@ import unittest # unittest2 doesn't exist in fiji import sys from lipase.imageengine import IImageEngine, PixelType, Aabb, NullDebugger, FileBasedDebugger from lipase.imagej.ijimageengine import IJImageEngine, IJImage -from lipase.localprojector import create_circle_image +from lipase.circsymdetector import create_circle_image class ImProcTester(unittest.TestCase): diff --git a/tests/test0001.py b/tests/test0001.py index 60807a6..c71126b 100644 --- a/tests/test0001.py +++ b/tests/test0001.py @@ -16,6 +16,7 @@ from lipase.traps_detector import TrapsDetector from lipase.catalog import ImageCatalog, Sequence from lipase.lipase import Lipase, ImageLogger from lipase.lipase import GlobulesAreaEstimator, EmptyFrameBackgroundEstimator +from lipase.circsymdetector import CircularSymmetryDetector from lipase.imagej.hdf5serializer import save_hdf5_file @@ -35,78 +36,86 @@ class TestLipase(unittest.TestCase): print("uninitializing TestLipase instance") self.catalog = None - def test_estimate_white(self): - sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2'] - white_estimator = WhiteEstimator(open_size=75, close_size=75, average_size=75) - white_estimate = white_estimator.estimate_white([sequence], ['DM300_327-353_fluo']) - # find_white_reference_image(white_estimate, sequence.get_white()) - print(white_estimate) - IImageEngine.get_instance().debugger.on_image(white_estimate, 'white_estimate') - # assert False, "hellooooo" - print('end of test_estimate_white') + # def test_estimate_white(self): + # sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos2'] + # white_estimator = WhiteEstimator(open_size=75, close_size=75, average_size=75) + # white_estimate = white_estimator.estimate_white([sequence], ['DM300_327-353_fluo']) + # # find_white_reference_image(white_estimate, sequence.get_white()) + # print(white_estimate) + # IImageEngine.get_instance().debugger.on_image(white_estimate, 'white_estimate') + # # assert False, "hellooooo" + # print('end of test_estimate_white') - def test_uniform_lighting_correction(self): - 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)) # pylint: disable=unused-variable + # def test_uniform_lighting_correction(self): + # 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)) # pylint: disable=unused-variable - 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.get_width(), image.get_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_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.get_width(), image.get_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 - tolerance = 1500 - maxima_finder = MaximaFinder(threshold, tolerance) - template_matcher = TemplateMatcher(maxima_finder) - traps_detector = TrapsDetector(template_matcher) - sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0'] - x_min = 423 - x_max = 553 - y_min = 419 - y_max = 533 - trap_aabb = Aabb(x_min, y_min, x_max, y_max) - 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 test_traps_detector(self): + # # the typical value of peaks is -500 and the value between peaks is below -2500 + # threshold = -1500.0 + # tolerance = 1500 + # maxima_finder = MaximaFinder(threshold, tolerance) + # template_matcher = TemplateMatcher(maxima_finder) + # traps_detector = TrapsDetector(template_matcher) + # sequence = self.catalog.sequences['res_soleil2018/GGH/GGH_2018_cin2_phiG_I_327_vis_-40_1/Pos0'] + # x_min = 423 + # x_max = 553 + # y_min = 419 + # y_max = 533 + # trap_aabb = Aabb(x_min, y_min, x_max, y_max) + # 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 test_visible_traps_sequence_processing(self): + # 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) + # results = processor.detect_particles(visible_traps_sequence) + # save_hdf5_file('results.h5', results) + # # results file could be checked with "h5dump --xml ./lipase.git/results.h5" + # first_frame_measured_ratio = results['globules_area_ratio'][(0,)] + # first_frame_expected_ratio = 0.008 + # self.assertAlmostEqual(first_frame_measured_ratio, first_frame_expected_ratio, delta=0.01) + + def test_circle_detector(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) - results = processor.detect_particles(visible_traps_sequence) - save_hdf5_file('results.h5', results) - # results file could be checked with "h5dump --xml ./lipase.git/results.h5" - first_frame_measured_ratio = results['globules_area_ratio'][(0,)] - first_frame_expected_ratio = 0.008 - self.assertAlmostEqual(first_frame_measured_ratio, first_frame_expected_ratio, delta=0.01) - + src_image = visible_traps_sequence.get_image(frame_index=0) + ie = IImageEngine.get_instance() + detector = CircularSymmetryDetector(max_radius=10.0) + radial_profiles = detector.compute_radial_profiles(src_image) + # def test_lipase_process(self): # lipase = Lipase(self.catalog, debugger=NullDebugger())