Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

Python OpenCV sorting contours in clockwise

I'm putting together an image processing tool to follow the deformation of a part using images. The part has rectangular markers that get detected with image segmentation and cv2.findContours function. Contour centers are then used to calculate distances and to bend radiuses. Everything seems to work fine, but I found out that the contours aren't sorted how I would like to sort them when reviewing results. The part is repeatedly bent, and the contours are positioned in a circle.

I found this article that describes the sorting horizontally and vertically:

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

Does anyone have any idea how to sort the contours in a clockwise direction?

The code is below.

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

Image is here: enter image description here

question from:https://stackoverflow.com/questions/66047399/python-opencv-sorting-contours-in-clockwise

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I used openCV's minEnclosingCircle to "fit" a circle to the points (it's not actually a fit, but it's good enough for finding a point inside the curvature of the markers). Marking each contour with the angle from its centroid to the circle's center gave me a set of angles that I could sort with.

enter image description here

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...