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):
        """
        Filter contour by size:
        'Keep or descard contours according to their size
        Real time: False

        Keyword Arguments (in parentheses, argument name):
            * Activate tool (enabled): Toggle whether or not tool is active
            * Lower bound limit (min_threshold): Only contours bigger than lower limit bound will be kept
            * Upper bound limit (max_threshold): Only contours smaller than lower limit bound will be kept
            * 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:
            if self.get_value_of("enabled") == 1:
                mask = self.get_mask()
                if mask is None:
                    logger.error(f"FAIL {self.name}: mask must be initialized")
                    return

                lt, ut = self.get_value_of("min_threshold"), self.get_value_of(
                    "max_threshold")

                # 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)
                colors = ipc.build_color_steps(step_count=len(contours))

                dbg_img = np.dstack((np.zeros_like(mask), np.zeros_like(mask),
                                     np.zeros_like(mask)))
                for clr, cnt in zip(colors, contours):
                    cv2.drawContours(dbg_img, [cnt], 0, clr, -1)
                dbg_img = np.dstack((
                    cv2.bitwise_and(dbg_img[:, :, 0], mask),
                    cv2.bitwise_and(dbg_img[:, :, 1], mask),
                    cv2.bitwise_and(dbg_img[:, :, 2], mask),
                ))
                wrapper.store_image(
                    image=dbg_img,
                    text="all_contours",
                )

                fnt = (cv2.FONT_HERSHEY_SIMPLEX, 0.6)
                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    x, y, w, h = cv2.boundingRect(cnt)
                    x += w // 2 - 10
                    y += h // 2
                    if area_ > 0:
                        cv2.putText(
                            dbg_img,
                            f"{area_}",
                            (x, y),
                            fnt[0],
                            fnt[1],
                            ipc.C_WHITE,
                            2,
                        )
                wrapper.store_image(
                    image=dbg_img,
                    text="all_contours_with_sizes",
                )

                dbg_img = np.dstack((np.zeros_like(mask), np.zeros_like(mask),
                                     np.zeros_like(mask)))
                out_mask = np.zeros_like(mask)

                # Discarded contours
                size_cnts = np.dstack(
                    (np.zeros_like(mask), np.zeros_like(mask),
                     np.zeros_like(mask)))
                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    if area_ < lt:
                        cv2.drawContours(size_cnts, [cnt], 0, ipc.C_RED, -1)
                    elif area_ > ut:
                        cv2.drawContours(size_cnts, [cnt], 0, ipc.C_BLUE, -1)
                    else:
                        cv2.drawContours(size_cnts, [cnt], 0, ipc.C_WHITE, -1)
                wrapper.store_image(image=size_cnts, text="cnts_by_size")

                # Discarded contours
                size_cnts = np.dstack(
                    (np.zeros_like(mask), np.zeros_like(mask),
                     np.zeros_like(mask)))
                for cnt in sorted(contours,
                                  key=lambda x: cv2.contourArea(x),
                                  reverse=True):
                    area_ = cv2.contourArea(cnt)
                    if area_ < lt:
                        cv2.drawContours(size_cnts, [cnt], 0, ipc.C_RED, -1)
                    elif area_ > ut:
                        cv2.drawContours(size_cnts, [cnt], 0, ipc.C_BLUE, -1)
                    else:
                        cv2.drawContours(size_cnts, [cnt], 0, ipc.C_WHITE, -1)
                wrapper.store_image(image=size_cnts,
                                    text="cnts_by_size_reversed")

                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    if not (lt < area_ < ut):
                        cv2.drawContours(dbg_img, [cnt], 0, ipc.C_RED, -1)
                # Discarded contours borders
                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    if not (lt < area_ < ut):
                        cv2.drawContours(dbg_img, [cnt], 0, ipc.C_MAROON, 4)
                # Kept contours
                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    if lt < area_ < ut:
                        cv2.drawContours(out_mask, [cnt], 0, 255, -1)
                        cv2.drawContours(dbg_img, [cnt], 0, ipc.C_GREEN, -1)
                    else:
                        cv2.drawContours(out_mask, [cnt], 0, 0, -1)
                        cv2.drawContours(dbg_img, [cnt], 0, ipc.C_RED, -1)
                dbg_img = np.dstack((
                    cv2.bitwise_and(dbg_img[:, :, 0], mask),
                    cv2.bitwise_and(dbg_img[:, :, 1], mask),
                    cv2.bitwise_and(dbg_img[:, :, 2], mask),
                ))
                # Discarded sizes
                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    if not (lt < area_ < ut):
                        x, y, w, h = cv2.boundingRect(cnt)
                        x += w // 2 - 10
                        y += h // 2
                        cv2.putText(
                            dbg_img,
                            f"{area_}",
                            (x, y),
                            fnt[0],
                            fnt[1],
                            ipc.C_BLUE,
                            thickness=2,
                        )
                # Kept sizes
                for cnt in contours:
                    area_ = cv2.contourArea(cnt)
                    if lt < area_ < ut:
                        x, y, w, h = cv2.boundingRect(cnt)
                        x += w // 2 - 10
                        y += h // 2
                        cv2.putText(
                            dbg_img,
                            f"{area_}",
                            (x, y),
                            fnt[0],
                            fnt[1],
                            ipc.C_BLUE,
                            thickness=2,
                        )

                out_mask = cv2.bitwise_and(
                    out_mask,
                    mask,
                )

                # Apply ROIs if needed
                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:
                    untouched_mask = regions.delete_rois(rois=rois,
                                                         image=self.get_mask())
                    self.result = cv2.bitwise_or(
                        untouched_mask,
                        regions.keep_rois(rois=rois, image=out_mask))
                    self.demo_image = cv2.bitwise_or(
                        dbg_img,
                        np.dstack(
                            (untouched_mask, untouched_mask, untouched_mask)),
                    )
                else:
                    self.result = out_mask
                    self.demo_image = dbg_img

                wrapper.store_image(image=self.result,
                                    text="filtered_contours")
                wrapper.store_image(image=self.demo_image,
                                    text="tagged_contours")

                res = True
            else:
                wrapper.store_image(wrapper.current_image, "current_image")
                res = True
        except Exception as e:
            res = False
            logger.exception(
                f"Filter contour by size FAILED, exception: {repr(e)}")
        else:
            pass
        finally:
            return res
Ejemplo n.º 3
0
    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):
        """
        Split overlapped ellipses:
        Split overlapped ellipses.
                Three methods are available: Concave points detection, Hough circle detection and extrapolation from median area.
        Real time: True

        Keyword Arguments (in parentheses, argument name):
            * Activate tool (enabled): Toggle whether or not tool is active
            * Use concave detection method (concave):
            * Use Hough detection method (hough):
            * Use median area detection method (median):
            * Approximation factor (approx_factor):
            * Debug font scale (dbg_font_scale):
            * Debug font thickness (dbg_font_thickness):
            * Split object if size is over (min_size_to_split):
            * Residue size (residue_size): Consider all object below this size as noise when analysing objects
            * Minimal radius to consider (min_radius): All circles smaller than this will be ignored
            * Maximal radius to consider (max_radius): All circles bigger than this will be ignored
            * Minimum distance between two circles (min_distance): Remove circles that are too close
            * Radius granularity (step_radius): Steps for scanning radius
        --------------"""

        wrapper = self.init_wrapper(**kwargs)
        if wrapper is None:
            return False

        res = False
        try:
            if self.get_value_of("enabled") == 1:
                method = self.get_value_of("method")

                mask = self.get_mask()
                if mask is None:
                    logger.error(
                        "Failure Split overlapped ellipses: 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)

                demo_images = []
                if self.get_value_of("concave") == 1:
                    res = self.concave_detection(
                        wrapper=wrapper,
                        mask=mask,
                        contours=contours,
                    )
                    demo_images.append(res["demo_image"])
                    self.add_value(
                        key="ellipses_concave_method",
                        value=res["count"],
                        force_add=True,
                    )
                if self.get_value_of("hough") == 1:
                    res = self.hough_detection(
                        wrapper=wrapper,
                        mask=mask,
                        contours=contours,
                    )
                    demo_images.append(res["demo_image"])
                    self.add_value(
                        key="ellipses_hough_method",
                        value=res["count"],
                        force_add=True,
                    )
                if self.get_value_of("median") == 1:
                    res = self.median_area_detection(
                        wrapper=wrapper,
                        mask=mask,
                        contours=contours,
                    )
                    demo_images.append(res["demo_image"])
                    self.add_value(
                        key="ellipses_median_method",
                        value=res["count"],
                        force_add=True,
                    )

                if len(demo_images) == 3:
                    demo_images.append(wrapper.current_image)

                self.demo_image = wrapper.auto_mosaic(demo_images)

                res = True
            else:
                wrapper.store_image(wrapper.current_image, "current_image")
                res = True
        except Exception as e:
            res = False
            logger.error(
                f"Split overlapped ellipses FAILED, exception: {repr(e)}")
        else:
            pass
        finally:
            return res
    def concave_detection(self, wrapper, mask, contours):

        min_size_to_split = self.get_value_of("min_size_to_split")
        dbg_font_scale = self.get_value_of("dbg_font_scale")
        dbg_font_thickness = self.get_value_of("dbg_font_thickness")
        residue_size = self.get_value_of("residue_size")
        total_ellipses = 0
        dbg_img = np.dstack((
            np.zeros_like(mask),
            np.zeros_like(mask),
            np.zeros_like(mask),
        ))
        for cnt in contours:
            x, y, w, h = cv2.boundingRect(cnt)
            clr = (
                random.randint(50, 200),
                random.randint(50, 200),
                random.randint(50, 200),
            )
            handled_ = cv2.contourArea(cnt) > min_size_to_split
            if handled_ is True:
                work_img = dbg_img.copy()
                cv2.drawContours(
                    image=work_img,
                    contours=[cv2.convexHull(cnt)],
                    contourIdx=0,
                    color=ipc.C_WHITE,
                    thickness=-1,
                )
                cv2.drawContours(
                    image=work_img,
                    contours=[cnt],
                    contourIdx=0,
                    color=ipc.C_BLACK,
                    thickness=-1,
                )
                work_img = work_img[y:y + h, x:x + w].copy()
                count_ = 0
                for c in ipc.get_contours(
                        mask=work_img[:, :, 0],
                        retrieve_mode=cv2.RETR_LIST,
                        method=cv2.CHAIN_APPROX_SIMPLE,
                ):
                    if (cv2.contourArea(c, True) < 0) and (cv2.contourArea(c)
                                                           >= residue_size):
                        cv2.drawContours(
                            image=work_img,
                            contours=[c],
                            contourIdx=0,
                            color=ipc.C_GREEN,
                            thickness=-1,
                        )
                        count_ += 1
                    else:
                        cv2.drawContours(
                            image=work_img,
                            contours=[c],
                            contourIdx=0,
                            color=ipc.C_RED,
                            thickness=-1,
                        )
                dbg_img[y:y + h, x:x + w] = work_img
                if count_ < 2:
                    count_ += 1
            else:
                count_ = 1
            cv2.drawContours(dbg_img, [cnt], 0, ipc.C_WHITE, -1)

            total_ellipses += count_

            cx = x + w // 2 - 10
            cy = y + h // 2
            cv2.putText(
                img=dbg_img,
                text=str(count_),
                org=(cx, cy),
                fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                fontScale=dbg_font_scale,
                color=(0, 255, 0),
                thickness=dbg_font_thickness,
            )
            if handled_ is True:
                cv2.putText(
                    img=dbg_img,
                    text=f"{x}, {y}",
                    org=(x + w, y + h),
                    fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                    fontScale=dbg_font_scale / 2,
                    color=(0, 0, 255),
                    thickness=dbg_font_thickness // 2,
                )
                cv2.putText(
                    img=dbg_img,
                    text=f"{cv2.contourArea(cnt)}",
                    org=(x + w, y),
                    fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                    fontScale=dbg_font_scale / 2,
                    color=(0, 0, 255),
                    thickness=dbg_font_thickness // 2,
                )

        return {
            "count":
            total_ellipses,
            "demo_image":
            self.write_result(
                image=dbg_img,
                method="concave detection",
                value=total_ellipses,
            ),
        }
Ejemplo n.º 6
0
    def process_wrapper(self, **kwargs):
        """
        Fix perspective:
        Fixes perspective using four dots to detect rectangle boundary.
        Use the included threshold utility to detect the dots.
        Real time: True

        Keyword Arguments (in parentheses, argument name):
            * Activate tool (enabled): Toggle whether or not tool is active
            * Module mode (mode):
            * Channel 1 (c1):
            * Min threshold for channel 1 (c1_low):
            * Max threshold for channel 1 (c1_high):
            * Channel 2 (c2):
            * Min threshold for channel 2 (c2_low):
            * Max threshold for channel 2 (c2_high):
            * Channel 3 (c3):
            * Min threshold for channel 3 (c3_low):
            * Max threshold for channel 3 (c3_high):
            * How to merge thresholds (merge_mode):
            * Morphology operator (morph_op):
            * Kernel size (kernel_size):
            * Kernel shape (kernel_shape):
            * Iterations (proc_times):
            * Minimal dot size (surface) (min_dot_size):
            * Maximal dot size (surface) (max_dot_size):
            * Destination width (dst_width):
            * Destination height (dst_height):
        """
        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
                pm = self.get_value_of("mode")

                channels = []
                stored_names = []
                for i in [1, 2, 3]:
                    c = self.get_value_of(f"c{i}")
                    if c == "none":
                        continue
                    msk, stored_name = wrapper.get_mask(
                        src_img=img,
                        channel=c,
                        min_t=self.get_value_of(f"c{i}_low"),
                        max_t=self.get_value_of(f"c{i}_high"),
                    )
                    channels.append(msk)
                    stored_names.append(stored_name)
                    wrapper.store_image(image=msk, text=stored_name)

                wrapper.store_image(
                    image=wrapper.build_mosaic(
                        shape=(wrapper.height // len(stored_names), wrapper.width, 3),
                        image_names=np.array(stored_names),
                    ),
                    text="partial_thresholds",
                )

                func = getattr(wrapper, self.get_value_of("merge_mode"), None)
                if func:
                    mask = self.apply_morphology_from_params(
                        func([mask for mask in channels if mask is not None])
                    )
                    wrapper.store_image(image=mask, text="merged_mask")
                else:
                    logger.error("Unable to merge partial masks")
                    res = False
                    return

                if pm == "threshold":
                    self.result = mask
                    res = True
                    return

                # Clean mask if needed
                labels = measure.label(input=mask, neighbors=8, background=0)
                dots_mask = np.zeros(mask.shape, dtype="uint8")
                min_dot_size = self.get_value_of("min_dot_size")
                max_dot_size = self.get_value_of("max_dot_size")
                # loop over the unique components
                for label in np.unique(labels):
                    # if this is the background label, ignore it
                    if label == 0:
                        continue

                    # otherwise, construct the label mask and count the
                    # number of pixels
                    labelMask = np.zeros(mask.shape, dtype="uint8")
                    labelMask[labels == label] = 255
                    numPixels = cv2.countNonZero(labelMask)

                    # if the number of pixels in the component is sufficiently
                    # large, then add it to our mask of "large blobs"
                    if min_dot_size <= numPixels <= max_dot_size:
                        dots_mask = cv2.add(dots_mask, labelMask)
                wrapper.store_image(image=dots_mask, text="mask_cleaned")

                # Find dots' positions
                mask = dots_mask
                # find the contours in the mask, then sort them from left to
                # right
                cnts = ipc.get_contours(
                    mask=mask,
                    retrieve_mode=cv2.RETR_EXTERNAL,
                    method=cv2.CHAIN_APPROX_SIMPLE,
                )
                cnts = sort_contours(cnts)[0]

                # loop over the contours
                dots = []
                for (i, c) in enumerate(cnts):
                    # draw the bright spot on the image
                    (x, y, w, h) = cv2.boundingRect(c)
                    ((cX, cY), radius) = cv2.minEnclosingCircle(c)
                    dots.append((int(cX), int(cY)))

                # Reorder dots
                top_left = min_distance(origin=(0, 0), points=dots)
                cv2.circle(img, top_left, 20, (0, 0, 255), 3)
                cv2.putText(
                    img,
                    f"top_left - {top_left}",
                    top_left,
                    cv2.FONT_HERSHEY_SIMPLEX,
                    2,
                    (255, 0, 255),
                    6,
                )
                top_right = min_distance(origin=(wrapper.width, 0), points=dots)
                cv2.circle(img, top_right, 20, (0, 0, 255), 3)
                cv2.putText(
                    img,
                    f"top_right - {top_right}",
                    top_right,
                    cv2.FONT_HERSHEY_SIMPLEX,
                    2,
                    (255, 0, 255),
                    6,
                )
                bottom_left = min_distance(origin=(0, wrapper.height), points=dots)
                cv2.circle(img, bottom_left, 20, (0, 0, 255), 3)
                cv2.putText(
                    img,
                    f"bottom_left - {bottom_left}",
                    bottom_left,
                    cv2.FONT_HERSHEY_SIMPLEX,
                    2,
                    (255, 0, 255),
                    6,
                )
                bottom_right = min_distance(
                    origin=(wrapper.width, wrapper.height), points=dots
                )
                cv2.circle(img, bottom_right, 20, (0, 0, 255), 3)
                cv2.putText(
                    img,
                    f"bottom_right - {bottom_right}",
                    bottom_right,
                    cv2.FONT_HERSHEY_SIMPLEX,
                    2,
                    (255, 0, 255),
                    6,
                )
                wrapper.store_image(image=img, text="dotted_image")

                if pm == "dot_detection":
                    self.result = img
                    res = True
                    return

                # pad_hor = self.get_value_of('pad_hor')
                # pad_ver = self.get_value_of('pad_ver')
                # top_left = (top_left[0] - pad_hor, top_left[1] - pad_ver)
                # top_right = (top_right[0] + pad_hor, top_right[1] - pad_ver)
                # bottom_left = (bottom_left[0] - pad_hor, bottom_left[1] + pad_ver)
                # bottom_right = (bottom_right[0] + pad_hor, bottom_right[1] + pad_ver)

                # Transform the image
                mat = cv2.getPerspectiveTransform(
                    src=np.array(
                        [top_left, top_right, bottom_right, bottom_left],
                        dtype="float32",
                    ),
                    dst=np.array(
                        [
                            [0, 0],
                            [self.get_value_of("dst_width") - 1, 0],
                            [
                                self.get_value_of("dst_width") - 1,
                                self.get_value_of("dst_height") - 1,
                            ],
                            [0, self.get_value_of("dst_height") - 1],
                        ],
                        dtype="float32",
                    ),
                )
                self.result = cv2.warpPerspective(
                    src=wrapper.current_image,
                    M=mat,
                    dsize=(
                        self.get_value_of("dst_width"),
                        self.get_value_of("dst_height"),
                    ),
                )
                wrapper.store_image(image=self.result, text="warped_image")

                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