简体   繁体   English

Python opencv 检测具有交叉点的形状

[英]Python opencv detect shapes with intersections

We are conducting shape detection using Python openCV.我们正在使用 Python openCV 进行形状检测。 We find approx_poly_dp and then count number of vertices.我们找到 approx_poly_dp 然后计算顶点数。 However, the process does not work when object has many intersections (seen in second picture below).但是,当 object 有许多交叉点时(如下图第二张所示),该过程不起作用。 Opencv cannot detect individual objects, and just finds one large shape. Opencv 无法检测单个物体,只能找到一个大形状。 In dealing with intersections, what is best way to find shapes, given general picture inputs.在处理交叉点时,在给定一般图片输入的情况下,找到形状的最佳方法是什么。 Or is this not a functionality of opencv, maybe better suited with machine learning?或者这不是 opencv 的功能,也许更适合机器学习?

    image_original = cv2.imread(file_name)
    image_gray = cv2.cvtColor(image_original, cv2.COLOR_BGR2GRAY)
    image_blur = cv2.GaussianBlur(image_gray, (5, 5), 0)
    image_morph = cv2.morphologyEx(image_blur, cv2.MORPH_OPEN, kernel)
    image_canny = cv2.Canny(image_morph, 50, 50)
    image_dilate = cv2.dilate(image_canny, kernel, iterations=2)
    image_final = cv2.erode(image_dilate, kernel, iterations=1)

    contours, hierarchy = cv2.findContours(image_final, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    peri = cv2.arcLength(contours, True)
    approx = cv2.approxPolyDP(contours, 0.04 * peri, True)

    if len(approx) == 3:
        shape = "triangle"
    elif len(approx) == 4:
        (x, y, w, h) = cv2.boundingRect(approx)
        ar = w / float(h)
        shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
    elif len(approx) == 5:
        shape = "pentagon"
    else:
        shape = "circle"

在此处输入图像描述

Intersection Picture:路口图片:

在此处输入图像描述

Following procedure might work for simple figures:以下过程可能适用于简单的数字:

Find the connected components, and find neighboring components of each component ignoring the background and outline components.查找连接的组件,并找到每个组件的相邻组件,忽略背景和轮廓组件。

Try a component with combinations of its neighboring components and feed it to the shape classifier that you already have.尝试一个具有相邻组件组合的组件,并将其提供给您已有的形状分类器。 As a pre-processing step, you might have to combine the components into a single blob using a morphological closing.作为预处理步骤,您可能必须使用形态闭合将组件组合成单个 blob。

import cv2 as cv
import numpy as np
from itertools import combinations

im = np.zeros((512, 512), dtype=np.uint8)
im = cv.rectangle(im, (100, 200), (400, 400), 255, 2)
im = cv.circle(im, (250, 380), 100, 255, 2)
im = cv.rectangle(im, (50, 50), (250, 250), 255, 2)

ret, bw = cv.threshold(im, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
ncomp, labels, stats, centroids = cv.connectedComponentsWithStats(bw)

def getNeighbors(labels, n):
    r = 4 # you might have to change this depending on outline thickness
    se = cv.getStructuringElement(cv.MORPH_CROSS, (2*r+1, 2*r+1))
    neighbors = cv.dilate((labels==n).astype(np.uint8), se)
    # ignore background, outline and the component of interest
    return np.setdiff1d(np.unique(labels[neighbors==1]), [0, 1, n])

def shapes(arr, i):
    p = arr.tolist()
    for r in range(1, len(p)+1):
        for l in list(combinations(p, r)):
            print l + (i, )

For the following figure, you get the listed combinations of connected components.对于下图,您将获得列出的连接组件组合。 Note that I haven't bothered to remove duplicates.请注意,我没有费心删除重复项。

for i in range(2, ncomp):
    shapes(getNeighbors(labels, i), i)

(3, 2)
(2, 3)
(4, 3)
(2, 4, 3)
(3, 4)
(5, 4)
(3, 5, 4)
(4, 5)
(6, 5)
(4, 6, 5)
(5, 6)

IM1 抄送

I wouldn't say this is an efficient solution as it involves trying out all combinations of neighbors, but I'm publishing this just in case someone finds this image processing based solution useful.我不会说这是一个有效的解决方案,因为它涉及尝试所有邻居组合,但我发布这个以防万一有人发现这个基于图像处理的解决方案有用。

I came up with a solution to detect shape from an intersected image by using parent and child contour relation and contour smoothing method.我想出了一个解决方案,通过使用父子轮廓关系和轮廓平滑方法从相交的图像中检测形状。

输出图像一

In the above last image red border is the parent contour and yellow is the child contour's border.在上图中,红色边框是父轮廓,黄色是子轮廓的边框。 Iterate all child contour with parent contour apply contour smoothing approach to extract shapes.使用父轮廓迭代所有子轮廓应用轮廓平滑方法来提取形状。 This error smoothing approach is discussed in my question by ian-chu ian-chu我的问题中讨论了这种错误平滑方法

import cv2
import numpy as np


def get_parent_contour(contours, hierarchy):
    parent = hierarchy[0, :, 3]
    unique_parent, counts_parent = np.unique(parent, return_counts=True)

    parent_contour_id = -1
    max_area = 0

    for c in unique_parent:
        if c != -1:
            area = cv2.contourArea(contours[c])
            if area > max_area:
                max_area = area
                parent_contour_id = c

    return parent_contour_id, contours[parent_contour_id]


def get_child_contours(contours, hierarchy, parent_contour_id):
    parent = hierarchy[0, :, 3]

    # Select all contour whose parent is selected parent above
    child_contour_id = [i for i, v in enumerate(parent) if v == parent_contour_id]
    child_contours = [contours[i] for i in child_contour_id]

    return child_contour_id, child_contours


def smoothed(contour, dists, cutoff):
    smooth_con = []
    for a in range(len(dists)):
        if dists[a] < cutoff:
            smooth_con.append(contour[a])
    return np.asarray(smooth_con)


# get the distance list for an array of points
def distList(src, other):
    dists = []
    for point in src:
        point = point[0]  # drop extra brackets
        _, dist = closestPoint(point, other)
        dists.append(dist)
    return dists


# returns squared distance of two points
def squaredDist(one, two):
    dx = one[0] - two[0]
    dy = one[1] - two[1]
    return dx * dx + dy * dy


# find closest point (just do a linear search)
def closestPoint(point, arr):
    # init tracker vars
    closest = None
    best_dist = np.Inf

    # linear search
    for other in arr:
        other = other[0]  # remove extra brackets
        dist = squaredDist(point, other)
        if dist < best_dist:
            closest = other
            best_dist = dist
    return closest, best_dist


image = cv2.imread("image.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

_, threshold = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("threshold", threshold)

contours, hierarchy = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

blank_image = np.zeros_like(image)

if hierarchy is not None:

    for contour in contours:
        color = np.random.randint(0, 255, size=(3,))
        color = (int(color[0]), int(color[1]), int(color[2]))
        cv2.drawContours(blank_image, [contour], 0, color, 2)

cv2.imshow("contour image", blank_image)

parent_contour_id, parent_contour = get_parent_contour(contours, hierarchy)
child_contour_ids, child_contours = get_child_contours(contours, hierarchy, parent_contour_id)

blank_image_2 = np.zeros_like(image)
cv2.drawContours(blank_image_2, [parent_contour], 0, (0, 0, 255), 2)

for child_cnt in child_contours:
    cv2.drawContours(blank_image_2, [child_cnt], 0, (0, 255, 255), 1)
cv2.imshow("parent child contours", blank_image_2)

for child_cnt in child_contours:

    one = parent_contour
    two = child_cnt

    # get distances
    one_dists = distList(one, two)
    two_dists = distList(two, one)
    try:
        # dump values greater than 10
        smooth_one = smoothed(one, one_dists, 35)
        smooth_two = smoothed(two, two_dists, 35)

        # draw new contour
        blank_image_3 = np.zeros_like(threshold)
        cv2.drawContours(blank_image_3, [smooth_one], -1, 255, -1)
        cv2.drawContours(blank_image_3, [smooth_two], -1, 0, -1)

        cv2.imshow("smooth image", blank_image_3)
        
        # you can apply filters on smoothed contour image to accepting and reject smooth contour
        # And apply single shape detection algorithm on smoothed contour image to find shapes
        
    except:
        print("no near points. ")

    cv2.waitKey(0)

cv2.destroyAllWindows()

Smooth output Images平滑 output 图像

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM