【问题标题】:Python OpenCV sorting contours in clockwisePython OpenCV顺时针排序轮廓
【发布时间】:2021-05-08 20:35:43
【问题描述】:

我正在组装一个图像处理工具,以使用图像跟踪零件的变形。该部件具有矩形标记,可通过图像分割和 cv2.findContours 函数检测到。然后使用轮廓中心来计算距离和弯曲半径。一切似乎都很好,但我发现轮廓没有按照我在查看结果时的排序方式进行排序。 零件反复折弯,轮廓定位成圆形。

我发现这篇文章描述了水平和垂直排序:

https://www.pyimagesearch.com/2015/04/20/sorting-contours-using-python-and-opencv/

有人知道如何按顺时针方向对轮廓进行排序吗?

代码如下。

import os
import exifread
import cv2
import numpy as np
import scipy
from matplotlib import pyplot as plt
import imutils
import pandas as pd


#---------- INPUT ----------

# Define the image filename
img_filename = 'frame397.jpg'

img_path = img_filename

# Define values for cropping
x = 0
y = 200
w = 1200
h = 800

# Define color values for segmentation
# the values can be probed with GIMP

h1 = 0
s1 = 70
v1 = 120
h2 = 255
s2 = 255
v2 = 255

red_lower = np.array([h1,s1,v1])
red_upper = np.array([h2,s2,v2])

# Define desired area size
# desired area size is pixel count - use GIMP for probe
s1 = 500
s2 = 10000


#---------- PROCESS IMAGES ----------

# Create an empty dataframe for storing results
# in shape of (image_name,time,angle,angle_smooth,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11)

# Define the results dataframe shape and column names
results_df = pd.DataFrame(columns=['image_name','alpha','r1','r2','r3','r4','r5','r6','r7','r8','r9','r10','r11',
                                   'center_dist1', 'center_dist2','center_dist3','center_dist4',
                                   'center_dist5','center_dist6','center_dist7','center_dist8',
                                   'center_dist9','center_dist10','center_dist11'])

