added the processing that computes the traps mask from the clean trap image
Bug 2804 - Faire un traitement qui détecte les pièges
This commit is contained in:
parent
cc6d89ed7c
commit
b4458c0697
|
@ -39,6 +39,10 @@ class PixelType:
|
|||
|
||||
class IImage(ABC):
|
||||
|
||||
@abc.abstractmethod
|
||||
def clone(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def width(self):
|
||||
pass
|
||||
|
@ -51,16 +55,25 @@ class IImage(ABC):
|
|||
def get_subimage(self, aabb):
|
||||
"""
|
||||
:param Aabb aabb: axis-aligned bounding box of the subimage
|
||||
:return IImage:
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_subimage(self, subimage, position):
|
||||
"""
|
||||
:param IImage subimage: the image to copy into self
|
||||
:param (int, int) position: where to insert the subimage in self (upper left pixel)
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_pixel_type(self):
|
||||
"""
|
||||
:rtype PixelType:
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
class IHyperStack(ABC):
|
||||
|
||||
|
@ -277,6 +290,15 @@ class IImageEngine(ABC):
|
|||
:param str out_file_path: eg './white_estimate.tiff'
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def compute_edge_transform(self, image):
|
||||
"""
|
||||
computes the edge transform using a sobel filter
|
||||
|
||||
:param IImage image:
|
||||
:rtype IImage:
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def compute_max(self, image_feeder):
|
||||
"""Compute for each pixel position the maximum at this position in all input images.
|
||||
|
@ -349,3 +371,37 @@ class IImageEngine(ABC):
|
|||
:param float tolerance: ignore local maxima if they are in the neighborhood of another maximum. The neighborhood of a maximum is defined by the area that propagates until the the difference between the pixel value and the value of the considered local maximum exceeds tolerance. The higher the tolerance, the more local maxima are removed....
|
||||
:return list(Match): maxima
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def auto_threshold(self, src_image):
|
||||
"""
|
||||
:rtype IImage:
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def flood_fill(self, src_binary_image, flood_start_pos):
|
||||
"""
|
||||
performs flood fill from starting point
|
||||
|
||||
:param IImage src_binary_image:
|
||||
:param (int, int) flood_start_pos: start position of the flood fill
|
||||
:rtype IImage: binary image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def fill_holes(self, src_binary_image):
|
||||
"""
|
||||
fills the holes in the given binary image
|
||||
|
||||
:param IImage src_binary_image:
|
||||
:rtype IImage: binary image
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def invert(self, src_binary_image):
|
||||
"""
|
||||
inverts the given image pixel values (black becomes white and white becomes black)
|
||||
|
||||
:param IImage src_binary_image:
|
||||
:rtype IImage: binary image
|
||||
"""
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
from ..imageengine import IImage, IHyperStack, IImageEngine, PixelType
|
||||
from ..maxima_finder import Match
|
||||
from ij import IJ, ImagePlus # pylint: disable=import-error
|
||||
from ij.process import FloodFiller # pylint: disable=import-error
|
||||
from ij.process import Blitter # pylint: disable=import-error
|
||||
from ij.measure import ResultsTable # pylint: disable=import-error
|
||||
from ij.plugin import ImageCalculator # pylint: disable=import-error
|
||||
from ij.plugin.filter import MaximumFinder # pylint: disable=import-error
|
||||
|
@ -41,6 +43,11 @@ 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() )
|
||||
return copy
|
||||
|
||||
def width(self):
|
||||
return self.ij_image.width
|
||||
|
||||
|
@ -61,6 +68,11 @@ class IJImage(IImage):
|
|||
# ij_subimage.paste()
|
||||
return IJImage(self.image_engine, ij_subimage)
|
||||
|
||||
def set_subimage(self, subimage, position):
|
||||
(x, y) = position
|
||||
src_processor = subimage.ij_image.getProcessor()
|
||||
dst_processor = self.ij_image.getProcessor()
|
||||
dst_processor.copyBits(src_processor, x, y, Blitter.COPY)
|
||||
|
||||
class IJHyperStack(IHyperStack):
|
||||
|
||||
|
@ -216,6 +228,12 @@ class IJImageEngine(IImageEngine):
|
|||
result_image = ic.run("Divide create float", numerator_image.ij_image, denominator_image.ij_image)
|
||||
return IJImage(self, result_image)
|
||||
|
||||
|
||||
def compute_edge_transform(self, image):
|
||||
result_image = image.clone()
|
||||
result_image.ij_image.getProcessor().findEdges()
|
||||
return result_image
|
||||
|
||||
def compute_max(self, image_feeder):
|
||||
#def compute_max(self, images_file_path):
|
||||
"""Computes for each pixel position the maximum at this position in all input images.
|
||||
|
@ -347,4 +365,34 @@ class IJImageEngine(IImageEngine):
|
|||
match = Match(x, y, correlation)
|
||||
# print(match)
|
||||
matches.append(match)
|
||||
return matches
|
||||
return matches
|
||||
|
||||
def auto_threshold(self, src_image):
|
||||
# Image/Adjust/Threshold, choose auto, then click apply
|
||||
# IJ.setAutoThreshold(imp, "Default dark");
|
||||
binary_image = src_image.clone()
|
||||
IJ.setAutoThreshold(binary_image.ij_image, "Default dark")
|
||||
IJ.run(binary_image.ij_image, "Convert to Mask", "")
|
||||
return binary_image
|
||||
|
||||
def flood_fill(self, src_binary_image, flood_start_pos):
|
||||
# https://imagej.nih.gov/ij/developer/api/ij/process/FloodFiller.html
|
||||
flood_filled_image = src_binary_image.clone()
|
||||
image_processor = flood_filled_image.ij_image.getProcessor()
|
||||
image_processor.setValue(255)
|
||||
flood_filler = FloodFiller(image_processor)
|
||||
(x, y) = flood_start_pos
|
||||
flood_filler.fill(x, y)
|
||||
return flood_filled_image
|
||||
|
||||
def fill_holes(self, src_binary_image):
|
||||
result_image = src_binary_image.clone()
|
||||
IJ.run(result_image.ij_image, "Fill Holes", "")
|
||||
return result_image
|
||||
|
||||
def invert(self, src_binary_image):
|
||||
inverted_image = src_binary_image.clone()
|
||||
image_processor = inverted_image.ij_image.getProcessor()
|
||||
image_processor.invert()
|
||||
return inverted_image
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class TemplateMatcher(object):
|
|||
|
||||
correlation_image = ie.match_template(image, pattern)
|
||||
|
||||
# ie.save_as_tiff(correlation_image, '/home/graffy/Desktop/similarity.tiff')
|
||||
ie.save_as_tiff(correlation_image, './similarity.tiff')
|
||||
|
||||
matches = self.maxima_finder.find_maxima(correlation_image)
|
||||
return matches
|
||||
|
|
|
@ -7,6 +7,45 @@ from preprocessing import WhiteEstimator, correct_non_uniform_lighting
|
|||
from maxima_finder import Match
|
||||
from template_matcher import TemplateMatcher
|
||||
|
||||
class TrapBinarizer(object):
|
||||
|
||||
def __init__(self, flood_fill_start_point):
|
||||
self.flood_fill_start_point = flood_fill_start_point
|
||||
|
||||
def binarize_trap(self, clean_trap_image):
|
||||
"""
|
||||
:param IImage clean_trap_image:
|
||||
"""
|
||||
ie = IImageEngine.get_instance()
|
||||
# convert image to edges because the grey level of traps is not uinform across the image
|
||||
# The threshold works much better on the edge image because the edges of the traps are clearly visible
|
||||
# Process/Find edges
|
||||
# https://imagej.nih.gov/ij/developer/api/ij/process/ImageProcessor.html#findEdges--
|
||||
edge_image = ie.compute_edge_transform(clean_trap_image)
|
||||
ie.save_as_tiff(edge_image, './clean_trap_edge_image.tiff')
|
||||
# IJ.run(imp, "Find Edges", "");
|
||||
# after find edges, the walls are dark lines surrounded by 2 lines of clear pixels.
|
||||
# Image/Adjust/Threshold, choose auto, then click apply
|
||||
# IJ.setAutoThreshold(imp, "Default dark");
|
||||
is_edge = ie.auto_threshold( edge_image )
|
||||
ie.save_as_tiff(is_edge, './is_trap_edge.tiff')
|
||||
# convert image to mask
|
||||
# at this point, the walls are black lines surrounded by 2 thick lines of white pixels
|
||||
background_seed_pos = (70,34)
|
||||
outer_edge_merged = ie.flood_fill(is_edge, background_seed_pos)
|
||||
ie.save_as_tiff(outer_edge_merged, './outer_edge_merged.tiff')
|
||||
# floodFill(70,34);
|
||||
# ij.process.FloodFiller
|
||||
# the start point of the flood fill is expected to be outside the trap so that the flood will merge with the outer white line, and stop at the wall position.
|
||||
# IJ.run(imp, "Invert");
|
||||
is_trap_outer_limit = ie.invert(outer_edge_merged)
|
||||
ie.save_as_tiff(is_trap_outer_limit, './is_trap_outer_limit.tiff')
|
||||
# the wall is now white and surrounded wy black. Inside the walls we have the inner black line, plus som more white pixels in the trap's flat areas. Filling holes will then perge together what's not the outside; after this, all trap pixels are white.
|
||||
# IJ.run(imp, "Fill Holes", "");
|
||||
is_trap = ie.fill_holes(is_trap_outer_limit)
|
||||
ie.save_as_tiff(is_trap, './is_trap.tiff')
|
||||
return is_trap
|
||||
|
||||
class TrapsDetector(object):
|
||||
|
||||
def __init__(self, template_matcher):
|
||||
|
@ -34,13 +73,14 @@ class TrapsDetector(object):
|
|||
|
||||
first_image = uniform_stack.get_image(frame_index=0)
|
||||
template_trap_image = first_image.get_subimage(template_trap_aabb)
|
||||
# ie.save_as_tiff(trap_image, '/home/graffy/Desktop/template.tiff')
|
||||
|
||||
ie = IImageEngine.get_instance()
|
||||
ie.save_as_tiff(template_trap_image, './template_trap_image.tiff')
|
||||
|
||||
matches = self.template_matcher.match_template(first_image, template_trap_image)
|
||||
num_traps_per_frame = len(matches)
|
||||
num_traps = uniform_stack.num_frames() * num_traps_per_frame
|
||||
|
||||
ie = IImageEngine.get_instance()
|
||||
traps_stack = ie.create_hyperstack(width=template_trap_aabb.width, height=template_trap_aabb.height, num_slices=1, num_frames=num_traps, num_channels=1, pixel_type=PixelType.F32)
|
||||
|
||||
trap_index = 0
|
||||
|
@ -59,6 +99,15 @@ class TrapsDetector(object):
|
|||
clean_trap_image = ie.compute_median(image_feeder)
|
||||
ie.save_as_tiff(clean_trap_image, './clean_trap_image.tiff')
|
||||
|
||||
binarizer = TrapBinarizer((70,34))
|
||||
trap_mask = binarizer.binarize_trap(clean_trap_image)
|
||||
|
||||
traps_mask = ie.create_image(width=uniform_stack.width(), height=uniform_stack.height(), pixel_type=uniform_stack.get_pixel_type())
|
||||
for frame_trap_index in range(num_traps_per_frame):
|
||||
match = matches[frame_trap_index]
|
||||
traps_mask.set_subimage(trap_mask, (match.x, match.y))
|
||||
ie.save_as_tiff(traps_mask, './traps_mask.tiff')
|
||||
|
||||
#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')
|
||||
|
|
Loading…
Reference in New Issue