16

我想測量圓的圓度(「圓」的高度和寬度或橢圓參數的差異)。該圓圈中的圖片給出如下所示:用OpenCV和Python檢測觸摸/重疊圓/橢圓

做平常的東西像color2gray,閾值和邊界檢測之後,我得到如下圖所示:

有了這個,我已經嘗試了很多不同的東西:

  • 列表項與findContour的流域(類似於this question) - > openCV將圓圈之間的空間檢測爲閉合輪廓,而不是圓圈,因爲它們粘在一起不形成閉合輪廓
  • 與fitEllipse相同的問題。我在橢圓形背景上安裝橢圓而不是在兩者之間。
  • 只是想申請霍夫transforamtion(如代碼,並顯示第三畫面),以及導致奇怪的結果:

在這裏看到的代碼:

import sys 
import cv2 
import numpy 
from scipy.ndimage import label 

# Application entry point 
#img = cv2.imread("02_adj_grey.jpg") 
img = cv2.imread("fuss02.jpg") 

# Pre-processing. 
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  
cv2.imwrite("SO_0_gray.png", img_gray) 

#_, img_bin = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY) 
_, img_bin = cv2.threshold(img_gray, 170, 255, cv2.THRESH_BINARY) 
cv2.imwrite("SO_1_threshold.png", img_bin) 

#blur = cv2.GaussianBlur(img,(5,5),0) 
img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_CLOSE, numpy.ones((3, 3), dtype=int)) 
cv2.imwrite("SO_2_img_bin_morphoEx.png", img_bin) 

border = img_bin - cv2.erode(img_bin, None) 
cv2.imwrite("SO_3_border.png", border) 


circles = cv2.HoughCircles(border,cv2.cv.CV_HOUGH_GRADIENT,50,80, param1=80,param2=40,minRadius=10,maxRadius=150) 
print circles 

cimg = img 
for i in circles[0,:]: 
# draw the outer circle 
    cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2) 
    # draw the center of the circle 
    cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3) 
    cv2.putText(cimg,str(i[0])+str(',')+str(i[1]), (i[0],i[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.4, 255) 

cv2.imwrite("SO_8_cimg.png", cimg) 

有沒有人有想法來改進我的算法或完全不同的方法?到目前爲止,我一直在嘗試許多不同的方法,但沒有成功。謝謝大家的幫助。

+0

你是否對從圖像中提取出界的一個問題?我不太喜歡你想要的。 – rayryeng 2014-11-14 17:25:47

+0

是的,我無法像上面的邊框檢測圖像中看到的那樣分開圓圈。在過濾等過程中,很多邊框都會丟失。 – Merlin 2014-11-15 16:37:38

+0

我有一些想法。給我一點點 – rayryeng 2014-11-15 16:47:23

回答

32

這是我在檢測圓圈的嘗試。總之

  • 執行BGR-> HSV的轉換,並使用V通道進行處理

V通道:

enter image description here

  • 閾值時,應用形態學閉,則取距離變換(我會稱之爲dist

DIST圖像:

enter image description here

  • 創建模板。從圖像中圓圈的大小看,一個~75像素半徑的磁盤看起來是合理的。以它的距離變換,並用它作爲模板(我稱之爲臨時

溫度圖像:

enter image description here

  • 執行模板匹配:DIST * temp

DIST *臨時圖像:

enter image description here

  • 找到最終圖像的局部最大值。最大值的位置對應於圓心和最大值對應其半徑

閾值模板匹配的圖像:

enter image description here

檢測界稱爲局部最大值:

enter image description here

我用C++做了這個,因爲我對它很滿意。我認爲你可以很容易地將它轉換爲Python,如果你覺得這很有用。請注意,上面的圖片不是按比例繪製的。希望這可以幫助。

編輯:增加了Python版本

C++:

double min, max; 
    Point maxLoc; 

    Mat im = imread("04Bxy.jpg"); 
    Mat hsv; 
    Mat channels[3]; 
    // bgr -> hsv 
    cvtColor(im, hsv, CV_BGR2HSV); 
    split(hsv, channels); 
    // use v channel for processing 
    Mat& ch = channels[2]; 
    // apply Otsu thresholding 
    Mat bw; 
    threshold(ch, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); 
    // close small gaps 
    Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3)); 
    Mat morph; 
    morphologyEx(bw, morph, CV_MOP_CLOSE, kernel); 
    // take distance transform 
    Mat dist; 
    distanceTransform(morph, dist, CV_DIST_L2, CV_DIST_MASK_PRECISE); 
    // add a black border to distance transformed image. we are going to do 
    // template matching. to get a good match for circles in the margin, we are adding a border 
    int borderSize = 75; 
    Mat distborder(dist.rows + 2*borderSize, dist.cols + 2*borderSize, dist.depth()); 
    copyMakeBorder(dist, distborder, 
      borderSize, borderSize, borderSize, borderSize, 
      BORDER_CONSTANT | BORDER_ISOLATED, Scalar(0, 0, 0)); 
    // create a template. from the sizes of the circles in the image, 
    // a ~75 radius disk looks reasonable, so the borderSize was selected as 75 
    Mat distTempl; 
    Mat kernel2 = getStructuringElement(MORPH_ELLIPSE, Size(2*borderSize+1, 2*borderSize+1)); 
    // erode the ~75 radius disk a bit 
    erode(kernel2, kernel2, kernel, Point(-1, -1), 10); 
    // take its distance transform. this is the template 
    distanceTransform(kernel2, distTempl, CV_DIST_L2, CV_DIST_MASK_PRECISE); 
    // match template 
    Mat nxcor; 
    matchTemplate(distborder, distTempl, nxcor, CV_TM_CCOEFF_NORMED); 
    minMaxLoc(nxcor, &min, &max); 
    // threshold the resulting image. we should be able to get peak regions. 
    // we'll locate the peak of each of these peak regions 
    Mat peaks, peaks8u; 
    threshold(nxcor, peaks, max*.5, 255, CV_THRESH_BINARY); 
    convertScaleAbs(peaks, peaks8u); 
    // find connected components. we'll use each component as a mask for distance transformed image, 
    // then extract the peak location and its strength. strength corresponds to the radius of the circle 
    vector<vector<Point>> contours; 
    vector<Vec4i> hierarchy; 
    findContours(peaks8u, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); 
    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0]) 
    { 
     // prepare the mask 
     peaks8u.setTo(Scalar(0, 0, 0)); 
     drawContours(peaks8u, contours, idx, Scalar(255, 255, 255), -1); 
     // find the max value and its location in distance transformed image using mask 
     minMaxLoc(dist, NULL, &max, NULL, &maxLoc, peaks8u); 
     // draw the circles 
     circle(im, maxLoc, (int)max, Scalar(0, 0, 255), 2); 
    } 