# Open image, make it black and white and find contours
img = cv2.imread(img_path)
crop = img[y:y+h, x:x+w]
blur = cv2.blur(crop,(2,2))
hsv = cv2.cvtColor(blur,cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, red_lower, red_upper)
mask_copy = mask.copy()
cnts = cv2.findContours(mask_copy,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
#print cnts
x = []
y = []

# Loop through contours, calculate the centers and prepare the
#contours and contour centers display

#define the font for the text on the image
font = cv2.FONT_HERSHEY_SIMPLEX

for cnt in cnts:
    area = cv2.contourArea(cnt)
    moment = cv2.moments(cnt)
    if s1<area<s2:
        print area
        c_x = int(moment["m10"]/moment["m00"])
        c_y = int(moment["m01"]/moment["m00"])
        #draw contours
        cv2.drawContours(crop, cnt, -1, (0,255,0),3)
        #draw a circle in the center of every contour, -1 is for thickness, this means
        #that the cirlce will get filled in
        cv2.circle(crop, (c_x,c_y), 10, (0,255,0),-1)
        #display center coordinates on the image
        string = str(c_x) + ',' + str(c_y)
        cv2.putText(crop,string,(c_x,c_y),font,0.5,(255,255,255),2)
        x.append(float(c_x))
        y.append(float(c_y))
        print (c_x, c_y)

print x
print y

# Display image
cv2.namedWindow('Contours', cv2.WINDOW_NORMAL)
cv2.resizeWindow('Contours', 1200,900)
cv2.imshow('Contours', crop)

# Wait for windows closing
cv2.waitKey() & 0xFF
cv2.destroyAllWindows

图片在这里:

【问题讨论】:

    标签: python opencv opencv-contour


    【解决方案1】:

    我使用 openCV 的 minEnclosureCircle 将一个圆“拟合”到这些点上(它实际上并不合适,但它足以在标记的曲率内找到一个点)。用从其质心到圆心的角度标记每个轮廓,为我提供了一组可以用来排序的角度。

    import cv2
    import numpy as np
    import math
    
    # 2d distance
    def dist2D(one, two):
        dx = one[0] - two[0];
        dy = one[1] - two[1];
        return math.sqrt(dx*dx + dy*dy);
    
    # angle between three points (the last point is the middle)
    def angle3P(p1, p2, p3):
        # get distances
        a = dist2D(p3, p1);
        b = dist2D(p3, p2);
        c = dist2D(p1, p2);
    
        # calculate angle // assume a and b are nonzero
        # (law of cosines)
        numer = c**2 - a**2 - b**2;
        denom = -2 * a * b;
        if denom == 0:
            denom = 0.000001;
        rads = math.acos(numer / denom);
        degs = math.degrees(rads);
    
        # check if past 180 degrees
        if p1[1] > p3[1]:
            degs = 360 - degs;
        return degs;
    
    # load image
    img = cv2.imread("slinky.jpg");
    
    # rescale
    scale = 0.5;
    h, w = img.shape[:2];
    h = int(h * scale);
    w = int(w * scale);
    img = cv2.resize(img, (w,h));
    
    # change color space
    lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB);
    l,a,b = cv2.split(lab);
    
    # threshold
    thresh = cv2.inRange(a, 140, 255);
    
    # get rid of little dots
    kernel = np.ones((3,3),np.uint8)
    thresh = cv2.erode(thresh,kernel,iterations = 1);
    thresh = cv2.dilate(thresh,kernel, iterations = 1);
    
    # contours
    _, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
    
    # get centroids
    centroids = [];
    centers = [];
    for con in contours:
        m = cv2.moments(con);
        cx = int(m['m10'] / m['m00']);
        cy = int(m['m01'] / m['m00']);
        centers.append([cx, cy]);
        centroids.append([[cx, cy], con]);
        img = cv2.circle(img, (cx, cy), 10, (0,0,255), -1);
    
    # find circle around points
    # NOTE: this doesn't "fit" a circle to the points
    # I'm just using this to find a "good enough" center 
    # that's in the direction of the curve
    numped = np.array(centers);
    (x, y), radius = cv2.minEnclosingCircle(numped);
    img = cv2.circle(img, (int(x), int(y)), int(radius), (255,0,0), 2);
    middle = [x,y];
    offshoot = [x + 100, y];
    
    # get angles
    angles = [];
    for cen in centroids:
        center, contour = cen;
        angle = angle3P(center, offshoot, middle);
        angles.append([angle, center, contour]);
    
    # sort by angle
    final = sorted(angles, key = lambda a: a[0], reverse = True);
    
    # pull out just the contours
    contours = [clump[2] for clump in final];
    
    # draw contours in order
    marked = img.copy();
    counter = 0;
    for con in contours:
        cv2.drawContours(marked, [con], -1, (0, 255, 0), 2);
        cv2.imshow("marked", marked);
        cv2.imwrite("marking_seq/" + str(counter) + ".png", marked);
        counter += 1;
        cv2.waitKey(0);
    
    # show
    cv2.imshow("orig", img);
    cv2.imshow("a", a);
    cv2.imshow("thresh", thresh);
    cv2.waitKey(0);
    

    【讨论】:

    • 这是一个很棒的技巧,而且很有效!关于其他代码的几个问题:1。为什么使用 LAB 色彩空间? 2. 为什么在 findContours 中使用 cv2.RETR_TREE? 3. 你提到你必须去除的小圆点是什么? 4. 代码中“拉出轮廓”部分的clump是什么?
    • 我使用了 LAB 颜色空间,因为我不喜欢在 HSV 中找到红色,因为红色从 0 到 180 环绕。 findContours 中的 cv2.RETR_TREE 完全是任意的,请随意使用不同的参数如果您想使用不同的层次结构。小点只是图像掩码中的噪声位。一般来说,我会在阈值化后做一个这样的打开操作,以摆脱微小的规格。您还可以按轮廓大小过滤掉这些。 Clump 只是 for 循环中的一个临时变量名。该 for 循环遍历 final 中的每个项目。
    • 为了进一步澄清该行。 final 只是角度列表的排序副本。每个元素是[角度,中心,轮廓]。因此,当它以“for clump in final”进行迭代时,每个 clump 都是 [angle, center, contour] 的列表,这就是我们想要 clump[2] 的原因。
    • 如果您直接复制此代码,您可能还需要注释掉 cv2.imwrite 行。或者您需要将“marking_seq”文件夹添加到运行此代码的任何位置。
    • 好的,感谢您的解释。我将研究 LAB 色彩空间。
    猜你喜欢
    • 2017-01-17
    • 2021-09-14
    • 2015-12-16
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多