Exemple #1
0
    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
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #7
0
    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
Exemple #10
0
    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
Exemple #13
0
    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