more work related to graffy/lipase#3: for each pixel, the profile of variance along the circle is now computed. This image basically measures the non-roundedness for each pixel.

This commit is contained in:
Guillaume Raffy 2020-04-08 13:38:16 +02:00
parent 5a144f1907
commit bfca19e039
4 changed files with 48 additions and 12 deletions

View File

@ -1,8 +1,10 @@
#@ ImagePlus (label="the input image") INPUT_IMAGE
#@ Float (label="maximal radius", value=10.0, min=0.0, max=100.0, style="slider") MAX_RADIUS
#@ Int (label="number of angular sectors", value=4, min=0, max=100, style="slider") NUM_ANGULAR_SECTORS
#@ Float (label="maximal radius", value=32.0, min=0.0, max=100.0, style="slider") MAX_RADIUS
#@ Integer (label="number of radial sectors", value=8, min=0, max=100, style="slider") NUM_RADIAL_SECTORS
#@ Integer (label="number of angular sectors", value=4, min=0, max=100, style="slider") NUM_ANGULAR_SECTORS
#@output ImagePlus RADIAL_PROFILE
#@output ImagePlus RADIAL_PROFILES
#@output ImagePlus ANGULAR_VARIANCE_AVG_IMAGE
"""This script is supposed to be launched from fiji's jython interpreter
This imagej plugin computes the radial profile of each pixel of the input image. The resulting profiles are stored in a single hyperstack, where the profile data are stored along the channel axis
@ -25,17 +27,20 @@ from jarray import zeros, array # pylint: disable=import-error
def run_script():
global INPUT_IMAGE # pylint:disable=global-variable-not-assigned
global MAX_RADIUS # pylint:disable=global-variable-not-assigned
global RADIAL_PROFILE # pylint:disable=global-variable-not-assigned
global NUM_RADIAL_SECTORS # pylint:disable=global-variable-not-assigned
global NUM_ANGULAR_SECTORS # pylint:disable=global-variable-not-assigned
global RADIAL_PROFILES # pylint:disable=global-variable-not-assigned
global ANGULAR_VARIANCE_AVG_IMAGE # pylint:disable=global-variable-not-assigned
IImageEngine.set_instance(IJImageEngine())
src_image = IImageEngine.get_instance().create_image(width=1, height=1, pixel_type=PixelType.U8)
src_image.ij_image = INPUT_IMAGE # pylint: disable=undefined-variable
detector = CircularSymmetryDetector(max_radius=MAX_RADIUS, num_angular_sectors=NUM_ANGULAR_SECTORS) # pylint: disable=undefined-variable
radial_profiles = detector.compute_radial_profiles(src_image)
detector = CircularSymmetryDetector(max_radius=MAX_RADIUS, num_angular_sectors=NUM_ANGULAR_SECTORS, num_radial_sectors=NUM_RADIAL_SECTORS) # pylint: disable=undefined-variable
radial_profiles, angular_variance_avg_image = detector.compute_radial_profiles(src_image)
RADIAL_PROFILE = radial_profiles.hyperstack
RADIAL_PROFILES = radial_profiles.hyperstack
ANGULAR_VARIANCE_AVG_IMAGE = angular_variance_avg_image.ij_image
# note : when launched from fiji, __name__ doesn't have the value "__main__", as when launched from python

View File

@ -168,12 +168,17 @@ class CircularSymmetryDetector:
""" 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
:rtype IHyperstack, Image :
:returns:
- radial_profiles (:py:class:`IHyperstack`) - each radial profile is stored in the channel coordinate of the hyperstack
- angular_variance_avg (:py:class:`IImage`) - each pixel stores the average variance of signal along the circles of different diameters in the neighborhood
"""
# https://stackoverflow.com/questions/39759503/how-to-document-multiple-return-values-using-restructuredtext-in-python-2
ie = IImageEngine.get_instance()
ie.debugger.on_image(src_image, 'src_image')
projector_base = CircularSymmetryProjectorBase(self.max_radius, num_angular_sectors=self.num_angular_sectors, num_radial_sectors=self.num_radial_sectors, oversampling_scale=1)
radial_profile_image = ie.create_hyperstack(width=src_image.get_width(), height=src_image.get_height(), num_channels=projector_base.num_radial_sectors, num_slices=1, num_frames=1, pixel_type=PixelType.F32)
angular_variances_image = ie.create_hyperstack(width=src_image.get_width(), height=src_image.get_height(), num_channels=projector_base.num_radial_sectors, num_slices=1, num_frames=1, pixel_type=PixelType.F32)
for radius_index in range(projector_base.num_radial_sectors):
circle_stack = ie.create_hyperstack(width=src_image.get_width(), height=src_image.get_height(), num_channels=projector_base.num_angular_sectors, num_slices=1, num_frames=1, pixel_type=PixelType.F32)
for angular_sector_index in range(projector_base.num_angular_sectors):
@ -184,6 +189,15 @@ class CircularSymmetryDetector:
circle_stack.set_image(projection, frame_index=0, slice_index=0, channel_index=angular_sector_index)
# compute the image in which each pixel contains the mean value of the source image in the given circular region around the pixel
circle_mean_image = ie.compute_mean(StackImageFeeder(circle_stack))
radial_profile_image.set_image(circle_mean_image, frame_index=0, slice_index=0, channel_index=radius_index)
return radial_profile_image
square_diffs_stack = circle_stack # note : circle_stack is reused to store square_diffs_stack
for angular_sector_index in range(projector_base.num_angular_sectors):
angular_sector_image = circle_stack.get_image(frame_index=0, slice_index=0, channel_index=angular_sector_index)
diff_image = ie.subtract(angular_sector_image, circle_mean_image)
square_diff_image = ie.multiply(diff_image, diff_image)
square_diffs_stack.set_image(square_diff_image, frame_index=0, slice_index=0, channel_index=angular_sector_index)
variance_image = ie.compute_mean(StackImageFeeder(square_diffs_stack))
angular_variances_image.set_image(variance_image, frame_index=0, slice_index=0, channel_index=radius_index)
angular_variance_avg_image = ie.compute_mean(StackImageFeeder(angular_variances_image))
return radial_profile_image, angular_variance_avg_image

View File

@ -170,7 +170,6 @@ class IImageFeeder(ABC):
creates an hyperstack from this image feeder
:rtype IHyperStack
"""
it = iter(self)
image = it.next()
@ -235,6 +234,9 @@ class StackImageFeeder(IImageFeeder):
raise StopIteration
else:
image = self.hyperstack.get_image(frame_index=self.next_frame_index, slice_index=self.next_slice_index, channel_index=self.next_channel_index)
# mean_value = image.get_mean_value()
# print("mean_value", mean_value)
# assert mean_value > 0.001
# compute next image index
self.next_slice_index += 1
@ -380,7 +382,17 @@ class IImageEngine(ABC):
:param IImage image2:
:rtype IImage: image1-image2
"""
@abc.abstractmethod
def multiply(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):
"""

View File

@ -290,6 +290,11 @@ class IJImageEngine(IImageEngine):
result_image = ic.run("Subtract create float", image1.ij_image, image2.ij_image)
return IJImage(self, result_image)
def multiply(self, image1, image2):
ic = ImageCalculator()
result_image = ic.run("Multiply 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)