From ecf6bf0dace105c728bfcd2bce38d4ba10666ca7 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Wed, 29 Oct 2025 13:38:12 +0100 Subject: [PATCH] DAQ: updated ALC to use updated mlbox.py and to give more information to the DB --- daq/src/aaredaq/daq.py | 153 ++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 57 deletions(-) diff --git a/daq/src/aaredaq/daq.py b/daq/src/aaredaq/daq.py index d756a64e..9ce93ef7 100644 --- a/daq/src/aaredaq/daq.py +++ b/daq/src/aaredaq/daq.py @@ -785,18 +785,19 @@ class AareDAQ: time.sleep(0.2) # Just to be sure image is stable #curr_image = self.camera_image #box = self.__mlbox.predict(curr_image) - bgr_image = cv2.cvtColor(self.camera_image, cv2.COLOR_RGB2BGR) - box = self.__mlbox.predict(bgr_image, pref_class=(3,0)) - if box is None: + curr_image = cv2.cvtColor(self.camera_image, cv2.COLOR_RGB2BGR) + m = self.__mlbox.predict(curr_image, preferred_class=(3,0)) + if m is None: if filename is not None: - cv2.imwrite(f"{filename}_no_detection.jpg", bgr_image) - self.__aare.upload_image(sample_id, f"{filename}_no_detection", bgr_image) + cv2.imwrite(f"{filename}_no_detection.jpg", curr_image) + self.__aare.upload_image(sample_id, f"{filename}_no_detection", curr_image) return None - _, x1, y1, x2, y2 = box + x1, y1, x2, y2 = m.box.top_x, m.box.top_y, m.box.bottom_x, m.box.bottom_y + if filename is not None: - cv2.rectangle(bgr_image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) - cv2.imwrite(f"{filename}.jpg", bgr_image) - self.__aare.upload_image(sample_id, filename, bgr_image) + cv2.rectangle(curr_image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) + cv2.imwrite(f"{filename}.jpg", curr_image) + self.__aare.upload_image(sample_id, filename, curr_image) geom = self.sample_geometry @@ -817,41 +818,74 @@ class AareDAQ: omega_deg=geom.omega_deg ) - def __ml_loop_centre_box(self, sample_id: int | None = None, filename: str | None = None) -> SmargonCoordinate | None: - time.sleep(0.2) # Just to be sure image is stable + def __ml_loop_centre_box(self, sample_id: int | None = None, filename: str | None = None) -> tuple[SmargonCoordinate | None, int| None, list[int] | None]: + #time.sleep(0.2) # Just to be sure image is stable bgr_image = cv2.cvtColor(self.camera_image, cv2.COLOR_RGB2BGR) - box = self.__mlbox.predict(bgr_image, filename) + boxes = self.__mlbox.predict_all_best(bgr_image, overlap_with_pin = 0.5, confidence_min=0.3) - if box is None: + if boxes is None: if filename is not None: #cv2.imwrite(f"{filename}_no_detection.jpg", bgr_image) self.__aare.upload_image(sample_id, f"{filename}_no_detection", bgr_image) - return None + return None, None, None - cls, x1, y1, x2, y2 = box + classes: list[int] = [] + pin = None + centre_x, centre_y = None, None + for box in boxes.boxes.values(): + if box and box.cls is not None: + classes.append(int(box.cls.value)) + if int(box.cls.value) == 1: + pin=box + + best_box = self.__mlbox.get_preferred_class_box(boxes, (2,3,0,1)) + if best_box is None or best_box.box is None or best_box.cls is None: + return None, None, classes if classes else None + + + cls = int(best_box.cls.value) + x1 = best_box.box.top_x + y1 = best_box.box.top_y + x2 = best_box.box.bottom_x + y2 = best_box.box.bottom_y if filename is not None: - #cv2.rectangle(bgr_image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) - #cv2.imwrite(f"{filename}.jpg", bgr_image) self.__aare.upload_image(sample_id, filename, bgr_image) geom = self.sample_geometry if cls == 0: # loop_all - centre_y = y1 + (y2 - y1)/2 - centre_x = x1 + if y1 + y2 <= x1 + x2: + centre_y = y1 + (y2 - y1) / 2 + centre_x = x1 + elif pin: + position_dict = self.__mlbox.check_box_relation(pin, best_box) + if position_dict["overlap_y"] and position_dict["overlap_x"]: + centre_y = y1 + (y2 - y1) / 2 + centre_x = x1 + cls = 1 + logger.debug("significant overlap between pin and loop_all not picked up by ML") + else: + centre_y = y2 if position_dict["top"] else y1 + centre_x = x1 + (x2 - x1) / 2 + else: + centre_y = y1 + centre_x = x1 + (x2 - x1) / 2 + elif cls == 1: # pin centre_y = y1 + (y2 - y1)/2 centre_x = x1 + elif cls == 2 or cls == 3: #crystal or loop_face centre_y = y1 + (y2 - y1)/2 centre_x = x1 + (x2 - x1)/2 + else: print(f"unknown box class {cls}") - return None + return None, cls, classes if classes else None coord = geom.picture_to_smargon(Coordinate(x=centre_x, y=centre_y)) - return SmargonCoordinate(sh_mm=coord) + return SmargonCoordinate(sh_mm=coord), cls, classes def ml_bounding_box(self, sample_id: int | None = None, filename: str | None = None) -> RasterGridRequest | None: try: @@ -1014,71 +1048,74 @@ class AareDAQ: def __loop_center_sequence(self, sample_id: int | None = None) -> bool: self.__set_state(BeamlineStateEnum.SampleAlignment) self.__devs.lamp_light = 2.5 + + found_classes_count: dict[int, int] = {0:0, 1:0, 2:0, 3:0} + try: - i = 0 self.__cfg.zoom_mode = ZoomModeEnum.LoopCenter zoom_settings = self.__cfg.zoom_settings.z - for zoom_value in zoom_settings: + + for zoom_iter, zoom_value in enumerate(zoom_settings): exposure = zoom_settings[zoom_value].exposure gain = zoom_settings[zoom_value].gain max_attempt = 2 attempt = 0 - found = 0 - angles = (0, -90) + + base_angles = (0, 45, 90) if (zoom_iter % 2 == 0) else (90, 45, 0) if sample_id is not None: self.save_screenshot_db(sample_id, f"pre_alc") while attempt < max_attempt: - logger.debug('new loop center settings') self.__devs.samcam_settings = SampleCameraSettings(exposure=exposure, gain=gain) - self.__devs.zoom_sync(zoom_value) - logger.debug(f'zoom={zoom_value},gain={gain}, exp={exposure}') - - found_flag = False - found_angle = None - targets_found_this_attempt = 0 exp = int(exposure * 1000) gn = int(gain) + self.__devs.zoom_sync(zoom_value) + logger.debug(f'zoom={zoom_value},gain={gn}, exp={exp}ms') - for loop, angle in enumerate(angles): + found_flag = False + found_angle: int | None = None + targets_found_this_attempt = 0 + + for angle in base_angles: logger.debug(f"Moving to new omega: {angle}") - # if sample_id is not None and i == 0: - # self.save_screenshot_db(sample_id, f"pre_alc_{sample_id}_{angle}_{zoom_value:.0f}_{exp}_{gn}") time_to_move_aerotech= time.perf_counter() self.__devs.aerotech.move(angle, wait=True) logger.info(f"time to move: {time.perf_counter()-time_to_move_aerotech}") - #time.sleep(0.1) - filename = None - if sample_id is not None: - filename = f"{sample_id}_{angle}_{zoom_value:.0f}_{exp}_{gn}" - target = self.__ml_loop_centre_box(sample_id, filename) + filename = f"{sample_id}_{angle}_{zoom_value:.0f}_{exp}_{gn}" if sample_id is not None else None - if target is not None: + try: + target, cls, classes = self.__ml_loop_centre_box(sample_id, filename) + except: + target, cls, classes = None, None, None + + if target is None: + continue + + if classes: + for c in classes: + found_classes_count[int(c)] += 1 + + if cls is not None and cls != 1: + targets_found_this_attempt += 1 found_flag = True found_angle = angle - found += 1 - targets_found_this_attempt += 1 - time_to_move_smargon = time.perf_counter() - self.__devs.smargon.target = target - self.__devs.smargon.wait(60) - logger.info(f"time to move smargon: {time.perf_counter() - time_to_move_smargon}") - - if sample_id is not None: - self.__aare.sample_centered(self.__cfg.current_sample) - self.save_screenshot_db(sample_id, f"{sample_id}_{angle}_{zoom_value:.0f}_{exp}_{gn}") + time_to_move_smargon = time.perf_counter() + self.__devs.smargon.target = target + self.__devs.smargon.wait(60) + logger.info(f"time to move smargon: {time.perf_counter() - time_to_move_smargon}") if targets_found_this_attempt == 0: raise LoopCenteringFailed else: - if found >= 3 or targets_found_this_attempt >= 3: - logger.debug(f"sucessfully found {found} or {targets_found_this_attempt} targets in {attempt} attempts") + if targets_found_this_attempt >= len(base_angles): + logger.debug(f"sucessfully found {targets_found_this_attempt} targets in attempt {attempt + 1} ") break if found_flag is not None and found_angle is not None: - logger.debug(f"found a target at angle {found_angle} in attempt {attempt}") - angles = (found_angle, found_angle + 45, found_angle + 90) + logger.debug(f"found a target at angle {found_angle} in attempt {attempt + 1}") + base_angles = (found_angle, found_angle + 45, found_angle + 90) attempt += 1 logger.debug(f"attempt {attempt} of {max_attempt}") if attempt >= max_attempt: @@ -1086,8 +1123,11 @@ class AareDAQ: #i += 1 logger.debug("alc success") - #self.face_detection_sequence() + self.__aare.sample_centered(self.__cfg.current_sample) + if sample_id is not None: + self.save_screenshot_db(sample_id, f"{sample_id}_centered") return True + except Exception as e: logger.info(f"Error in loop centering: {e}") return False @@ -1097,7 +1137,6 @@ class AareDAQ: try: self.__cfg.try_set_busy(timeout=360) if not self.__loop_center_sequence(sample_id): - self.__aare.alc_failed(self.sample) raise LoopCenteringFailed self.__cfg.state_busy = False