Ejemplo n.º 1
0
def find_concetric_circles(gray_img, min_ring_count=3, visual_debug=False):
    # get threshold image used to get crisp-clean edges using blur to remove small features
    edges = cv2.adaptiveThreshold(cv2.blur(gray_img, (3, 3)), 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 5, 11)
    _, contours, hierarchy = cv2.findContours(edges,
                                              mode=cv2.RETR_TREE,
                                              method=cv2.CHAIN_APPROX_NONE,
                                              offset=(0, 0))  #TC89_KCOS
    if visual_debug is not False:
        cv2.drawContours(visual_debug, contours, -1, (200, 0, 0))
    if contours is None or hierarchy is None:
        return []
    clusters = get_nested_clusters(contours,
                                   hierarchy[0],
                                   min_nested_count=min_ring_count)
    concentric_cirlce_clusters = []

    #speed up code by caching computed ellipses
    ellipses = {}

    # for each cluster fit ellipses and cull members that dont have good ellipse fit
    for cluster in clusters:
        if visual_debug is not False:
            cv2.drawContours(visual_debug, [contours[i] for i in cluster], -1,
                             (0, 0, 255))
        candidate_ellipses = []
        for i in cluster:
            c = contours[i]
            if len(c) > 5:
                if not i in ellipses:
                    e = cv2.fitEllipse(c)
                    fit = max(dist_pts_ellipse(e, c))
                    ellipses[i] = e, fit
                else:
                    e, fit = ellipses[i]
                a, b = e[1][0] / 2., e[1][1] / 2.
                if fit < max(2, max(e[1]) / 20):
                    candidate_ellipses.append(e)
                    if visual_debug is not False:
                        cv2.ellipse(visual_debug, e, (0, 255, 0), 1)

        if candidate_ellipses:
            cluster_center = np.mean(np.array(
                [e[0] for e in candidate_ellipses]),
                                     axis=0)
            candidate_ellipses = [
                e for e in candidate_ellipses
                if np.linalg.norm(e[0] -
                                  cluster_center) < max(3,
                                                        min(e[1]) / 20)
            ]
            if len(candidate_ellipses) >= min_ring_count:
                concentric_cirlce_clusters.append(candidate_ellipses)
                if visual_debug is not False:
                    cv2.ellipse(visual_debug, candidate_ellipses[-1],
                                (0, 255, 255), 4)

    #return clusters sorted by size of outmost cirlce biggest first.
    return sorted(concentric_cirlce_clusters, key=lambda e: -max(e[-1][1]))
Ejemplo n.º 2
0
def find_concetric_circles(gray_img,min_ring_count=3, visual_debug=False):

    # get threshold image used to get crisp-clean edges using blur to remove small features
    edges = cv2.adaptiveThreshold(cv2.blur(gray_img,(3,3)), 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 11)
    contours, hierarchy = cv2.findContours(edges,
                                    mode=cv2.RETR_TREE,
                                    method=cv2.CHAIN_APPROX_NONE,offset=(0,0)) #TC89_KCOS
    if visual_debug is not False:
        cv2.drawContours(visual_debug,contours,-1,(200,0,0))
    if contours is None or hierarchy is None:
        return []
    clusters = get_nested_clusters(contours,hierarchy[0],min_nested_count=min_ring_count)
    concentric_cirlce_clusters = []





    #speed up code by caching computed ellipses
    ellipses = {}


    # for each cluster fit ellipses and cull members that dont have good ellipse fit
    for cluster in clusters:
        if visual_debug is not False:
            cv2.drawContours(visual_debug, [contours[i] for i in cluster],-1, (0,0,255))
        candidate_ellipses = []
        for i in cluster:
            c = contours[i]
            if len(c)>5:
                if not ellipses.has_key(i):
                    e = cv2.fitEllipse(c)
                    fit = max(dist_pts_ellipse(e,c))
                    ellipses[i] = e,fit
                else:
                    e,fit = ellipses[i]
                a,b = e[1][0]/2.,e[1][1]/2.
                if fit<max(2,max(e[1])/20):
                    candidate_ellipses.append(e)
                    if visual_debug is not False:
                        cv2.ellipse(visual_debug, e, (0,255,0),1)

        if candidate_ellipses:
            cluster_center = np.mean(np.array([e[0] for e in candidate_ellipses]),axis=0)
            candidate_ellipses = [e for e in candidate_ellipses if np.linalg.norm(e[0]-cluster_center)<max(3,min(e[1])/20) ]
            if len(candidate_ellipses) >= min_ring_count:
                concentric_cirlce_clusters.append(candidate_ellipses)
                if visual_debug is not False:
                    cv2.ellipse(visual_debug, candidate_ellipses[-1], (0,255,255),4)

    #return clusters sorted by size of outmost cirlce biggest first.
    return sorted(concentric_cirlce_clusters,key=lambda e:-max(e[-1][1]))
