def test_image_transformation(self): """Crop: Test that when an image is in an image goes out""" op = IptCrop() op.apply_test_values_overrides(use_cases=("Pre processing", )) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = op.process_wrapper(wrapper=wrapper) self.assertTrue(res, "Failed to process Crop") self.assertIsInstance(op.result, np.ndarray, "Empty result for Crop") """Crop: "Test that when using the basic mask generated script this tool extracts features""" op = IptCrop() op.apply_test_values_overrides(use_cases=("Feature extraction", )) script = LoosePipeline.load( "./ipso_phen/ipapi/samples/pipelines/test_extractors.json") script.add_module(operator=op, target_group="grp_test_extractors") wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = script.execute(src_image=wrapper, silent_mode=True) self.assertIsInstance( op, IptBaseAnalyzer, "Crop must inherit from ipso_phen.ipapi.iptBaseAnalyzer", ) self.assertTrue(res, "Failed to process Crop with test script") self.assertNotEqual( first=len(wrapper.csv_data_holder.data_list), second=0, msg="Crop returned no data", )
def test_mask_transformation(self): """Keep countours near ROIs: Test that when using the basic mask generated script this tool produces a mask""" op = IptKeepCountoursNearRois() op.apply_test_values_overrides(use_cases=("Mask cleanup",)) script = LoosePipeline.load( "./ipso_phen/ipapi/samples/pipelines/test_cleaners.json" ) script.add_module(operator=op, target_group="grp_test_cleaners") wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = script.execute(src_image=wrapper, silent_mode=True) self.assertTrue( res, "Failed to process Keep countours near ROIs with test script" ) self.assertIsInstance( wrapper.mask, np.ndarray, "Empty result for Range threshold" ) self.assertEqual(len(wrapper.mask.shape), 2, "Masks can only have one channel") self.assertEqual( np.sum(wrapper.mask[wrapper.mask != 255]), 0, "Masks values can only be 0 or 255", )
def test_feature_out(self): """Split overlapped ellipses: "Test that when using the basic mask generated script this tool extracts features""" op = IptSplitOverlappedEllipses() op.apply_test_values_overrides(use_cases=("Feature extraction", )) script = LoosePipeline.load( "./ipso_phen/ipapi/samples/pipelines/test_extractors.json") script.add_module(operator=op, target_group="grp_test_extractors") wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = script.execute(src_image=wrapper, silent_mode=True) self.assertIsInstance( op, IptBaseAnalyzer, "Split overlapped ellipses must inherit from ipso_phen.ipapi.iptBaseAnalyzer", ) self.assertTrue( res, "Failed to process Split overlapped ellipses with test script") self.assertNotEqual( first=len(wrapper.csv_data_holder.data_list), second=0, msg="Split overlapped ellipses returned no data", )
def test_extractors_pipeline(self): """Loose pipeline: Test extractors's test pipeline""" pipeline = LoosePipeline.load( os.path.join( self.pipeline_dir_path, "test_extractors.json", )) wrapper = BaseImageProcessor( os.path.join( os.path.dirname(__file__), "..", "ipso_phen", "ipapi", "samples", "images", "arabido_small.jpg", ), database=None, ) res = pipeline.execute(src_image=wrapper, silent_mode=True) self.assertTrue( res, "Failed to process Keep countours near ROIs with test pipeline") self.assertIsInstance(wrapper.mask, np.ndarray, "Empty result for Range threshold") self.assertEqual(len(wrapper.mask.shape), 2, "Masks can only have one channel") self.assertEqual( np.sum(wrapper.mask[wrapper.mask != 255]), 0, "Masks values can only be 0 or 255", )
def test_mask_transformation(self): """Clean horizontal noise (Hough method): Test that when using the basic mask generated script this tool produces a mask""" op = IptCleanHorizontalNoiseHough() op.apply_test_values_overrides(use_cases=("Mask cleanup", )) script = LoosePipeline.load( "./ipso_phen/ipapi/samples/pipelines/test_cleaners.json") script.add_module(operator=op, target_group="grp_test_cleaners") wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/18HP01U17-CAM11-20180712221558.bmp", database=None, ) res = script.execute(src_image=wrapper, silent_mode=True) self.assertTrue( res, "Failed to process Clean horizontal noise (Hough method) with test script", ) self.assertIsInstance(wrapper.mask, np.ndarray, "Empty result for Range threshold") self.assertEqual(len(wrapper.mask.shape), 2, "Masks can only have one channel") self.assertEqual( np.sum(wrapper.mask[wrapper.mask != 255]), 0, "Masks values can only be 0 or 255", )
def ipo_factory( file_path, options=None, force_abstract: bool = False, data_base=None, scale_factor=1, ): if force_abstract: return BaseImageProcessor( file_path, options, database=data_base, scale_factor=scale_factor, ) else: # Build unique class list ipt_classes_list = get_module_classes( package=class_pipelines, class_inherits_from=BaseImageProcessor, remove_abstract=True, ) # Create temporary image wrapper to detect experiment fh = file_handler_factory(file_path, data_base) # Select able class ipt_classes_list = list(set(ipt_classes_list)) for cls in ipt_classes_list: if callable(getattr(cls, "can_process", None)) and cls.can_process( dict(experiment=fh.experiment, robot=fh.__class__.__name__)): return cls( file_path, options, database=data_base, scale_factor=scale_factor, ) return BaseImageProcessor( file_path, options, database=data_base, scale_factor=scale_factor, )
def test_image_transformation(self): """Slic: Test that when an image is in an image goes out""" op = IptSlic() op.apply_test_values_overrides(use_cases=("Pre processing",)) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = op.process_wrapper(wrapper=wrapper) self.assertTrue(res, "Failed to process Slic") self.assertIsInstance(op.result, np.ndarray, "Empty result for Slic")
def test_visualization(self): """Print color spaces: Test that visualization tools add images to list""" op = IptPrintColorSpaces() op.apply_test_values_overrides(use_cases=("Visualization", )) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) wrapper.store_images = True res = op.process_wrapper(wrapper=wrapper) self.assertTrue(res, "Failed to process Simple white balance") self.assertGreater(len(wrapper.image_list), 0, "Visualizations must add images to list")
def test_image_transformation(self): """Horizontal line remover: Test that when an image is in an image goes out""" op = IptHorizontalLineDetector() op.apply_test_values_overrides(use_cases=("Pre processing",)) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/18HP01U17-CAM11-20180712221558.bmp", database=None, ) res = op.process_wrapper(wrapper=wrapper) self.assertTrue(res, "Failed to process Horizontal line remover") self.assertIsInstance( op.result, np.ndarray, "Empty result for Horizontal line remover" )
def test_bool_out(self): """Assert mask position: Test that tool returns a boolean""" op = IptAssertMaskPosition() op.apply_test_values_overrides(use_cases=("Assert...",)) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = op.process_wrapper(wrapper=wrapper) self.assertTrue(res, "Failed to process Assert mask position") self.assertIsInstance( op.result, bool, "Assert mask position must return a boolean" )
def test_roi_out(self): """Circle ROI: Test that tool generates an ROI""" op = IptCircleRoi() op.apply_test_values_overrides(use_cases=("Create an ROI", )) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = op.process_wrapper(wrapper=wrapper) self.assertTrue(hasattr(op, "generate_roi"), "Class must have method generate_roi") self.assertTrue(res, "Failed to process Circle ROI") r = op.generate_roi() self.assertIsInstance(r, regions.AbstractRegion, "ROI must be of type Region")
def _fix_source_image(self, img): if self.is_msp: # Fix brightness for darker images tmp_wrapper = BaseImageProcessor(self.file_path) with IptLinearTransformation( wrapper=tmp_wrapper, method="gamma_target", apply_case="if_under", target_brightness=75, max_delta_for_brightness=20, ) as (res, ed): if res: return ed.result else: return img
def test_mask_generation(self): """Otsu: Test that when an image is in a mask goes out""" op = IptOtsu() op.apply_test_values_overrides(use_cases=("Threshold", )) wrapper = BaseImageProcessor( "./ipso_phen/ipapi/samples/images/arabido_small.jpg", database=None, ) res = op.process_wrapper(wrapper=wrapper) self.assertTrue(res, "Failed to process Otsu") self.assertIsInstance(op.result, np.ndarray, "Empty result for Otsu") self.assertEqual(len(op.result.shape), 2, "Masks can only have one channel") self.assertEqual( np.sum(op.result[op.result != 255]), 0, "Masks values can only be 0 or 255", )
def main(): # Get the file # ____________ # Set working folder old_wd = os.getcwd() abspath = os.path.abspath(__file__) fld_name = os.path.dirname(abspath) os.chdir(fld_name) # Construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="Path to the image") ap.add_argument("-d", "--destination", required=False, help="Destination folder") ap.add_argument("-p", "--print_images", required=False, help="Print images, y or n") ap.add_argument("-m", "--print_mosaic", required=False, help="Print mosaic, y or n") args = vars(ap.parse_args()) file_name = args["image"] print_images = args.get("print_images", "n") == "y" print_mosaic = args.get("print_mosaic", "n") == "y" dst_folder = args.get("destination", "") # Restore working folder os.chdir(old_wd) # Build wrapper # _____________ wrapper = BaseImageProcessor(file_name) wrapper.lock = True wrapper.store_image(wrapper.current_image, "true_source_image") if print_images or print_mosaic: wrapper.store_images = True if print_images: wrapper.write_images = "plot" if print_mosaic: wrapper.write_mosaic = "plot" # Fix exposure # ____________________ wrapper.current_image = call_ipt( ipt_id="IptLinearTransformation", source=wrapper, return_type="result", method="alpha_beta_target", target_brightness=150, ) # Store image name for analysis wrapper.store_image(wrapper.current_image, "exposure_fixed") analysis_image = "exposure_fixed" if print_mosaic: wrapper.store_image(wrapper.current_image, "fixed_source") # Build static ROIs # _________________ roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="keep_roi", left=100, width=1885, top=300, height=1740, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="check_roi", roi_type="enforce", left=840, width=400, top=1640, height=400, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_type="other", left=100, width=1885, top=300, height=1740, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="open_pot_top", roi_type="open", left=710, width=660, top=1990, height=50, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="safe_roi", roi_type="safe", left=230, width=1550, top=350, height=1580, ) if roi is not None: wrapper.add_roi(new_roi=roi) # Pre process image (make segmentation easier) # ____________________________________________ wrapper.current_image = call_ipt( ipt_id="IptExposureChecker", source=wrapper, return_type="result", overexposed_limit=175, over_color="blue_cabin", underexposed_limit=35, under_color="blue_cabin", ) wrapper.current_image = call_ipt( ipt_id="IptPartialPosterizer", source=wrapper, return_type="result", blue_color="blue_cabin", post_blue_value=45, ) if print_mosaic: wrapper.store_image(wrapper.current_image, "pre_processed_image") # Build coarse masks # __________________ mask_list = [] current_mask_ = call_ipt(ipt_id="IptThreshold", source=wrapper, return_type="result", max_t=100) mask_list.append(current_mask_) current_mask_ = call_ipt( ipt_id="IptThreshold", source=wrapper, return_type="result", channel="b", min_t=125, ) mask_list.append(current_mask_) # Merge masks func = getattr(wrapper, "multi_and", None) if func: wrapper.mask = func([mask for mask in mask_list if mask is not None]) wrapper.store_image(wrapper.mask, f"mask_multi_and") if print_mosaic: wrapper.store_image(wrapper.mask, "coarse_mask") else: logger.error("Unable to merge coarse masks") return # ROIs to be applied after mask merging # _____________________________________ handled_rois = ["keep", "delete", "erode", "dilate", "open", "close"] rois_list = [ roi for roi in wrapper.rois_list if roi.tag in handled_rois and not (roi.target and roi.target != "none") ] wrapper.mask = wrapper.apply_roi_list(img=wrapper.mask, rois=rois_list, print_dbg=True) if print_mosaic: wrapper.store_image(wrapper.mask, "mask_after_roi") # Clean merged mask # _________________ wrapper.mask = call_ipt( ipt_id="IptKeepLinkedContours", source=wrapper, return_type="result", tolerance_distance=50, tolerance_area=100, ) if wrapper.mask is None: return if print_mosaic: wrapper.store_image(wrapper.mask, "clean_mask") # Check that the mask is where it belongs # _______________________________________ if print_images: res = True enforcers_list = wrapper.get_rois({"enforce"}) for i, enforcer in enumerate(enforcers_list): mask = wrapper.mask.copy() mask = wrapper.keep_roi(mask, enforcer) partial_ok = np.count_nonzero(mask) > 0 res = partial_ok and res if partial_ok: roi_img = np.dstack( (np.zeros_like(mask), mask, np.zeros_like(mask))) else: roi_img = np.dstack( (np.zeros_like(mask), np.zeros_like(mask), mask)) background_img = cv2.bitwise_and(wrapper.mask, wrapper.mask, mask=255 - mask) img = cv2.bitwise_or( roi_img, np.dstack((background_img, background_img, background_img))) enforcer.draw_to(img, line_width=4) wrapper.store_image(img, f"enforcer_{i}_{enforcer.name}") if not res: return else: enforcers_list = wrapper.get_rois({"enforce"}) for i, enforcer in enumerate(enforcers_list): mask = wrapper.mask.copy() mask = wrapper.keep_roi(mask, enforcer) if np.count_nonzero(mask) == 0: return # Extract features # ________________ wrapper.current_image = wrapper.retrieve_stored_image("exposure_fixed") wrapper.csv_data_holder = AbstractCsvWriter() current_data = call_ipt(ipt_id="IptAnalyseObservation", source=wrapper, return_type="data") if isinstance(current_data, dict): wrapper.csv_data_holder.data_list.update(current_data) else: logger.error("Failed to add extracted data") current_data = call_ipt(ipt_id="IptAnalyzeChlorophyll", source=wrapper, return_type="data") if isinstance(current_data, dict): wrapper.csv_data_holder.data_list.update(current_data) else: logger.error("Failed to add extracted data") current_data = call_ipt(ipt_id="IptAnalyzeColor", source=wrapper, return_type="data") if isinstance(current_data, dict): wrapper.csv_data_holder.data_list.update(current_data) else: logger.error("Failed to add extracted data") current_data = call_ipt(ipt_id="IptAnalyzeObject", source=wrapper, return_type="data") if isinstance(current_data, dict): wrapper.csv_data_holder.data_list.update(current_data) else: logger.error("Failed to add extracted data") # Save CSV if dst_folder and (len(wrapper.csv_data_holder.data_list) > 0): with open( os.path.join(dst_folder, "", wrapper.file_handler.file_name_no_ext + ".csv"), "w", newline="", ) as csv_file_: wr = csv.writer(csv_file_, quoting=csv.QUOTE_NONE) wr.writerow(wrapper.csv_data_holder.header_to_list()) wr.writerow(wrapper.csv_data_holder.data_to_list()) # Build mosaic # ____________ if print_mosaic: wrapper.store_mosaic = "result" wrapper.mosaic_data = np.array([ ["fixed_source", "pre_processed_image", "coarse_mask"], [ "mask_after_roi", "clean_mask", wrapper.draw_image( src_image=wrapper.current_image, src_mask=wrapper.mask, background="bw", foreground="source", bck_grd_luma=120, contour_thickness=6, hull_thickness=6, width_thickness=6, height_thickness=6, centroid_width=20, centroid_line_width=8, ), ], ]) wrapper.print_mosaic(padding=4) print("Done.")
def build_single_plant_video(args): plant, dst_folder_, db, dates_, experiment_, angles_ = args p_output = os.path.join(dst_folder_, f"{plant}.mp4") if os.path.isfile(p_output): return f"Plant {plant} already handled" ret = db.query( command="SELECT", columns="filepath", additional="ORDER BY date_time ASC", date=dict(operator="IN", values=dates_), experiment=experiment_, plant=plant, angle=angles_[0], ) main_angle_image_list_ = [item[0] for item in ret] fourcc = cv2.VideoWriter_fourcc(*"mp4v") out = cv2.VideoWriter(p_output, fourcc, 24.0, (video_width, video_height)) fnt = cv2.FONT_HERSHEY_DUPLEX for main_angle_image_ in main_angle_image_list_: main_angle_wrapper_side = BaseImageProcessor( main_angle_image_, db, ) try: img_main_angle = build_image(main_angle_wrapper_side) if img_main_angle is None: continue main_angle_wrapper_side.store_image(image=img_main_angle, text=angles_[0], force_store=True) except Exception as e: print( f'Exception "{repr(e)}" while handling {str(main_angle_wrapper_side)}' ) current_date_time = main_angle_wrapper_side.date_time for secondary_angle in angles_[1:]: secondary_angle_image_ = db.query_one( command="SELECT", columns="filepath", additional="ORDER BY date_time ASC", experiment=experiment_, plant=plant, angle=secondary_angle, date_time=dict( operator="BETWEEN", date_min=current_date_time - datetime.timedelta(hours=1), date_max=current_date_time + datetime.timedelta(hours=1), ), ) if secondary_angle_image_: secondary_angle_image_ = secondary_angle_image_[0] if secondary_angle_image_ and os.path.isfile( secondary_angle_image_): secondary_angle_wrapper = BaseImageProcessor( secondary_angle_image_) try: secondary_angle_img = build_image(secondary_angle_wrapper) main_angle_wrapper_side.store_image( image=secondary_angle_img, text=secondary_angle, force_store=True) except Exception as e: print( f'Exception "{repr(e)}" while handling {str(secondary_angle_wrapper)}' ) mosaic = main_angle_wrapper_side.build_mosaic( (video_height, video_width, 3), angles_) cv2.putText( mosaic, current_date_time.strftime("%d/%m/%Y - %H:%M:%S"), (10, 1000), fnt, 1, (255, 0, 255), 2, cv2.LINE_AA, ) # cv2.imwrite( # os.path.join(os.path.dirname(p_output), main_angle_wrapper_side.luid + ".jpg"), mosaic # ) def write_image_times(out_writer, img_, times=24): for _ in range(0, times): out_writer.write(img_) # Print source image write_image_times(out, mosaic) # Release everything if job is finished out.release() cv2.destroyAllWindows() return None
def build_sbs_video(): p_output = os.path.join(dst_folder, f'output_{"_".join(plants)}_.mp4') if os.path.isfile(p_output): return f"Plants {'_'.join(plants)} already handled" ret = current_database.query( command="SELECT", columns="filepath", additional="ORDER BY date_time ASC", date=dict(operator="IN", values=dates), experiment=experiment, plant=plants[0], angle=angle, ) file_list_ = [item[0] for item in ret] fourcc = cv2.VideoWriter_fourcc(*"mp4v") out = cv2.VideoWriter(p_output, fourcc, 24.0, (video_width, video_height)) fnt = cv2.FONT_HERSHEY_DUPLEX total = len(file_list_) current_progress = st.progress(0) time_counter = 1 for i, source_vis_plant in enumerate(file_list_): if not (source_vis_plant and os.path.isfile(source_vis_plant)): continue # Handle first image main_wrapper = BaseImageProcessor(source_vis_plant) try: img_main = build_image(main_wrapper) main_wrapper.store_image(image=img_main, text=plants[0], force_store=True) except Exception as e: print( f'Exception "{repr(e)}" while handling {str(source_vis_plant)}' ) current_date_time = main_wrapper.date_time # Handle the rest has_missing_ = False for counter, ancillary_plant in enumerate(plants[1:]): file_name_ = current_database.query_one( command="SELECT", columns="filepath", additional="ORDER BY date_time ASC", experiment=experiment, plant=ancillary_plant, angle=angle, date_time=dict( operator="BETWEEN", date_min=current_date_time - datetime.timedelta(hours=10), date_max=current_date_time + datetime.timedelta(hours=10), ), ) if file_name_: file_name_ = file_name_[0] if file_name_ and os.path.isfile(file_name_): try: main_wrapper.store_image( image=build_image(BaseImageProcessor(file_name_)), text=plants[counter + 1], force_store=True, ) except Exception as e: print( f'Exception "{repr(e)}" while handling {str(file_name_)}' ) else: has_missing_ = True mosaic = main_wrapper.build_mosaic((video_height, video_width, 3), plants) # cv2.imwrite( # os.path.join(os.path.dirname(p_output), f"{main_wrapper.plant}_{time_counter}.jpg"), # mosaic, # ) time_counter += 1 def write_image_times(out_writer, img_, times=12): for _ in range(0, times): out_writer.write(img_) # Print source image write_image_times(out, mosaic) current_progress.progress((i + 1) / total) # Release everything if job is finished out.release() cv2.destroyAllWindows()
def main(): # Get the file # ____________ # Set working folder old_wd = os.getcwd() abspath = os.path.abspath(__file__) fld_name = os.path.dirname(abspath) os.chdir(fld_name) # Construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", required=True, help="Path to the image") ap.add_argument("-d", "--destination", required=False, help="Destination folder") ap.add_argument("-p", "--print_images", required=False, help="Print images, y or n") ap.add_argument("-m", "--print_mosaic", required=False, help="Print mosaic, y or n") args = vars(ap.parse_args()) file_name = args["image"] print_images = args.get("print_images", "n") == "y" print_mosaic = args.get("print_mosaic", "n") == "y" dst_folder = args.get("destination", "") # Restore working folder os.chdir(old_wd) # Build wrapper # _____________ wrapper = BaseImageProcessor(file_name) wrapper.lock = True wrapper.store_image(wrapper.current_image, "true_source_image") if print_images or print_mosaic: wrapper.store_images = True if print_images: wrapper.write_images = "plot" if print_mosaic: wrapper.write_mosaic = "plot" # Fix exposure # ____________________ wrapper.current_image = call_ipt( ipt_id="IptLinearTransformation", source=wrapper, return_type="result", method="gamma_target", target_brightness=145, text_overlay=1, ) # Store image name for analysis wrapper.store_image(wrapper.current_image, "exposure_fixed") analysis_image = "exposure_fixed" if print_mosaic: wrapper.store_image(wrapper.current_image, "fixed_source") # Build static ROIs # _________________ roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="check_exp_pot_top", roi_type="other", tool_target="IptExposureChecker", left=394, width=1317, top=1990, height=70, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="check_exp_pot_bottom", roi_type="other", tool_target="IptExposureChecker", left=394, width=1317, top=2049, height=320, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="check_exp_top", roi_type="other", tool_target="IptExposureChecker", left=1583, width=461, height=90, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="open_pot_top", roi_type="open", left=500, width=1100, top=1940, height=200, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="erode_pot_bottom", roi_type="erode", left=500, width=1100, top=2140, height=240, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="keep_roi", left=200, width=1600, height=2350, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="erode_top_right", roi_type="erode", left=1606, width=279, top=2, height=376, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="open_left_leg", roi_type="open", left=527, width=140, top=2050, height=381, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="open_right_leg", roi_type="open", left=1390, width=140, top=2050, height=381, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="split_threshold_inside_bottom", tool_target="IptSplittedRangeThreshold", left=489, width=1071, top=2019, height=427, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="split_threshold_inside_top_right", tool_target="IptSplittedRangeThreshold", left=1593, width=453, height=370, ) if roi is not None: wrapper.add_roi(new_roi=roi) roi = call_ipt_func( ipt_id="IptRoiManager", source=wrapper, function_name="generate_roi", roi_name="split_threshold_inside_right", tool_target="IptSplittedRangeThreshold", left=1920, width=125, top=351, height=2088, ) if roi is not None: wrapper.add_roi(new_roi=roi) # Pre process image (make segmentation easier) # ____________________________________________ wrapper.current_image = call_ipt( ipt_id="IptPartialPosterizer", source=wrapper, return_type="result", blue_color="black", post_blue_value=41, ) wrapper.current_image = call_ipt( ipt_id="IptExposureChecker", source=wrapper, return_type="result", overexposed_limit=200, over_color="black", underexposed_limit=40, under_color="black", show_grey_zones=1, grey_zone_limit=2, grey_zone_color="black", roi_names="check_exp_pot_top,check_exp_top", ) wrapper.current_image = call_ipt( ipt_id="IptExposureChecker", source=wrapper, return_type="result", overexposed_limit=180, over_color="black", underexposed_limit=20, under_color="black", show_grey_zones=1, grey_zone_limit=6, grey_zone_color="black", roi_names="check_exp_pot_bottom", ) if print_mosaic: wrapper.store_image(wrapper.current_image, "pre_processed_image") # Build coarse masks # __________________ mask_list = [] current_mask_ = call_ipt( ipt_id="IptThreshold", source=wrapper, return_type="result", channel="l", min_t=15, ) mask_list.append(current_mask_) current_mask_ = call_ipt( ipt_id="IptSplittedRangeThreshold", source=wrapper, return_type="result", channel="b", roi_names="split_threshold_inside_bottom,split_threshold_inside_top_right,split_threshold_inside_right", min_inside_t=135, min_outside_t=120, kernel_size=4, build_mosaic=1, ) mask_list.append(current_mask_) # Merge masks func = getattr(wrapper, "multi_and", None) if func: wrapper.mask = func([mask for mask in mask_list if mask is not None]) wrapper.store_image(wrapper.mask, f"mask_multi_and") if print_mosaic: wrapper.store_image(wrapper.mask, "coarse_mask") else: wrapper.error_holder.add_error("Unable to merge coarse masks") return # ROIs to be applied after mask merging # _____________________________________ handled_rois = ["keep", "delete", "erode", "dilate", "open", "close"] rois_list = [ roi for roi in wrapper.rois_list if roi.tag in handled_rois and not (roi.target and roi.target != "none") ] wrapper.mask = wrapper.apply_roi_list( img=wrapper.mask, rois=rois_list, print_dbg=True ) if print_mosaic: wrapper.store_image(wrapper.mask, "mask_after_roi") # Clean merged mask # _________________ wrapper.mask = call_ipt( ipt_id="IptKeepLinkedContours", source=wrapper, return_type="result", tolerance_distance=50, tolerance_area=500, root_position="MIDDLE_CENTER", ) if wrapper.mask is None: return if print_mosaic: wrapper.store_image(wrapper.mask, "clean_mask") # Check that the mask is where it belongs # _______________________________________ mask = None if print_images: res = True enforcers_list = wrapper.get_rois({"enforce"}) for i, enforcer in enumerate(enforcers_list): mask = wrapper.mask.copy() mask = wrapper.keep_roi(mask, enforcer) partial_ok = np.count_nonzero(mask) > 0 res = partial_ok and res if partial_ok: roi_img = np.dstack((np.zeros_like(mask), mask, np.zeros_like(mask))) else: roi_img = np.dstack((np.zeros_like(mask), np.zeros_like(mask), mask)) background_img = cv2.bitwise_and( wrapper.mask, wrapper.mask, mask=255 - mask ) img = cv2.bitwise_or( roi_img, np.dstack((background_img, background_img, background_img)) ) enforcer.draw_to(img, line_width=4) wrapper.store_image(img, f"enforcer_{i}_{enforcer.name}") if not res: return else: enforcers_list = wrapper.get_rois({"enforce"}) for i, enforcer in enumerate(enforcers_list): mask = wrapper.mask.copy() mask = wrapper.keep_roi(mask, enforcer) if np.count_nonzero(mask) == 0: return # Print selection as color on bw background # ____________________________________________ id_objects, obj_hierarchy = ipc.get_contours_and_hierarchy( mask=mask, retrieve_mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE ) wrapper.object_composition(wrapper.current_image, id_objects, obj_hierarchy) # Build mosaic # ____________ if print_mosaic: wrapper.store_mosaic = "result" wrapper.mosaic_data = np.array( [ ["fixed_source", "pre_processed_image", "coarse_mask"], [ "mask_after_roi", "clean_mask", wrapper.draw_image( src_image=wrapper.current_image, src_mask=wrapper.mask, background="bw", foreground="source", bck_grd_luma=120, contour_thickness=6, hull_thickness=6, width_thickness=6, height_thickness=6, centroid_width=20, centroid_line_width=8, ), ], ] ) wrapper.print_mosaic(padding=4) print("Done.")