Python OpenCV 顺时针排序轮廓

Python OpenCV sorting contours in clockwise

我正在组装一个图像处理工具,以使用图像跟踪零件的变形。该零件具有矩形标记,可通过图像分割和 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

图片在这里:

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

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);