的Python:

import cv2 

im = cv2.imread('04Bxy.jpg') 
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) 
th, bw = cv2.threshold(hsv[:, :, 2], 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) 
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) 
morph = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel) 
dist = cv2.distanceTransform(morph, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE) 
borderSize = 75 
distborder = cv2.copyMakeBorder(dist, borderSize, borderSize, borderSize, borderSize, 
           cv2.BORDER_CONSTANT | cv2.BORDER_ISOLATED, 0) 
gap = 10         
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2*(borderSize-gap)+1, 2*(borderSize-gap)+1)) 
kernel2 = cv2.copyMakeBorder(kernel2, gap, gap, gap, gap, 
           cv2.BORDER_CONSTANT | cv2.BORDER_ISOLATED, 0) 
distTempl = cv2.distanceTransform(kernel2, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE) 
nxcor = cv2.matchTemplate(distborder, distTempl, cv2.TM_CCOEFF_NORMED) 
mn, mx, _, _ = cv2.minMaxLoc(nxcor) 
th, peaks = cv2.threshold(nxcor, mx*0.5, 255, cv2.THRESH_BINARY) 
peaks8u = cv2.convertScaleAbs(peaks) 
contours, hierarchy = cv2.findContours(peaks8u, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) 
peaks8u = cv2.convertScaleAbs(peaks) # to use as mask 
for i in range(len(contours)): 
    x, y, w, h = cv2.boundingRect(contours[i]) 
    _, mx, _, mxloc = cv2.minMaxLoc(dist[y:y+h, x:x+w], peaks8u[y:y+h, x:x+w]) 
    cv2.circle(im, (int(mxloc[0]+x), int(mxloc[1]+y)), int(mx), (255, 0, 0), 2) 
    cv2.rectangle(im, (x, y), (x+w, y+h), (0, 255, 255), 2) 
    cv2.drawContours(im, contours, i, (0, 0, 255), 2) 

cv2.imshow('circles', im) 
+0

+1非常好的解釋 – 2014-11-17 14:04:58

+0

我同意!非常好! – rayryeng 2014-11-18 07:41:31

+0

WOW。非常好。 – Merlin 2014-11-18 09:17:55