From 93f0c812e789100c2d8035c5510380c090aa9331 Mon Sep 17 00:00:00 2001 From: alexgobbo Date: Fri, 13 Sep 2024 10:03:45 +0200 Subject: [PATCH] --- config/settings.properties | 2 +- devices/cover_detection.properties | 8 ++ script/devices/CoverDetection.py | 129 +++++++++++++++++++++++++++ script/imgproc/NewCoverDetection.py | 131 +--------------------------- script/test/TestCoverDetection.py | 90 +++---------------- 5 files changed, 154 insertions(+), 206 deletions(-) create mode 100644 devices/cover_detection.properties create mode 100644 script/devices/CoverDetection.py diff --git a/config/settings.properties b/config/settings.properties index 6da54be..d4fe438 100644 --- a/config/settings.properties +++ b/config/settings.properties @@ -1,4 +1,4 @@ -#Thu Sep 12 16:17:12 CEST 2024 +#Fri Sep 13 09:52:22 CEST 2024 barcode_reader_scan_pucks=false beamline_status_enabled=false cold_position_timeout=3600 diff --git a/devices/cover_detection.properties b/devices/cover_detection.properties new file mode 100644 index 0000000..5f635d6 --- /dev/null +++ b/devices/cover_detection.properties @@ -0,0 +1,8 @@ +#Fri Sep 13 10:02:48 CEST 2024 +center_x=820.0 +center_y=570.0 +continuous=false +modulo=3 +number_images=2 +scale_x=0.3 +scale_y=0.3 diff --git a/script/devices/CoverDetection.py b/script/devices/CoverDetection.py new file mode 100644 index 0000000..82d029f --- /dev/null +++ b/script/devices/CoverDetection.py @@ -0,0 +1,129 @@ +run("imgproc/NewCoverDetection") + + +class CoverDetectionConfig(RegisterConfig): + def __init__(self): + self.number_images = 1 + self.continuous = False + self.modulo = 1 + self.center_x = 820.0 + self.center_y = 570.0 + self.scale_x = 1.0 + self.scale_y = 1.0 + +class CoverDetection(ReadonlyAsyncRegisterBase, ReadonlyRegisterArray): + + def __init__(self, name, image): + ReadonlyAsyncRegisterBase.__init__(self, name, CoverDetectionConfig()) + self.image = image + + class ImgListener (ImageListener): + def onImage(s, origin, image, data): + self.append(image) + def onError(s, origin, ex): + print ex + + self.listener = ImgListener() + self.enabled = False + self.images = [] + self.processing_time = None + self.renderer = None + self.error = None + self.error_overlay=None + self.img_counter, self.proc_counter = 0, 0 + self.offset_px = self.offset_um = None + + def doInitialize(self): + self.enable() + self.clear() + + def doClose(self): + self.disable() + + def set_renderer(self, renderer): + self.clear() + self.renderer = renderer + + def enable(self): + if not self.enabled: + self.enabled = True + self.image.addListener(self.listener) + self.img_counter, self.proc_counter = 0, 0 + self.offset_px = self.offset_um = None + + def disable(self): + if self.enabled: + self.image.removeListener(self.listener) + self.enabled = False + self.onReadout(None) + self.clear() + + def clear(self): + self.set_marker(None) + self.set_error_overlay(None) + + def append(self, image): + self.img_counter += 1 + if self.img_counter % self.config.modulo == 0: + ip = load_image(ImagingUtils.grayscale(image, None)) + self.images.append(ip) + while len(self.images) > self.config.number_images: + self.images.pop(0) + if len(self.images) == self.config.number_images: + start = time.time() + if len(self.images) ==1: + ip = self.images[0] + else: + ip = integrate(self.images) + try: + self.error = None + x,y = detect(ip) + except Exception as e: + self.error = e + x,y = -1, -1 + self.processing_time = time.time()-start + self.proc_counter += 1 + self.set(x, y, self.processing_time * 1000, self.error) + if not self.config.continuous: + self.images=[] + + def set(self, x, y, tm, error): + if self.enabled: + if error: + self.offset_px = self.offset_um = None + value = None + else: + offx, offy = x-cover_detection.config.center_x, -(y-cover_detection.config.center_y) + self.offset_px = opx, opy = round(offx), round(offy) + self.offset_um = omx, omy = self.config.scale_x * offx * 1000.0, self.config.scale_y * offy *1000.0 + value = to_array([x, y, opx, opy, omx, omy, tm], 'i') + self.onReadout(value) + if self.renderer is not None: + if error is None: + marker = Overlays.Crosshairs(renderer.getPenProfile(), Point(int(x),int(y)), Dimension(-1, -1)) + else: + marker = None + self.set_marker(marker); + self.set_error_overlay(error) + + def set_marker(self, marker): + if self.renderer is not None: + self.renderer.setMarker(marker) + + def set_error_overlay(self, error): + if self.renderer is not None: + if error is None: + error_overlay = None + else: + error_overlay = Overlays.Text(renderer.getPenErrorText(), str(error), Font("Verdana", Font.PLAIN, 12), Point(20, 20)) + error_overlay.setFixed(True) + error_overlay.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT) + renderer.updateOverlays(error_overlay, self.error_overlay) + self.error_overlay = error_overlay + else: + self.error_overlay = None + + +add_device(CoverDetection("cover_detection", image), force = True) +renderer=show_panel(image) +cover_detection.set_renderer(renderer) diff --git a/script/imgproc/NewCoverDetection.py b/script/imgproc/NewCoverDetection.py index a50534d..c5d8da2 100644 --- a/script/imgproc/NewCoverDetection.py +++ b/script/imgproc/NewCoverDetection.py @@ -7,10 +7,8 @@ FILL_STRATEGY = "mask" #"mask", "bits", "none" OPEN_STRATEGY = "both" #"black", "white", "both", "none" DIAM_1 = 84 DIAM_2 = 550 -DETECT_THRESHOLD = 10 #PIXELS -FRAMES = 5 +OFFSET_THRESHOLD= 10 #PIXELS MASK_SIZE = 0.8 # of DIAM_2 -ROLLING_WINDOW = False def detect(ip, print_table=False, output_image=False): @@ -72,134 +70,11 @@ def detect(ip, print_table=False, output_image=False): x1,y1 = res1.getValue("XM", 0), res1.getValue("YM", 0) - if abs(x1-x2) > DETECT_THRESHOLD or abs(y1-y2) > DETECT_THRESHOLD: - msg = "Detection offseterror: " + + "(" + str(x1-x2) + ", " + str(y1-y2) + ")" + if abs(x1-x2) > OFFSET_THRESHOLD or abs(y1-y2) > OFFSET_THRESHOLD: + msg = "Detection offset error: " + + "(" + str(x1-x2) + ", " + str(y1-y2) + ")" raise Exception( msg ) return x1,y1 -""" -if FRAMES>1: - ip = integrate_frames(samples = FRAMES) -else: - ip = load_image(image.output, title="Image") - -try: - x,y = detect(ip) - print("Detected: ", (x, y)) - p = Point(int(x),int(y)) - ov = Overlays.Crosshairs(renderer.getPenProfile(), p, Dimension(-1, -1)) - renderer.setMarker(ov); -except Exception as e: - traceback.print_exc(e) - print(e) - renderer.setMarker(None) -""" - -error_overlay=None -def clear_error_overlay(renderer): - global error_overlay - try: - renderer.removeOverlay(error_overlay) - except: - pass - error_overlay = None - -def add_error_overlay(renderer, error): - global error_overlay - former = error_overlay - error_overlay = Overlays.Text(renderer.getPenErrorText(), error, Font("Verdana", Font.PLAIN, 12), Point(20, 20)) - error_overlay.setFixed(True) - error_overlay.setAnchor(Overlay.ANCHOR_VIEWPORT_TOP_LEFT) - renderer.updateOverlays(error_overlay, former) - - -class CoverDetection(ReadonlyAsyncRegisterBase, ReadonlyRegisterArray): - - def __init__(self, name, image, renderer): - ReadonlyAsyncRegisterBase.__init__(self, name) - self.image = image - - class ImgListener (ImageListener): - def onImage(s, origin, image, data): - self.append(image) - def onError(s, origin, ex): - print ex - - self.listener = ImgListener() - self.enabled = False - self.images = [] - self.processing_time = None - self.renderer = renderer - self.error = None - - - def doInitialize(self): - print "Init" - #super(ReadonlyAsyncRegisterBase, self).doInitialize() - #ReadonlyRegisterBase.doInitialize(self) - self.enable() - self.clear() - - def doClose(self): - print 1 - self.disable() - print 2 - #DeviceBase.doClose(self) - #print 3 - - - def enable(self): - if not self.enabled: - self.enabled = True - self.image.addListener(self.listener) - - def disable(self): - if self.enabled: - self.image.removeListener(self.listener) - self.enabled = False - self.onReadout(None) - self.clear() - - def clear(self): - if self.renderer is not None: - renderer.setMarker(None) - clear_error_overlay(renderer) - - def append(self, image): - ip = load_image(ImagingUtils.grayscale(image, None)) - self.images.append(ip) - while len(self.images) > FRAMES: - self.images.pop(0) - if len(self.images) == FRAMES: - ip = integrate(self.images) - #print type(ip) - start = time.time() - try: - self.error = None - x,y = detect(ip) - except Exception as e: - self.error = e - x,y = -1, -1 - self.processing_time = time.time()-start - self.set(x, y, self.processing_time * 1000, self.error) - if not ROLLING_WINDOW: - self.images=[] - - def set(self, x, y, tm, error): - if self.enabled: - value = to_array([x, y, tm], 'i') - self.onReadout(value) - if self.renderer is not None: - if error is None: - clear_error_overlay(renderer) - marker = Overlays.Crosshairs(renderer.getPenProfile(), Point(int(x),int(y)), Dimension(-1, -1)) - else: - add_error_overlay(renderer, str(error)) - marker = None - renderer.setMarker(marker); - -renderer=show_panel(image) -add_device(CoverDetection("cover_detection", image, renderer), force = True) diff --git a/script/test/TestCoverDetection.py b/script/test/TestCoverDetection.py index eb6b12f..b908535 100644 --- a/script/test/TestCoverDetection.py +++ b/script/test/TestCoverDetection.py @@ -1,80 +1,16 @@ -import math -from ijutils import * -import ij.process.Blitter as Blitter - - -FILL_STRATEGY = "mask" #"mask", "bits", "none" -OPEN_STRATEGY = "both" #"black", "white", "both", "none" -DIAM_1 = 84 -DIAM_2 = 550 -DETECT_THRESHOLD = 10 #PIXELS -FRAMES = 1 -MASK_SIZE = 0.8 # of DIAM_2 - - -renderer=show_panel(image) -renderer.setMarker(None); -start = time.time() - -#Image Loading +FRAMES =5 if FRAMES>1: - ig = integrate_frames(samples = FRAMES) + ip = integrate_frames(samples = FRAMES) else: - ip = load_image(image.output, title="Image") - ig = grayscale(ip, in_place=False) + ip = load_image(image.output, title="Image") - -#Threshoolding -it = auto_threshold(ig, dark_background=True, in_place=False) -w, h = it.getWidth(), it.getHeight() -#it.show() -if OPEN_STRATEGY in ("black", "both"): #Refine inner - binary_open(it, dark_background=True, iterations=1, count=1); -if OPEN_STRATEGY in ("white", "both"): #Refine outter - binary_open(it, dark_background=False, iterations=1, count=1); -#it.repaintWindow() - -#Detect outer white circle -px = int(pow(DIAM_2/2,2) * math.pi) -(res2,out2)=analyse_particles(it, px/2, px*2, \ - fill_holes = True, exclude_edges = True, print_table=True, \ - output_image = "outlines", minCirc = 0.8, maxCirc = 1.0) - -if res2.size() == 1: - x2,y2 = res2.getValue("XM", 0), res2.getValue("YM", 0) - invert(it, in_place=True) - ip = it.getProcessor() - dist = DIAM_2 * (MASK_SIZE/2.0) - if FILL_STRATEGY == "bits": - for y in range(h): - for x in range(w): - distance = math.hypot(x - x2, y - y2) - if distance >dist: - ip.putPixel(x, y, 0) - elif FILL_STRATEGY == "mask": - mask = ip.createProcessor(w, h) # Creates a new blank ImageProcessor - mask.setColor(255) # Set the drawing color to white (255) - mask.fillOval(int(x2 - dist), int(y2 - dist), int(2 * dist), int(2 * dist)) # Draw the circular ROI - mask.autoThreshold() # Ensures the mask is binary (if needed) - ip.copyBits(mask, 0, 0, Blitter.AND) # Apply the mask using a bitwise AND - - #it.show() - px = int(pow(DIAM_1/2,2) * math.pi) - (res1,out1)=analyse_particles(it, px/2, px*2, \ - fill_holes = FILL_STRATEGY!="none", exclude_edges = True, print_table=True, \ - output_image = "outlines", minCirc = 0.8, maxCirc = 1.0) - - if res1.size() == res2.size() == 1: - x1,y1 = res1.getValue("XM", 0), res1.getValue("YM", 0) - - - if abs(x1-x2) <= DETECT_THRESHOLD and abs(y1-y2) <= DETECT_THRESHOLD: - print("Detected: ", (x1, y1)) - renderer=show_panel(image) - p = Point(int(x1),int(y1)) - ov = Overlays.Crosshairs(renderer.getPenProfile(), p, Dimension(-1, -1)) - renderer.setMarker(ov); - - - -print "Processing time:", time.time()-start \ No newline at end of file +try: + x,y = detect(ip) + print("Detected: ", (x, y)) + p = Point(int(x),int(y)) + ov = Overlays.Crosshairs(renderer.getPenProfile(), p, Dimension(-1, -1)) + renderer.setMarker(ov); +except Exception as e: + traceback.print_exc(e) + print(e) + renderer.setMarker(None)