Ejemplo n.º 3
0
def find_concentric_circles(
    edge,
    scale,
    img_contrast,
    found_pos,
    found_size,
    first_check=True,
    min_ellipses_num=2,
):
    if first_check:
        concentric_circle_clusters = []

        # OpenCV version compatibility note:
        # - Opencv3 returns three results: image, contours, hierarchy
        # - Opencv4 returns two results: contours, hierarchy
        # We do not use `image` in any case. Therefore, we can ensure
        # compatibility by using `*_`
        *_, contours, hierarchy = cv2.findContours(
            edge, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_TC89_KCOS)
        # We use CHAIN_APPROX_TC89_KCOS because it is faster than
        # the default method CV_CHAIN_APPROX_NONE

        if contours is None or hierarchy is None:
            return []
        clusters = get_nested_clusters(contours, hierarchy[0],
                                       min_ellipses_num)
        # speed up code by caching computed ellipses
        ellipses = {}

        for cluster in clusters:
            candidate_ellipses = []
            first_ellipse = True
            for i in cluster:
                c = contours[i]
                if len(c) > 100:
                    continue
                if i in ellipses:
                    e, fit = ellipses[i]
                else:
                    if len(c) >= 5:
                        e = cv2.fitEllipse(c)
                        # Discard duplicates
                        if first_ellipse:
                            duplicates = [
                                k for k in range(len(found_pos))
                                if LA.norm(e[0] -
                                           found_pos[k]) < found_size[k] +
                                min(e[1])
                            ]
                            if len(duplicates) > 0:
                                ellipses[i] = e, 100.0
                                break
                            fit = 0
                        else:
                            fit = max(dist_pts_ellipse(e, c)) if min(
                                e[1]) else 0.0
                        e = e if min(e[1]) else (e[0], (0.1, 0.1), e[2])
                    else:
                        e_center = (
                            float(c[len(c) // 2][0][0]),
                            float(c[len(c) // 2][0][1]),
                        )
                        e = (e_center, (0.1, 0.1), 0.0)
                        # Discard duplicates
                        if first_ellipse:
                            duplicates = [
                                k for k in range(len(found_pos))
                                if LA.norm(e_center -
                                           found_pos[k]) < found_size[k] + 1
                            ]
                            if len(duplicates) > 0:
                                ellipses[i] = e, 100.0
                                break
                        fit = 0

                    ellipses[i] = e, fit

                # Discard the contour which does not fit the ellipse so well
                if first_ellipse or fit < max(1, max(e[1]) / 50):
                    e = (e[0], e[1], e[2], i)
                    candidate_ellipses.append(e)
                first_ellipse = False

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            # Discard the ellipses whose center is far away from the center of the second innermost ellipse
            cluster_center = np.array(candidate_ellipses[1][0])
            if max(candidate_ellipses[-1][1]) > 200:
                candidate_ellipses = [
                    e for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 5
                ]
            elif max(candidate_ellipses[-1][1]) > 100:
                candidate_ellipses = [
                    e for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 10
                ]
            else:
                candidate_ellipses = [
                    e for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(max(e[1]) / 20, 3)
                ]

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            c = contours[candidate_ellipses[-1][3]]
            boundary = (
                (np.amin(c, axis=0)[0][0], np.amax(c, axis=0)[0][0]),
                (np.amin(c, axis=0)[0][1], np.amax(c, axis=0)[0][1]),
            )

            candidate_ellipses = [(e[0], e[1], e[2])
                                  for e in candidate_ellipses]
            concentric_circle_clusters.append((candidate_ellipses, boundary))

        # Return clusters sorted by the number of ellipses and the size of largest ellipse
        return sorted(concentric_circle_clusters,
                      key=lambda x: (-len(x[0]), -max(x[0][-1][1])))

    else:
        *_, contours, hierarchy = cv2.findContours(
            edge, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)

        if contours is None or hierarchy is None:
            return []
        clusters = get_nested_clusters(contours, hierarchy[0],
                                       min_ellipses_num)
        # speed up code by caching computed ellipses
        ellipses = {}

        for cluster in clusters:
            candidate_ellipses = []
            first_ellipse = True
            for i in cluster:
                c = contours[i]
                if i in ellipses:
                    e, fit = ellipses[i]
                else:
                    if len(c) >= 5:
                        e = cv2.fitEllipse(c)
                        fit = max(dist_pts_ellipse(e, c)) if min(e[1]) else 0.0
                        if min(e[1]) == 0:
                            e = (e[0], (e[1][0] + 1.0, e[1][1] + 1.0), e[2])
                    else:
                        fit = 0
                        e_center = (
                            float(c[len(c) // 2][0][0]),
                            float(c[len(c) // 2][0][1]),
                        )
                        e_size = (
                            float(abs(c[-1][0][0] - c[0][0][0]) + 1),
                            float(abs(c[-1][0][1] - c[0][0][1]) + 1),
                        )
                        e = (e_center, e_size, 0)
                    ellipses[i] = e, fit
                # Discard the contour which does not fit the ellipse so well
                if first_ellipse:
                    fit_thres = 0.5 + (256 - img_contrast) / 256
                else:
                    if img_contrast <= 96:
                        fit_thres = max(
                            e[1]) * scale / 10 + (256 - img_contrast) / 256
                    else:
                        fit_thres = max(0.5, max(e[1]) * scale / 10)

                if fit < fit_thres:
                    candidate_ellipses.append(e)
                    if len(candidate_ellipses) == 4:
                        break
                first_ellipse = False

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            # Discard the ellipses whose center is far away from the center of the innermost ellipse
            cluster_center = np.array(candidate_ellipses[0][0])
            if max(candidate_ellipses[-1][1]) * scale > 200:
                candidate_ellipses = [
                    e for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 5
                ]
            elif max(candidate_ellipses[-1][1]) * scale > 100:
                candidate_ellipses = [
                    e for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 10
                ]
            else:
                candidate_ellipses = [
                    e for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(max(e[1]) / 20, 2)
                ]

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            return [(candidate_ellipses, [[0, 0], [0, 0]])]

    return []
Ejemplo n.º 4
0
def find_concentric_circles(
    edge,
    scale,
    img_contrast,
    found_pos,
    found_size,
    first_check=True,
    min_ellipses_num=2,
):
    if first_check:
        concentric_circle_clusters = []

        # OpenCV version compatibility note:
        # - Opencv3 returns three results: image, contours, hierarchy
        # - Opencv4 returns two results: contours, hierarchy
        # We do not use `image` in any case. Therefore, we can ensure
        # compatibility by using `*_`
        *_, contours, hierarchy = cv2.findContours(
            edge, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_TC89_KCOS
        )
        # We use CHAIN_APPROX_TC89_KCOS because it is faster than
        # the default method CV_CHAIN_APPROX_NONE

        if contours is None or hierarchy is None:
            return []
        clusters = get_nested_clusters(contours, hierarchy[0], min_ellipses_num)
        # speed up code by caching computed ellipses
        ellipses = {}

        for cluster in clusters:
            candidate_ellipses = []
            first_ellipse = True
            for i in cluster:
                c = contours[i]
                if len(c) > 100:
                    continue
                if i in ellipses:
                    e, fit = ellipses[i]
                else:
                    if len(c) >= 5:
                        e = cv2.fitEllipse(c)
                        # Discard duplicates
                        if first_ellipse:
                            duplicates = [
                                k
                                for k in range(len(found_pos))
                                if LA.norm(e[0] - found_pos[k])
                                < found_size[k] + min(e[1])
                            ]
                            if len(duplicates) > 0:
                                ellipses[i] = e, 100.0
                                break
                            fit = 0
                        else:
                            fit = max(dist_pts_ellipse(e, c)) if min(e[1]) else 0.0
                        e = e if min(e[1]) else (e[0], (0.1, 0.1), e[2])
                    else:
                        e_center = (
                            float(c[len(c) // 2][0][0]),
                            float(c[len(c) // 2][0][1]),
                        )
                        e = (e_center, (0.1, 0.1), 0.0)
                        # Discard duplicates
                        if first_ellipse:
                            duplicates = [
                                k
                                for k in range(len(found_pos))
                                if LA.norm(e_center - found_pos[k]) < found_size[k] + 1
                            ]
                            if len(duplicates) > 0:
                                ellipses[i] = e, 100.0
                                break
                        fit = 0

                    ellipses[i] = e, fit

                # Discard the contour which does not fit the ellipse so well
                if first_ellipse or fit < max(1, max(e[1]) / 50):
                    e = (e[0], e[1], e[2], i)
                    candidate_ellipses.append(e)
                first_ellipse = False

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            # Discard the ellipses whose center is far away from the center of the second innermost ellipse
            cluster_center = np.array(candidate_ellipses[1][0])
            if max(candidate_ellipses[-1][1]) > 200:
                candidate_ellipses = [
                    e
                    for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 5
                ]
            elif max(candidate_ellipses[-1][1]) > 100:
                candidate_ellipses = [
                    e
                    for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 10
                ]
            else:
                candidate_ellipses = [
                    e
                    for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(max(e[1]) / 20, 3)
                ]

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            c = contours[candidate_ellipses[-1][3]]
            boundary = (
                (np.amin(c, axis=0)[0][0], np.amax(c, axis=0)[0][0]),
                (np.amin(c, axis=0)[0][1], np.amax(c, axis=0)[0][1]),
            )

            candidate_ellipses = [(e[0], e[1], e[2]) for e in candidate_ellipses]
            concentric_circle_clusters.append((candidate_ellipses, boundary))

        # Return clusters sorted by the number of ellipses and the size of largest ellipse
        return sorted(
            concentric_circle_clusters, key=lambda x: (-len(x[0]), -max(x[0][-1][1]))
        )

    else:
        *_, contours, hierarchy = cv2.findContours(
            edge, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE
        )

        if contours is None or hierarchy is None:
            return []
        clusters = get_nested_clusters(contours, hierarchy[0], min_ellipses_num)
        # speed up code by caching computed ellipses
        ellipses = {}

        for cluster in clusters:
            candidate_ellipses = []
            first_ellipse = True
            for i in cluster:
                c = contours[i]
                if i in ellipses:
                    e, fit = ellipses[i]
                else:
                    if len(c) >= 5:
                        e = cv2.fitEllipse(c)
                        fit = max(dist_pts_ellipse(e, c)) if min(e[1]) else 0.0
                        if min(e[1]) == 0:
                            e = (e[0], (e[1][0] + 1.0, e[1][1] + 1.0), e[2])
                    else:
                        fit = 0
                        e_center = (
                            float(c[len(c) // 2][0][0]),
                            float(c[len(c) // 2][0][1]),
                        )
                        e_size = (
                            float(abs(c[-1][0][0] - c[0][0][0]) + 1),
                            float(abs(c[-1][0][1] - c[0][0][1]) + 1),
                        )
                        e = (e_center, e_size, 0)
                    ellipses[i] = e, fit
                # Discard the contour which does not fit the ellipse so well
                if first_ellipse:
                    fit_thres = 0.5 + (256 - img_contrast) / 256
                else:
                    if img_contrast <= 96:
                        fit_thres = max(e[1]) * scale / 10 + (256 - img_contrast) / 256
                    else:
                        fit_thres = max(0.5, max(e[1]) * scale / 10)

                if fit < fit_thres:
                    candidate_ellipses.append(e)
                    if len(candidate_ellipses) == 4:
                        break
                first_ellipse = False

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            # Discard the ellipses whose center is far away from the center of the innermost ellipse
            cluster_center = np.array(candidate_ellipses[0][0])
            if max(candidate_ellipses[-1][1]) * scale > 200:
                candidate_ellipses = [
                    e
                    for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 5
                ]
            elif max(candidate_ellipses[-1][1]) * scale > 100:
                candidate_ellipses = [
                    e
                    for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(e[1]) / 10
                ]
            else:
                candidate_ellipses = [
                    e
                    for e in candidate_ellipses
                    if LA.norm(e[0] - cluster_center) < max(max(e[1]) / 20, 2)
                ]

            # Discard false positives
            if len(candidate_ellipses) < min_ellipses_num:
                continue

            return [(candidate_ellipses, [[0, 0], [0, 0]])]

    return []