def process_wrapper(self, **kwargs): """ Contrast Limited Adaptive Histogram Equalization (CLAHE) Equalizes image using multiple histograms Real time : Yes Keyword Arguments (in parentheses, argument name): * Color space (color_space): Selected color space, default: RGB * Median filter size (median_filter_size): if >0 a median filter will be applied before, odd values only * Clip limit (clip_limit): Local histogram clipping limit * Tile grid size (tile_grid_size): Image partition size * Overlay text on top of images (text_overlay): Draw description text on top of images """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False clip_limit = self.get_value_of("clip_limit") tile_grid_size = self.get_value_of("tile_grid_size") color_space = self.get_value_of("color_space") median_filter_size = self.get_value_of("median_filter_size") text_overlay = self.get_value_of("text_overlay") == 1 res = False try: median_filter_size = (0 if median_filter_size == 1 else ensure_odd(median_filter_size)) if median_filter_size > 0: src_img = cv2.medianBlur(wrapper.current_image, median_filter_size) else: src_img = wrapper.current_image self.result = wrapper.apply_CLAHE( src_img, color_space=color_space, clip_limit=(clip_limit, clip_limit, clip_limit), tile_grid_size=(tile_grid_size, tile_grid_size), ) wrapper.store_image( self.result, f"CLAHE_{self.input_params_as_str()}", text_overlay=text_overlay, ) except Exception as e: res = False logger.error(f'Failed to process {self. name}: "{repr(e)}"') else: res = True finally: return res
def process_wrapper(self, **kwargs): """ Gaussian filtering: 'Apply Gaussian filter Real time: True Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Source (source): * Gaussian filter size (odd values only) (kernel_size):""" wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: kernel_size = ensure_odd(self.get_value_of("kernel_size")) img = wrapper.current_image if self.output_type == ipc.IO_MASK: mask = self.get_mask() if mask is None: logger.error( "Failure Match image and mask resolution: mask must be initialized" ) return else: mask = None self.result = cv2.GaussianBlur( src=img if self.output_type == ipc.IO_IMAGE else mask, ksize=(kernel_size, kernel_size), sigmaX=(kernel_size - 1) / 6, sigmaY=(kernel_size - 1) / 6, ) if self.output_type == ipc.IO_MASK: self.result[self.result != 0] = 255 wrapper.store_image(self.result, "current_image") res = True else: wrapper.store_image(wrapper.current_image, "current_image") res = True except Exception as e: res = False logger.error(f"Median Fileter FAILED, exception: {repr(e)}") else: pass finally: return res
def process_wrapper(self, **kwargs): # Copy here the docstring generated by IPSO Phen wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: img = wrapper.current_image threshold = self.get_value_of("threshold") max_value = self.get_value_of("max_value") median_filter_size = self.get_value_of("median_filter_size") median_filter_size = (0 if median_filter_size == 1 else ipc.ensure_odd(median_filter_size)) c = wrapper.get_channel(img, self.get_value_of("channel"), "", [], False, median_filter_size) self.result = pcv.threshold.binary( c, threshold, max_value, self.get_value_of("object_type")) # Write your code here wrapper.store_image(self.result, "current_image") res = True else: wrapper.store_image(wrapper.current_image, "current_image") res = True except Exception as e: res = False wrapper.error_holder.add_error( new_error_text=f'Failed to process {self. name}: "{repr(e)}"', new_error_level=35, target_logger=logger, ) else: pass finally: return res
def process_wrapper(self, **kwargs): """ Median Filter: 'Apply median filter Real time: True Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Median filter size (odd values only) (median_filter_size): """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: median_filter_size = self.get_value_of("median_filter_size") self.result = cv2.medianBlur( wrapper.current_image, ensure_odd(median_filter_size), ) wrapper.store_image(self.result, "current_image") res = True else: wrapper.store_image(wrapper.current_image, "current_image") res = True except Exception as e: res = False logger.error(f"Median Fileter FAILED, exception: {repr(e)}") else: pass finally: return res
def process_wrapper(self, **kwargs): """ Print channels: Real time : True Keyword Arguments (in parentheses, argument name): * Channel (channel): * Normalize channel (normalize): * Median filter size (odd values only) (median_filter_size): * Overlay text on top of images (text_overlay): Draw description text on top of images """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: median_filter_size = self.get_value_of("median_filter_size") channel = self.get_value_of("channel") text_overlay = self.get_value_of("text_overlay") == 1 normalize = self.get_value_of("normalize") == 1 if self.get_value_of("print_mosaic") == 1: if wrapper.file_handler.is_msp: _, c = wrapper.build_msp_mosaic( normalize=normalize, median_filter_size=median_filter_size, ) else: _, c = wrapper.build_channels_mosaic( src_img=wrapper.current_image, normalize=normalize, median_filter_size=median_filter_size, ) wrapper.store_image( c, "channel_mosaic", text_overlay=text_overlay, ) else: median_filter_size = ( 0 if median_filter_size == 1 else ensure_odd(median_filter_size) ) c = wrapper.get_channel( channel=channel, median_filter_size=median_filter_size, normalize=normalize, ) wrapper.store_image( c, f"channel_{self.input_params_as_str()}", text_overlay=text_overlay, ) self.result = c except Exception as e: res = False wrapper.error_holder.add_error( new_error_text=f'Failed to process {self. name}: "{repr(e)}"', new_error_level=35, target_logger=logger, ) else: res = True finally: return res
def process_wrapper(self, **kwargs): """ Keep countours near ROIs: Keep big contours inside a series of ROIs. Small contours inside ROIs may be added on conditions. Contours outsside ROIs may be added to root contours if close enough Real time: False Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Name of ROI to be used (roi_names): Operation will only be applied inside of ROI * ROI selection mode (roi_selection_mode): * Maximum distance to ROI (init_max_distance): Maximum distance to add a contour on initialization phase. If distance is >0, ROIs will dilated with a kernel of size distance. * Minimum contour size (init_min_size): Minimum accepted size for a contour on initialization phase * Delete all contours smaller than (delete_all_bellow): All contours below this size will be permanently ignored. The more smaller contours are delete, the faster the algorithm * Merge distance for root contours (root_merge_distance): * Aggregate small contours inside ROIs distance (small_contours_distance_tolerance): Aggregate small contours inside ROIs if closer than x to any root contour. Any aggregated contour is considered as a root one. * Aggregate unknown contours distance (unk_contours_distance_tolerance): Aggregate unknown contours if closer than x to any root contour. Any aggregated contour is considered as a root one. """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: img = wrapper.current_image mask = self.get_mask() if mask is None: logger.error(f"FAIL {self.name}: mask must be initialized") return # Get ROIs as mask rois = self.get_ipt_roi( wrapper=wrapper, roi_names=self.get_value_of("roi_names").replace( " ", "").split(","), selection_mode=self.get_value_of("roi_selection_mode"), ) if rois: rois_mask = np.zeros_like(mask) for roi in rois: rois_mask = roi.draw_to(dst_img=rois_mask, line_width=-1, color=255) else: self.result = mask logger.error( f"Warning {self.name}: must have at least one ROI") res = True return wrapper.store_image(rois_mask, "rois_as_mask") # Get source contours contours = ipc.get_contours( mask=mask, retrieve_mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE, ) # Dilate ROIs init_max_distance = self.get_value_of("init_max_distance") if init_max_distance > 0: rois_mask = wrapper.dilate( image=rois_mask, kernel_size=ipc.ensure_odd(i=init_max_distance, min_val=3), ) wrapper.store_image(rois_mask, "dilated_rois") # Remove smaller contours delete_all_bellow = self.get_value_of("delete_all_bellow") if delete_all_bellow > 0: fnt = (cv2.FONT_HERSHEY_SIMPLEX, 0.6) small_img = mask.copy() small_img = np.dstack((small_img, small_img, small_img)) for cnt in contours: area_ = cv2.contourArea(cnt) if area_ < delete_all_bellow: # Delete cv2.drawContours(mask, [cnt], 0, (0, 0, 0), -1) # Print debug image x, y, w, h = cv2.boundingRect(cnt) x += w // 2 - 10 y += h // 2 cv2.drawContours(small_img, [cnt], -1, ipc.C_RED, -1) cv2.putText( small_img, f"Area: {area_}", (x, y), fnt[0], fnt[1], ipc.C_FUCHSIA, 2, ) else: cv2.drawContours(small_img, [cnt], -1, ipc.C_GREEN, -1) wrapper.store_image(small_img, "small_removed_mask") # Find root contours root_cnts = [] small_cnts = [] unk_cnts = [] init_min_size = self.get_value_of("init_min_size") tmp_img = np.zeros_like(mask) for i, cnt in enumerate(contours): cv2.drawContours( image=tmp_img, contours=[cnt], contourIdx=0, color=255, thickness=-1, ) intersection = cv2.bitwise_and(tmp_img, rois_mask) cv2.drawContours( image=tmp_img, contours=[cnt], contourIdx=0, color=0, thickness=-1, ) if np.sum(intersection[intersection != 0]) > 0: if cv2.contourArea(cnt) > init_min_size: root_cnts.append(cnt) else: small_cnts.append(cnt) else: unk_cnts.append(cnt) # approx_eps = self.get_value_of("approx_eps") / 10000 cnt_approx = { "root": [{ "cnt": cnt, "label": i } for i, cnt in enumerate(root_cnts)], "small": [{ "cnt": cnt, "label": i + len(root_cnts) } for i, cnt in enumerate(small_cnts)], "unk": [{ "cnt": cnt, "label": i + len(root_cnts) + len(small_cnts) } for i, cnt in enumerate(unk_cnts)], "discarded": [], } cnt_data = [ { "name": "root", "start_color": (0, 50, 0), "stop_color": (0, 255, 0), "print_label": True, }, { "name": "small", "start_color": (50, 0, 0), "stop_color": (255, 0, 0), "print_label": False, }, { "name": "unk", "start_color": (0, 0, 50), "stop_color": (0, 0, 255), "print_label": False, }, ] bck_img = wrapper.current_image for roi in rois: bck_img = roi.draw_to(dst_img=bck_img, line_width=2, color=ipc.C_WHITE) self.draw_contours( canvas=bck_img, contours=cnt_approx, image_name="contours_after_init", contours_data=cnt_data, ) # Merge root contours by label root_merge_distance = self.get_value_of("root_merge_distance") stable = False step = 1 while not stable and step < 100: stable = True for left_index, left in enumerate(cnt_approx["root"]): for right_index, right in enumerate( cnt_approx["root"]): if left_index == right_index: continue if (left["label"] != right["label"] and wrapper.contours_min_distance( left["cnt"], right["cnt"]) < root_merge_distance): right["label"] = left["label"] stable = False self.draw_contours( canvas=wrapper.current_image, contours=cnt_approx, image_name=f"merging_root_{step}", contours_data=cnt_data, ) step += 1 self.draw_contours( canvas=wrapper.current_image, contours=cnt_approx, image_name="root_labels_merged", contours_data=cnt_data, ) # Merge small contours small_contours_distance_tolerance = self.get_value_of( "small_contours_distance_tolerance") stable = False step = 1 while not stable and step < 100: stable = True for root in cnt_approx["root"]: i = 0 while i < len(cnt_approx["small"]): small = cnt_approx["small"][i] if (wrapper.contours_min_distance( root["cnt"], small["cnt"]) < small_contours_distance_tolerance): new_root = cnt_approx["small"].pop(i) new_root["label"] = root["label"] cnt_approx["root"].append(new_root) stable = False else: i += 1 if not stable: self.draw_contours( canvas=wrapper.current_image, contours=cnt_approx, image_name=f"merging_small_{step}", contours_data=cnt_data, ) step += 1 self.draw_contours( canvas=wrapper.current_image, contours=cnt_approx, image_name="small_labels_merged", contours_data=cnt_data, ) # Merge unknown contours unk_contours_distance_tolerance = self.get_value_of( "unk_contours_distance_tolerance") stable = False step = 1 while not stable and step < 100: stable = True for root in cnt_approx["root"]: i = 0 while i < len(cnt_approx["unk"]): unk = cnt_approx["unk"][i] if (wrapper.contours_min_distance( root["cnt"], unk["cnt"]) < unk_contours_distance_tolerance): new_root = cnt_approx["unk"].pop(i) new_root["label"] = root["label"] cnt_approx["root"].append(new_root) stable = False else: i += 1 if not stable: self.draw_contours( canvas=wrapper.current_image, contours=cnt_approx, image_name=f"merging_unk_{step}", contours_data=cnt_data, ) step += 1 self.demo_image = self.draw_contours( canvas=wrapper.current_image, contours=cnt_approx, image_name="unk_labels_merged", contours_data=cnt_data, ) # Clean mask contours = ipc.get_contours( mask=mask, retrieve_mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE, ) src_image = wrapper.current_image for cnt in contours: is_good_one = False for root in cnt_approx["root"]: if wrapper.contours_min_distance(root["cnt"], cnt) <= 0: is_good_one = True break if is_good_one: cv2.drawContours(src_image, [cnt], 0, (0, 255, 0), 2) else: cv2.drawContours(src_image, [cnt], 0, (0, 0, 255), 2) cv2.drawContours(mask, [cnt], 0, 0, -1) wrapper.store_image(src_image, "kcnr_img_wth_tagged_cnt") wrapper.store_image(mask, "kcnr_final") self.result = mask res = True else: wrapper.store_image(wrapper.current_image, "current_image") res = True except Exception as e: res = False wrapper.error_holder.add_error( new_error_text=f'Failed to process {self. name}: "{repr(e)}"', new_error_level=35, target_logger=logger, ) else: pass finally: return res
def process_wrapper(self, **kwargs): """ Adaptive threshold: Real time : True Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Select source file type (source_file): no clue * Channel (channel): * Invert mask (invert_mask): Invert result * Max value (max_value): Non-zero value assigned to the pixels for which the condition is satisfied * Adaptive method (method): Adaptive thresholding algorithm to use, see cv::AdaptiveThresholdTypes * Block size (block_size): Size of a pixel neighborhood that is used to calculate a threshold value for the pixel: 3, 5, 7, and so on. * C (C): Constant subtracted from the mean or weighted mean (see the details below). Normally, it is positive but may be zero or negative as well. * Build mosaic (build_mosaic): If true source and result will be displayed side by side * Median filter size (odd values only) (median_filter_size): * Morphology operator (morph_op): * Kernel size (kernel_size): * Kernel shape (kernel_shape): * Iterations (proc_times): * Overlay text on top of images (text_overlay): Draw description text on top of images """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: src_img = self.wrapper.current_image wrapper.store_image(src_img, "source") if self.get_value_of("enabled") == 1: median_filter_size = self.get_value_of("median_filter_size") median_filter_size = ( 0 if median_filter_size == 1 else ensure_odd(median_filter_size) ) if self.get_value_of("invert_mask") == 1: invert = cv2.THRESH_BINARY_INV else: invert = cv2.THRESH_BINARY max_value = self.get_value_of("max_value") method = self.get_value_of("method") if method == "gauss": method = cv2.ADAPTIVE_THRESH_GAUSSIAN_C elif method == "mean": method = cv2.ADAPTIVE_THRESH_MEAN_C else: logger.error(f"Unknown method {method}") return False block_size = self.get_value_of("block_size") if block_size % 2 == 0: block_size += 1 c_value = self.get_value_of("C") channel = self.get_value_of("channel") text_overlay = self.get_value_of("text_overlay") == 1 build_mosaic = self.get_value_of("build_mosaic") == 1 c = wrapper.get_channel( src_img, channel, median_filter_size=median_filter_size ) if c is None: self.do_channel_failure(channel) return # Crop if channel is msp if (c.shape != src_img.shape) and ( self.get_value_of("source_file", "source") == "cropped_source" ): c = wrapper.crop_to_keep_roi(c) mask = cv2.adaptiveThreshold( src=c, maxValue=max_value, adaptiveMethod=method, thresholdType=invert, blockSize=block_size, C=c_value, ) self.result = self.apply_morphology_from_params(mask) wrapper.store_image( self.result, f"adaptive_threshold_{self.input_params_as_str()}", text_overlay=text_overlay, ) if build_mosaic: wrapper.store_image(c, "source_channel") canvas = wrapper.build_mosaic( image_names=np.array( [ "source_channel", f"adaptive_threshold_{self.input_params_as_str()}", ] ) ) wrapper.store_image(canvas, "mosaic") res = True except Exception as e: res = False logger.error(f'Failed to process {self. name}: "{repr(e)}"') else: pass finally: return res
def process_wrapper(self, **kwargs): """ Performs channel subtraction while ensure 8bit range, each channel can have a weight. If threshold min or max are set a threshold operation will be performed. Real time : Yes Keyword Arguments (in parentheses, argument name): * Channel 1 (channel_1): First channel * Weight of the first channel (alpha): Weight of the first channel * Channel 2 (channel_2): Second channel * Weight of the second channel (beta): Weight of the second channel * Threshold min value (min): Lower threshold bound, if >0 threshold will be applied * Threshold max value (max): Upper threshold bound, if <255 threshold will be applied * Median filter size (median_filter_size): odd values only, if not 1 will be added * Morphology operator (morphology_op): Can be none * Morphology kernel size (kernel_size): Kernel is elliptical """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False channel_1 = self.get_value_of("channel_1") alpha = self.get_value_of("alpha") / 100 channel_2 = self.get_value_of("channel_2") beta = self.get_value_of("beta") / 100 post_processing = self.get_value_of("post_processing") min_ = self.get_value_of("min") max_ = self.get_value_of("max") median_filter_size = self.get_value_of("median_filter_size") median_filter_size = (0 if median_filter_size == 1 else ensure_odd(median_filter_size)) res = False try: img = self.wrapper.current_image c1 = wrapper.get_channel(img, channel_1) c2 = wrapper.get_channel(img, channel_2) if c1 is None: self.do_channel_failure(channel_1) return if c2 is None: self.do_channel_failure(channel_2) return wrapper.store_image(c1, f"Channel 1: {channel_1}") wrapper.store_image(c2, f"Channel 1: {channel_2}") tmp = (c1 * alpha) - (c2 * beta) if post_processing == "normalize": tmp = ((tmp - tmp.min()) / (tmp.max() - tmp.min()) * 255).astype(np.uint8) elif post_processing == "cut_negative": tmp[tmp < 0] = 0 tmp = tmp.astype(np.uint8) elif post_processing == "cut_negative_and_normalize": tmp[tmp < 0] = 0 tmp = ((tmp - tmp.min()) / (tmp.max() - tmp.min()) * 255).astype(np.uint8) elif post_processing == "rescale": tmp = (tmp - tmp.min()).astype(np.uint8) else: logger.error(f"Unknown postprocessing {post_processing}") res = False return wrapper.store_image( tmp, f"Subtraction_{self.input_params_as_str(exclude_defaults=True)}", text_overlay=True, ) if (min_ > 0) or (max_ < 255): mask, _ = wrapper.get_mask( tmp, "", min_, max_, median_filter_size=median_filter_size) mask = self.apply_morphology_from_params(mask) wrapper.store_image( mask, f"sub_threshold_{self.input_params_as_str(exclude_defaults=True)}", text_overlay=True, ) self.result = mask.copy() else: self.result = tmp.copy() except Exception as e: res = False logger.error(f'Failed to process {self. name}: "{repr(e)}"') else: res = True finally: return res
def process_wrapper(self, **kwargs): """ Smooth contours: 'Smooth contours Real time: True Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Degree of spline curve (spline_degree): Degree of the spline. Cubic splines are recommended. Even values of k should be avoided especially with a small s-value. 1 <= k <= 5, default is 3. * Smoothing condition (smoothing): Value will be divide by 10""" wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: img = wrapper.current_image mask = self.get_mask() if mask is None: logger.error( "Failure Smooth contours: mask must be initialized") return # Get source contours contours = [ c for c in ipc.get_contours( mask=mask, retrieve_mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE, ) if (cv2.contourArea(c, True) < 0) ] contours.sort(key=lambda x: cv2.contourArea(x), reverse=True) output = mask.copy() for contour in contours: x, y = contour.T # Convert from np arrays to normal arrays tck, u, = splprep( [x.tolist()[0], y.tolist()[0]], u=None, k=ipc.ensure_odd(self.get_value_of("spline_degree")), s=self.get_value_of("smoothing") / 10, per=1, ) u_new = np.linspace(u.min(), u.max(), 1000) x_new, y_new = splev(u_new, tck, der=0) cv2.drawContours( output, [ np.asarray( [[[int(i[0]), int(i[1])]] for i in zip(x_new, y_new)], dtype=np.int32, ) ], 0, 255, -1, ) self.result = output # Write your code here wrapper.store_image(output, "current_image") res = True else: wrapper.store_image(wrapper.current_image, "current_image") res = True except Exception as e: res = False logger.error(f"Smooth contours FAILED, exception: {repr(e)}") else: pass finally: return res
def process_wrapper(self, **kwargs): """ Triangle binarization: Real time : No Keyword Arguments (in parentheses, argument name): * Channel (channel): Selected channel * Invert mask (invert_mask): Invert result * Median filter size (odd values only) (median_filter_size): Size of median filter to be applied * Morphology operator (morphology_op): Morphology operator to be applied afterwards * Morphology kernel size (kernel_size): - * Overlay text on top of images (text_overlay): Draw description text on top of images """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False median_filter_size = self.get_value_of("median_filter_size") invert_mask = self.get_value_of("invert_mask") == 1 channel = self.get_value_of("channel") text_overlay = self.get_value_of("text_overlay") == 1 build_mosaic = self.get_value_of("build_mosaic") == 1 res = False try: src_img = self.wrapper.current_image median_filter_size = (0 if median_filter_size == 1 else ensure_odd(median_filter_size)) c = wrapper.get_channel(src_img, channel, median_filter_size=median_filter_size) if c is None: self.do_channel_failure(channel) return # Crop if channel is msp if (c.shape != src_img.shape) and (self.get_value_of( "source_file", "source") == "cropped_source"): c = wrapper.crop_to_keep_roi(c) _, mask = cv2.threshold(c, 0, 255, cv2.THRESH_TRIANGLE) if invert_mask: mask = 255 - mask self.result = self.apply_morphology_from_params(mask) wrapper.store_image( self.result, f"triangle_binarization_{self.input_params_as_str(exclude_defaults=True)}", text_overlay=text_overlay, ) if build_mosaic: wrapper.store_image(c, "source_channel") canvas = wrapper.build_mosaic(image_names=np.array([ "source_channel", f"triangle_binarization_{self.input_params_as_str(exclude_defaults=True)}", ])) wrapper.store_image(canvas, "mosaic") res = True except Exception as e: res = False logger.error(f'Failed to process {self. name}: "{repr(e)}"') else: pass finally: return res
def process_wrapper(self, **kwargs): """ Edge detectors: Performs edge detection with various common operators. Mostly used by other tools. Real time: True Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Select source file type (source_file): no clue * Channel (channel): * Normalize channel (normalize): Normalize channel before edge detection * Median filter size (odd values only) (median_filter_size): * Select edge detection operator (operator): * Canny's sigma for scikit, aperture for OpenCV (canny_sigma): Sigma. * Canny's first Threshold (canny_first): First threshold for the hysteresis procedure. * Canny's second Threshold (canny_second): Second threshold for the hysteresis procedure. * Kernel size (kernel_size): * Threshold (threshold): Threshold for kernel based operators * Apply threshold (apply_threshold): * Overlay text on top of images (text_overlay): Draw description text on top of images """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: operator_ = self.get_value_of("operator") canny_sigma = self.get_value_of("canny_sigma") canny_first = self.get_value_of("canny_first") canny_second = self.get_value_of("canny_second") channel = self.get_value_of("channel") kernel_size = self.get_value_of("kernel_size") threshold = self.get_value_of("threshold") text_overlay = self.get_value_of("text_overlay") == 1 input_kind = self.get_value_of("source_selector") if input_kind == "mask": src_img = self.get_mask() elif input_kind == "current_image": src_img = wrapper.current_image else: src_img = None logger.error(f"Unknown source: {input_kind}") self.result = None return if self.get_value_of("enabled") == 1: c = wrapper.get_channel( src_img=src_img, channel=channel, normalize=self.get_value_of("normalize") == 1, median_filter_size=self.get_value_of("median_filter_size"), ) if c is None: self.do_channel_failure(channel) return # Crop if channel is msp ch, cw, *_ = c.shape sh, sw, *_ = src_img.shape if ((ch != sh) or (cw != sh)) and (self.get_value_of( "source_file", "source") == "cropped_source"): c = wrapper.crop_to_keep_roi(c) if operator_ == "canny_scik": edges = canny( c, sigma=canny_sigma, low_threshold=canny_first, high_threshold=canny_second, ) elif operator_ == "canny_opcv": edges = cv2.Canny( c, threshold1=canny_first, threshold2=canny_second, apertureSize=ipc.ensure_odd(i=canny_sigma, min_val=3, max_val=7), ) elif operator_ == "laplacian": if kernel_size == 1: kernel_size = 0 elif (kernel_size > 0) and (kernel_size % 2 == 0): kernel_size += 1 if kernel_size >= 3: gauss = cv2.GaussianBlur(c, (kernel_size, kernel_size), 0) wrapper.store_image( gauss, f"filtered_image_{self.input_params_as_str()}", text_overlay=True, ) edges = cv2.Laplacian(gauss, cv2.CV_64F) else: edges = cv2.Laplacian(c, cv2.CV_64F) elif operator_ == "sobel": edges = sobel(c) elif operator_ == "sobel_v": edges = sobel_v(c) elif operator_ == "sobel_h": edges = sobel_h(c) elif operator_ == "roberts": edges = roberts(c) elif operator_ == "prewitt": edges = prewitt(c) else: edges = c.copy() edges = self.to_uint8(edges) if (operator_ in [ "laplacian", "sobel", "sobel_v", "sobel_h", "roberts", "prewitt", ] and self.get_value_of("apply_threshold", default_value=1)): edges[edges < threshold] = 0 edges[edges >= threshold] = 255 self.result = edges img_name = f"edges_{self.input_params_as_str()}" if text_overlay: wrapper.store_image( self.result, img_name, text_overlay=self.input_params_as_str( exclude_defaults=False, excluded_params=("progress_callback", ), ).replace(", ", "\n"), ) else: wrapper.store_image(self.result, img_name, text_overlay=text_overlay) else: wrapper.store_image(src_img, "source") self.demo_image = self.result res = True except Exception as e: res = False logger.error(f'Failed to process {self. name}: "{repr(e)}"') else: pass finally: return res
def process_wrapper(self, **kwargs): """ Local binary pattern threshold: Gray scale and rotation invariant LBP (Local Binary Patterns). LBP is an invariant descriptor that can be used for texture classification. Real time: False Keyword Arguments (in parentheses, argument name): * Activate tool (enabled): Toggle whether or not tool is active * Channel (channel): * Number of circularly symmetric neighbor (P): Number of circularly symmetric neighbor set points (quantization of the angular space) * Radius of circle (R): Radius of circle (spatial resolution of the operator) * Method to determine the pattern (method): * Transformation applied to output (transformation): * Cut x%% lower values (lower_cut): Increase to smooth low frequency textures regions and add detail to high frequencies * Cut x%% upper values (upper_cut): Increase to smooth high frequency textures regions and add detail to low frequencies * Postprocessing option (post_processing): * Threshold min value (min_t): * Threshold max value (max_t): * Median filter size (odd values only) (median_filter_size): * Morphology operator (morph_op): * Kernel size (kernel_size): * Kernel shape (kernel_shape): * Iterations (proc_times): * Select pseudo color map (color_map): """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: if self.get_value_of("enabled") == 1: c = ipc.resize_image( wrapper.get_channel( wrapper.current_image, self.get_value_of("channel") ), target_rect=RectangleRegion(width=800, height=600), keep_aspect_ratio=True, output_as_bgr=False, ) c = local_binary_pattern( image=c, P=self.get_value_of("P", scale_factor=wrapper.scale_factor), R=self.get_value_of("R", scale_factor=wrapper.scale_factor), method=self.get_value_of("method"), ) # Transform ct = self.get_value_of(f"transformation") if ct == "sigmoid": c = np.interp(c, (c.min(), c.max()), (0, 5)) c = expit(c) elif ct == "log": c = np.log(c + 1) elif ct == "logit": c = np.interp(c, (c.min(), c.max()), (0.5, 0.99)) c = logit(c) elif ct == "arcsin": c = np.interp(c, (c.min(), c.max()), (0, 1)) c = np.arcsin(c) elif ct == "sqrt": c = np.interp(c, (c.min(), c.max()), (0, 1)) c = np.sqrt(c) # Cut lower_cut = self.get_value_of("lower_cut") if lower_cut > 0: c[c < np.max(c) * (lower_cut / 100)] = 0 upper_cut = self.get_value_of("upper_cut") if upper_cut > 0: upper_cut = np.max(c) * ((100 - upper_cut) / 100) c[c > upper_cut] = upper_cut c = self.to_uint8(c) # Post processing pp = self.get_value_of("post_processing") if pp == "threshold": median_filter_size = self.get_value_of("median_filter_size") median_filter_size = ( 0 if median_filter_size == 1 else ipc.ensure_odd(median_filter_size) ) c, _ = wrapper.get_mask( src_img=c, channel=None, min_t=self.get_value_of("min_t"), max_t=self.get_value_of("max_t"), median_filter_size=median_filter_size, ) self.result = self.apply_morphology_from_params(c) elif pp == "false_color": color_map = self.get_value_of("color_map") _, color_map = color_map.split("_") self.result = cv2.applyColorMap(c, int(color_map)) else: self.result = c # Store wrapper.store_image(self.result, "local_binary_pattern") res = True else: wrapper.store_image(wrapper.current_image, "current_image") res = True except Exception as e: res = False logger.error(f'Failed to process {self. name}: "{repr(e)}"') else: pass finally: return res
def process_wrapper(self, **kwargs): """ Grab cut: Implementation of OpenCV grab cut function. Better if used with a ROI. Better if ROI is extracted from mask. Even better if used after keep linked contours builds a ROI & and a mask. Real time: False Keyword Arguments (in parentheses, argument name): * Name of ROI to be used (roi_names): Operation will only be applied inside of ROI * ROI selection mode (roi_selection_mode): * Size of dilation's kernel (prob_dilate_size): Size of kernel for the morphology operator applied to grow the source mask to set a probable mask * Size of errode's kernel (sure_erode_size): Size of kernel for the morphology operator applied to shrink the source mask to set a sure mask * Select pseudo color map (color_map): * GraphCut iterations allowed (gc_iter_count): * Build mosaic (build_mosaic): * Name of ROI to be used (roi_names): Operation will only be applied inside of ROI * ROI selection mode (roi_selection_mode): """ wrapper = self.init_wrapper(**kwargs) if wrapper is None: return False res = False try: # Get Source image img = wrapper.current_image # Get starting mask mask = self.get_mask() if mask is None: logger.error(f"FAIL {self.name}: mask must be initialized") return # Get ROI rois = self.get_ipt_roi( wrapper=wrapper, roi_names=self.get_value_of("roi_names").replace( " ", "").split(","), selection_mode=self.get_value_of("roi_selection_mode"), ) if len(rois) > 0: roi = rois[0] else: roi = None # Initialize mask gc_in_mask = np.full_like(mask, cv2.GC_BGD) if roi is not None: gc_in_mask = roi.draw_to(gc_in_mask, line_width=-1, color=cv2.GC_PR_BGD) # Apply dilation dks = self.get_value_of("prob_dilate_size") 0 if dks == 1 else ensure_odd(dks) if dks > 0: d_mask = wrapper.dilate(image=mask, kernel_size=dks) gc_in_mask[d_mask != 0] = cv2.GC_PR_FGD # Apply erosion eks = self.get_value_of("sure_erode_size") 0 if eks == 1 else ensure_odd(eks) if eks > 0: e_mask = wrapper.erode(image=mask, kernel_size=eks) gc_in_mask[e_mask != 0] = cv2.GC_FGD else: gc_in_mask[mask != 0] = cv2.GC_FGD color_map = self.get_value_of("color_map") _, color_map = color_map.split("_") dbg_img = cv2.applyColorMap( self.to_uint8(gc_in_mask, normalize=True), int(color_map)) if roi is not None: dbg_img = roi.draw_to(dbg_img, line_width=wrapper.width // 200) wrapper.store_image(dbg_img, "grabcut_initialized_mask") # Initialize the other ones bgd_model = np.zeros((1, 65), np.float64) fgd_model = np.zeros((1, 65), np.float64) # Grab the cut cv2.grabCut( img=img, mask=gc_in_mask, rect=None if roi is None else roi.to_opencv(), bgdModel=bgd_model, fgdModel=fgd_model, iterCount=self.get_value_of("gc_iter_count"), mode=cv2.GC_INIT_WITH_MASK, ) wrapper.store_image( cv2.applyColorMap(self.to_uint8(gc_in_mask, normalize=True), int(color_map)), "grabcut_false_color_mask", ) self.result = np.where(gc_in_mask == cv2.GC_FGD, 255, 0).astype("uint8") wrapper.store_image(self.result, "grabcut_mask") wrapper.store_image( image=self.wrapper.draw_image( src_image=img, src_mask=self.result, background="bw", foreground="source", ), text="result_on_bw", ) res = True if self.get_value_of("build_mosaic") == 1: canvas = wrapper.build_mosaic(image_names=np.array([ [ "current_image", "grabcut_initialized_mask", "grabcut_false_color_mask", ], ["grabcut_mask", "", "result_on_bw"], ])) wrapper.store_image(canvas, "mosaic") except Exception as e: logger.error(f'Failed to process {self. name}: "{repr(e)}"') res = False else: pass finally: return res