diff --git a/config/settings.properties b/config/settings.properties index d6838a9..6da54be 100644 --- a/config/settings.properties +++ b/config/settings.properties @@ -1,4 +1,4 @@ -#Wed Sep 11 16:12:29 CEST 2024 +#Thu Sep 12 16:17:12 CEST 2024 barcode_reader_scan_pucks=false beamline_status_enabled=false cold_position_timeout=3600 diff --git a/script/imgproc/NewCoverDetection.py b/script/imgproc/NewCoverDetection.py new file mode 100644 index 0000000..a50534d --- /dev/null +++ b/script/imgproc/NewCoverDetection.py @@ -0,0 +1,205 @@ +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 = 5 +MASK_SIZE = 0.8 # of DIAM_2 +ROLLING_WINDOW = False + + +def detect(ip, print_table=False, output_image=False): + if output_image==True: + output_image= "outlines" + if ip.getProcessor().isGrayscale(): + ig = ip + else: + ig = grayscale(ip, in_place=False) + #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=print_table, \ + output_image = output_image, minCirc = 0.8, maxCirc = 1.0) + + if res2.size() != 1: + msg = "Outter detection error: " + ("none" if res2.size()==0 else "") + for i in range( res2.size()): + msg = msg + "(" + str(res2.getValue("XM", 0)) + ", " + str( res2.getValue("YM", 0)) + ")" + raise Exception( msg ) + + 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 + + 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=print_table, \ + output_image = output_image, minCirc = 0.8, maxCirc = 1.0) + + if res1.size() != 1: + msg = "Inner detection error: " + ("none" if res1.size()==0 else "") + for i in range( res1.size()): + msg = msg + "(" + str(res1.getValue("XM", 0)) + ", " + str( res2.res1("YM", 0)) + "), " + raise Exception( msg ) + + 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) + ")" + 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/imgproc/Utils.py b/script/imgproc/Utils.py index addc508..e122dbe 100644 --- a/script/imgproc/Utils.py +++ b/script/imgproc/Utils.py @@ -63,6 +63,7 @@ roi_radius = 600 roi_border = 30 def get_roi(): + img = image #roi_center = (img.output.width/2, img.output.height/2) #roi_radius = min(roi_center[0], roi_center[1]) #return (roi_center[0] - roi_radius, roi_center[1] - roi_radius, 2* roi_radius, 2*roi_radius) @@ -72,12 +73,13 @@ def get_roi(): return (0,0,img.output.width, img.output.height) def get_image(): + img = image roi = get_roi() #ip = load_image(img.output) #ret = ip if (roi is None) else sub_image(ip, roi[0], roi[1], roi[2], roi[3]) #grayscale(ret, do_scaling=True) - ret = load_image(Utils.grayscale(img.output, Rectangle(roi[0], roi[1], roi[2], roi[3]) if (roi is not None) else None)) + ret = load_image(ImagingUtils.grayscale(img.output, Rectangle(roi[0], roi[1], roi[2], roi[3]) if (roi is not None) else None)) return ret #def detect_pucks(ip): diff --git a/script/test/TestCoverDetection.py b/script/test/TestCoverDetection.py index 15de0d6..eb6b12f 100644 --- a/script/test/TestCoverDetection.py +++ b/script/test/TestCoverDetection.py @@ -1,58 +1,80 @@ import math from ijutils import * +import ij.process.Blitter as Blitter - -DIAM_1 = 80 +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 -ip = load_image(image.output, title="Image") -aux = grayscale(ip, in_place=False) -#invert(aux); - -px1 = int(pow(DIAM_1/2,2) * math.pi) -px2 = int(pow(DIAM_2/2,2) * math.pi) - -#aux.show() - -#binary_open(aux); aux.repaintWindow() +if FRAMES>1: + ig = integrate_frames(samples = FRAMES) +else: + ip = load_image(image.output, title="Image") + ig = grayscale(ip, in_place=False) -ti = auto_threshold(aux, dark_background=False, in_place=False) #; aux.repaintWindow() -#ti.show() -#binary_fill_holes(ti) -#ti.repaintWindow() +#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() -(res1,out1)=analyse_particles(ti, px1/2, px1*2, \ - fill_holes = False, exclude_edges = True, print_table=True, \ - output_image = "outlines", minCirc = 0.8, maxCirc = 1.0) -#out1.show() - -ti = auto_threshold(aux, dark_background=True, in_place=False) #; aux.repaintWindow() -(res2,out2)=analyse_particles(ti, px2/2, px2*2, \ - fill_holes = False, exclude_edges = True, print_table=True, \ +#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) - -#output_img.show() - -if res1.size() == res2.size() == 1: - x1,y1 = res1.getValue("XM", 0), res1.getValue("YM", 0) +if res2.size() == 1: x2,y2 = res2.getValue("XM", 0), res2.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); + 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