DAQ: changs to face detection, mlbox, measure for automation tests
This commit is contained in:
+92
-36
@@ -443,8 +443,12 @@ class AareDAQ:
|
||||
try:
|
||||
value = self.__devs.tell.mount(address=target.tell_address(), force=True, auto_unmount=True, read_dm=False,
|
||||
wait=True, timeout=360.0)
|
||||
if self.__cfg.current_sample is not None and self.__cfg.current_sample.db_id is not None:
|
||||
self.__aare.sample_unmounted(self.__cfg.current_sample)
|
||||
logger.info(f"Mount result: {value}")
|
||||
self.__cfg.current_sample = target
|
||||
if self.__cfg.current_sample is not None and self.__cfg.current_sample.db_id is not None:
|
||||
self.__aare.sample_mounted(self.__cfg.current_sample)
|
||||
except Exception as e:
|
||||
logger.error(f"Mount failed: {e}")
|
||||
if self.__cfg.current_sample is not None and self.__cfg.current_sample.db_id is not None:
|
||||
@@ -702,14 +706,21 @@ class AareDAQ:
|
||||
else:
|
||||
return None
|
||||
|
||||
def __setup_datacollection(self, request: RasterGridRequest | RasterGridRequest):
|
||||
def __setup_datacollection(self, request: RasterGridRequest | RasterGridRequest, screening: bool = False):
|
||||
|
||||
if request.dtz is not None:
|
||||
logger.info(f'requesting dtz to move to {request.dtz}')
|
||||
self.__cfg.dtz = request.dtz
|
||||
|
||||
if self.sample is not None and self.sample.db_id is not None:
|
||||
self.save_screenshot_db(self.sample.db_id, f"{self.sample.db_id}_before_data_collection")
|
||||
sample_id = self.sample.db_id
|
||||
if screening:
|
||||
screenshot_name = f"{sample_id}_before_screening"
|
||||
elif type(request) is RasterGridRequest:
|
||||
screenshot_name = f"{sample_id}_before_raster"
|
||||
else:
|
||||
screenshot_name = f"{sample_id}_before_data_collection"
|
||||
self.save_screenshot_db(sample_id, screenshot_name)
|
||||
|
||||
self.__set_state(BeamlineStateEnum.DataCollection)
|
||||
|
||||
@@ -769,12 +780,13 @@ class AareDAQ:
|
||||
|
||||
def __raster(self, request: RasterGridRequest) -> CompletedRasterGridElem:
|
||||
self.__devs.aerotech_omega = request.omega_deg
|
||||
previous_smargon_pos = self.__devs.smargon_pos
|
||||
self.__setup_datacollection(request=request)
|
||||
|
||||
status = self.status
|
||||
logger.info(f"raster status {status}")
|
||||
logger.info(f'raster grid request: {request}')
|
||||
total_time = request.exp_time_s*request.n_x*request.n_y
|
||||
total_time = request.exp_time_s*request.n_x*request.n_y+request.n_y*0.3
|
||||
result = None
|
||||
if not self.__cfg.simulated_detector:
|
||||
pass
|
||||
@@ -789,6 +801,8 @@ class AareDAQ:
|
||||
logger.info("Simulated detector mode enabled; returning fake zero raster result.")
|
||||
|
||||
try:
|
||||
if self.sample is not None and self.sample.db_id is not None:
|
||||
self.__aare.create_gridscan_run(self.sample, request, status)
|
||||
self.__devs.aerotech.grid_scan(grid_elem_size_y_um=request.grid_size_mm.y*1000,
|
||||
grid_elem_size_x_um=request.grid_size_mm.x*1000,
|
||||
grid_elem_count_x=request.n_x,
|
||||
@@ -798,7 +812,27 @@ class AareDAQ:
|
||||
|
||||
self.__devs.aerotech.wait_till_done(timeout=int(round(total_time*2,0)))
|
||||
|
||||
self.__devs.aerotech_pos = self.__cfg.abr_meas_pos
|
||||
#go back to aerotech x,y,z home not U home (0 degrees).
|
||||
if type(self.__cfg.abr_meas_pos) is Coordinate:
|
||||
coord = self.__cfg.abr_meas_pos
|
||||
else:
|
||||
coord = self.__cfg.abr_meas_pos.at_mm
|
||||
self.__devs.aerotech_pos = AerotechCoordinate(at_mm=coord, omega_deg=self.__devs.aerotech_omega)
|
||||
|
||||
self.__devs.smargon_pos = previous_smargon_pos
|
||||
self.__devs.smargon_wait(timeout=180)
|
||||
|
||||
result = self._build_fake_raster_result(request)
|
||||
|
||||
try:
|
||||
sample_id = self.sample.db_id if self.sample is not None and self.sample.db_id is not None else None
|
||||
if sample_id:
|
||||
self.save_screenshot_db(sample_id, f"{sample_id}_post_raster_{request.omega_deg}deg")
|
||||
self.__aare.ingest_gridscan(sample = self.sample, raster_result = result.result,
|
||||
raster_request = request, geom = self.sample_geometry,
|
||||
com = None, beam_mark_pxl=self.__cfg.get_beam_mark(self.zoom))
|
||||
except Exception as e:
|
||||
logger.error(f"Exception ingesting grid scan: {e}")
|
||||
|
||||
# try:
|
||||
# result = self.__jfjoch.wait_till_done(60)
|
||||
@@ -808,7 +842,7 @@ class AareDAQ:
|
||||
|
||||
if result is None:
|
||||
logger.warning("JFJoch returned no ScanResult; using fake result for raster scan.")
|
||||
return self._build_fake_raster_result(request)
|
||||
return result
|
||||
|
||||
#return CompletedRasterGridElem(request=copy.deepcopy(request), result=result, centre_of_mass=None)
|
||||
|
||||
@@ -858,7 +892,8 @@ class AareDAQ:
|
||||
|
||||
#self.__aare.create_rotation_run(self.sample, request, status)
|
||||
total_time = request.exp_time_s * request.steps
|
||||
|
||||
if self.sample is not None and self.sample.db_id is not None:
|
||||
self.__aare.create_rotation_run(self.sample, request, status)
|
||||
try:
|
||||
|
||||
if self.__cfg.simulated_detector:
|
||||
@@ -905,22 +940,23 @@ class AareDAQ:
|
||||
# else:
|
||||
# # Let JFJochCommunicationError propagate
|
||||
# result = self.__jfjoch.wait_till_done(60)
|
||||
self.__set_state(BeamlineStateEnum.SampleAlignment)
|
||||
if self.sample is not None and self.sample.db_id is not None:
|
||||
self.save_screenshot_db(self.sample.db_id, "scan_preview")
|
||||
try:
|
||||
self.__aare.sample_collected(self.sample)
|
||||
self.__aare.ingest_scan(sample=self.sample, result=result.result,
|
||||
geom=self.sample_geometry, beam_mark_pxl=self.__cfg.get_beam_mark(self.zoom))
|
||||
|
||||
# if self.sample is not None and self.sample.db_id is not None:
|
||||
# self.save_screenshot_db(self.sample.db_id, "after_dc")
|
||||
# try:
|
||||
# self.__aare.ingest_scan(sample=self.sample, result=result,
|
||||
# geom=self.sample_geometry, beam_mark_pxl=self.__cfg.get_beam_mark(self.zoom))
|
||||
#
|
||||
# except Exception as e:
|
||||
# logger.error(f"Exception ingesting scan: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Exception ingesting scan: {e}")
|
||||
except JFJochCommunicationError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Exception during rotation scan: {e}")
|
||||
raise
|
||||
|
||||
return CompletedRotationScan(request=copy.deepcopy(request), result=result)
|
||||
return result
|
||||
|
||||
def measure_rotation(self, request: RotationScanRequest) -> CompletedRotationScan:
|
||||
"""
|
||||
@@ -1162,13 +1198,16 @@ class AareDAQ:
|
||||
self.__cfg.state_busy = False
|
||||
raise
|
||||
|
||||
def face_detection(self, steps: int = 14, step_size: int = 15) -> dict:
|
||||
def face_detection(self, steps: int = 14, step_size: int = 15, face_min_ratio: float =0.3) -> dict:
|
||||
"""
|
||||
Perform a face detection sequence by rotating the sample and using ML to find the flat face.
|
||||
|
||||
Args:
|
||||
steps: Number of rotation steps.
|
||||
step_size: Size of each rotation step in degrees.
|
||||
steps: Number of rotation steps. Default is 14.
|
||||
step_size: Size of each rotation step in degrees. Default is 15.
|
||||
face_min_ratio: Minimum ratio of loopface count to loop_all count to use loop_face over loop_all.
|
||||
i.e. if 10 loop_face vs 4 loop_all pick loop_face. if 2 loop_face and 12 loop_all use loop_all.
|
||||
Default is 0.3.
|
||||
|
||||
Returns:
|
||||
Dictionary containing face detection results, including found samples and fits.
|
||||
@@ -1176,7 +1215,7 @@ class AareDAQ:
|
||||
self.__cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
logger.info("running face detection sequence")
|
||||
result = self.__face_detection_sequence(steps=steps, step_size=step_size)
|
||||
result = self.__face_detection_sequence(steps=steps, step_size=step_size,face_min_ratio=face_min_ratio)
|
||||
except Exception as e:
|
||||
logger.error(f"error in face detection sequence {e}")
|
||||
result = {
|
||||
@@ -1208,7 +1247,7 @@ class AareDAQ:
|
||||
|
||||
return
|
||||
|
||||
def __face_detection_sequence(self, steps: int = 14, step_size: int = 15) -> dict:
|
||||
def __face_detection_sequence(self, steps: int = 14, step_size: int = 15, face_min_ratio: float = 0.3) -> dict:
|
||||
self.__set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self.__devs.lamp_light = 2.5
|
||||
self.__cfg.zoom_mode = ZoomModeEnum.LoopCenter
|
||||
@@ -1219,7 +1258,8 @@ class AareDAQ:
|
||||
|
||||
self.__devs.set_zoom(zoom_value, wait=True)
|
||||
|
||||
boxes: dict[int, tuple[float, float, float, float]] = {}
|
||||
boxes_face: dict[int, tuple[float, float, float, float]] = {}
|
||||
boxes_loop: dict[int, tuple[float, float, float, float]] = {}
|
||||
curr_angle = int(self.__devs.aerotech_omega)
|
||||
total_range = steps * step_size + 1
|
||||
start_angle = curr_angle if curr_angle + total_range < 720 else 0
|
||||
@@ -1232,6 +1272,8 @@ class AareDAQ:
|
||||
logger.info(f"time to rotate 15 degrees: {time.perf_counter() - rotate_time}")
|
||||
|
||||
curr_image = cv2.cvtColor(self.camera_image, cv2.COLOR_RGB2BGR)
|
||||
if self.sample is not None and self.sample.db_id is not None:
|
||||
self.save_screenshot_db(self.sample.db_id, f"fd_{self.sample.db_id}_{angle}deg")
|
||||
box_time = time.perf_counter()
|
||||
m = self.__mlbox.predict(curr_image, filename=None, preferred_class=(3, 0))
|
||||
logger.info(f"time to predict: {time.perf_counter() - box_time}")
|
||||
@@ -1241,7 +1283,7 @@ class AareDAQ:
|
||||
self._emit_face_detection_progress({
|
||||
"running": True,
|
||||
"current_angle_deg": angle,
|
||||
"samples": fd.get_samples_out(boxes),
|
||||
"samples": fd.get_samples_out(boxes_face),
|
||||
"height_fit": {},
|
||||
"area_fit": {},
|
||||
})
|
||||
@@ -1253,25 +1295,42 @@ class AareDAQ:
|
||||
self.face_detection_centre_correction(m, tolerance=0.2)
|
||||
|
||||
if cls_id == 3:
|
||||
boxes[angle] = (x1, y1, x2, y2)
|
||||
boxes_face[angle] = (x1, y1, x2, y2)
|
||||
logger.info(f"accepted box at angle {angle}, cls={cls_id}, box={(x1, y1, x2, y2)}")
|
||||
elif cls_id == 0:
|
||||
boxes_loop[angle] = (x1, y1, x2, y2)
|
||||
logger.info(f"accepted box at angle {angle}, cls={cls_id}, box={(x1, y1, x2, y2)}")
|
||||
else:
|
||||
logger.debug(f"ignoring class {cls_id} (pin/crystal) at angle {angle}")
|
||||
logger.debug(f"ignoring class {cls_id} at angle {angle}")
|
||||
|
||||
self._emit_face_detection_progress({
|
||||
"running": True,
|
||||
"current_angle_deg": angle,
|
||||
"samples": fd.get_samples_out(boxes),
|
||||
"samples": fd.get_samples_out(boxes_face),
|
||||
"height_fit": {},
|
||||
"area_fit": {},
|
||||
})
|
||||
|
||||
if not boxes:
|
||||
if not boxes_face and not boxes_loop:
|
||||
logger.info("no boxes found")
|
||||
result = {"running": False, "samples": [], "height_fit": {}, "area_fit": {}}
|
||||
self._emit_face_detection_progress(result)
|
||||
return result
|
||||
|
||||
total_detections = len(boxes_face) + len(boxes_loop)
|
||||
face_ratio = len(boxes_face) / total_detections if total_detections > 0 else 0.0
|
||||
|
||||
if boxes_face and face_ratio >= face_min_ratio:
|
||||
boxes = boxes_face
|
||||
logger.info(f"using loop_face boxes ({len(boxes_face)}/{total_detections}, ratio={face_ratio:.2f})")
|
||||
elif boxes_loop:
|
||||
boxes = boxes_loop
|
||||
logger.info(
|
||||
f"falling back to loop_all boxes ({len(boxes_loop)}/{total_detections}, ratio={1 - face_ratio:.2f})")
|
||||
else:
|
||||
boxes = boxes_face
|
||||
logger.info(f"using loop_face boxes (only source, {len(boxes_face)} entries)")
|
||||
|
||||
best_fit_angle_area, area_params = fd.get_flat_face(boxes, start_angle, end_angle, True)
|
||||
best_fit_angle_height, height_params = fd.get_flat_face(boxes, start_angle, end_angle, False)
|
||||
fit_results = {
|
||||
@@ -1351,6 +1410,7 @@ class AareDAQ:
|
||||
filename = f"{sample_id}_{angle}_{zoom_value:.0f}" if sample_id is not None else None
|
||||
|
||||
try:
|
||||
self.save_screenshot(filename=f'{sample_id}_{angle}')
|
||||
target, cls, classes = self.__ml_loop_centre_box(sample_id, filename)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting ML box for angle {angle}")
|
||||
@@ -1403,6 +1463,7 @@ class AareDAQ:
|
||||
|
||||
#i += 1
|
||||
logger.debug("alc success")
|
||||
logger.debug(f"current sample: {self.__cfg.current_sample}, sample_id of scan: {sample_id}")
|
||||
self.__aare.sample_centered(self.__cfg.current_sample)
|
||||
if sample_id is not None:
|
||||
logger.info(f"sample {sample_id} centered")
|
||||
@@ -1486,7 +1547,8 @@ class AareDAQ:
|
||||
def save_screenshot(self, filename: str):
|
||||
#time.sleep(0.2) # Wait 200 ms to ensure camera image is stable
|
||||
bgr_image = cv2.cvtColor(self.camera_image, cv2.COLOR_RGB2BGR)
|
||||
#cv2.imwrite(f"{filename}.jpg", bgr_image)
|
||||
logger.debug(f"saving screenshot {filename}")
|
||||
cv2.imwrite(f"/sls/mx/applications/logs/{filename}.jpg", bgr_image)
|
||||
|
||||
def save_screenshot_db(self, sample_id: int, filename: str):
|
||||
"""
|
||||
@@ -1642,9 +1704,6 @@ class AareDAQ:
|
||||
start_mount=time.perf_counter()
|
||||
logger.info(f"starting mount {sample.db_id} at {time.ctime()}")
|
||||
self.__mount(sample)
|
||||
if sample.db_id is not None:
|
||||
self.__aare.sample_mounted(sample)
|
||||
self.save_screenshot_db(sample.db_id, f"{sample.db_id}_mounted")
|
||||
logger.info(f"mounting done at {time.perf_counter() - start_mount}, total time: {time.perf_counter() - start}")
|
||||
#self.__devs.smargon_pos
|
||||
#self.__devs.aerotech_pos =
|
||||
@@ -1695,12 +1754,9 @@ class AareDAQ:
|
||||
transmission=params.transmission,
|
||||
))
|
||||
logger.info(f"rotation done at {time.perf_counter() - start}")
|
||||
if sample.db_id is not None:
|
||||
self.__aare.sample_collected(sample)
|
||||
self.save_screenshot_db(sample.db_id, f"{sample.db_id}_collected")
|
||||
self.__cfg.state_busy = False
|
||||
end = time.perf_counter()
|
||||
return end - start
|
||||
self.__cfg.state_busy = False
|
||||
end = time.perf_counter()
|
||||
return end - start
|
||||
else:
|
||||
logger.error("auto center failed")
|
||||
self.__aare.axc_failed(sample)
|
||||
|
||||
Reference in New Issue
Block a user