2017-03-07 39 views
1

我正在檢測由激光束照射的圓形孔徑的中心和半徑。該算法將從我沒有物理控制的系統(即,調暗光源或調整激光位置)饋送圖像。我需要使用C++執行此操作,並選擇使用openCV。如何過濾邊緣檢測的極其嘈雜的圓點?

在一些地區,光圈的邊緣是明確的,但在其他地區是非常嘈雜。我目前正試圖隔離「好」點來做RANSAC,但我一直採取其他步驟。以下是參考兩個原始圖像:

testImage1 testImage2

我第一次開始試圖做霍夫配合。我執行了一箇中值模糊來去除鹽和胡椒噪聲,然後進行高斯模糊,然後將圖像提供給openCV中的HoughCircle函數,其中滑塊控制Hough參數1和2,定義爲here。結果是災難性的:bad results

然後我決定嘗試處理圖像,然後再發送到HoughCircle。我從原始圖像開始,模糊中值,高斯模糊,閾值化,擴張,Canny邊緣檢測,然後將Canny圖像提供給函數。

我最終能夠對我的圓圈進行合理的估計,但它是在手動降低Hough參數時出現的第15個圓圈。我手動繪製了紫色輪廓,綠色圓圈表示Hough輸出,這些輸出接近我的手動估計。以下圖像是:

  1. 而不擴張
  2. 坎尼輸出的Canny輸出與擴張
  3. 擴張的Canny算子圖像的霍夫輸出繪製的原始圖像上。

canny image test

正如你所看到的,無效的圈數大大租稅正確的一圈,我不太清楚如何好圓鑑於Hough變換回報隔離很多其他參數更加嚴格的無效圓圈。

我目前有一些我實現的代碼,對於我給出的所有測試圖像都可以正常工作,但代碼與許多可調參數似乎非常脆弱。我所做的背後的驅動邏輯是從注意到激光良好照明的光圈邊緣區域在幾個閾值水平上相對恆定(如下圖所示)。

image

我沒有邊緣檢測在兩個閾值電平,並且在兩個圖像重疊存儲的點。目前,結果也存在一些不準確的結果,因爲光圈邊緣在不同的閾值水平下仍然稍微變化。我可以張貼了很長的代碼,如果有必要對於這一點,但它背後的僞代碼:

1. Perform a median blur, followed by a Gaussian blur. Kernels are 9x9. 
2. Threshold the image until 35% of the image is white. (~intensities > 30) 
3. Take the Canny edges of this thresholded image and store (Canny1) 
4. Take the original image, perform the same median and Gaussian blurs, but threshold with a 50% larger value, giving a smaller spot (~intensities > 45) 
5. Perform the "Closing" morphology operation to further erode the spot and remove any smaller contours. 
6. Perform another Canny to get the edges, and store this image (Canny2) 
7. Blur both the Canny images with a 7x7 Gaussian blur. 
8. Take the regions where the two Canny images overlap and say that these points are likely to be good points. 
9. Do a RANSAC circle fit with these points. 

我注意到,有邊緣的區域中檢測到循環,是通過人眼分辨的漂亮作爲最佳圈子的一部分。有沒有辦法將這些區域分離出來以適合RANSAC?

代碼霍夫:

int houghParam1 = 100; 
int houghParam2 = 100; 
int dp = 10; //divided by 10 later 
int x=616; 
int y=444; 
int radius = 398; 
int iterations = 0; 

int main() 
{ 
namedWindow("Circled Orig"); 
namedWindow("Processed", 1); 
namedWindow("Circles"); 
namedWindow("Parameters"); 
namedWindow("Canny"); 
createTrackbar("Param1", "Parameters", &houghParam1, 200); 
createTrackbar("Param2", "Parameters", &houghParam2, 200); 
createTrackbar("dp", "Parameters", &dp, 20); 
createTrackbar("x", "Parameters", &x, 1200); 
createTrackbar("y", "Parameters", &y, 1200); 
createTrackbar("radius", "Parameters", &radius, 900); 
createTrackbar("dilate #", "Parameters", &iterations, 20); 
std::string directory = "Secret"; 
std::string suffix = ".pgm"; 
Mat processedImage; 
Mat origImg; 
for (int fileCounter = 2; fileCounter < 3; fileCounter++) //1, 12 
{ 
    std::string numString = std::to_string(static_cast<long long>(fileCounter)); 
    std::string imageFile = directory + numString + suffix; 
    testImage = imread(imageFile); 
    Mat bwImage; 
    cvtColor(testImage, bwImage, CV_BGR2GRAY); 
    GaussianBlur(bwImage, processedImage, Size(9, 9), 9); 
    threshold(processedImage, processedImage, 25, 255, THRESH_BINARY); //THRESH_OTSU 
    int numberContours = -1; 
    int iterations = 1; 
    imshow("Processed", processedImage); 
} 

vector<Vec3f> circles; 
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(5, 5)); 
float dp2 = dp; 
while (true) 
{ 
    float dp2 = dp; 
    Mat circleImage = processedImage.clone(); 
    origImg = testImage.clone(); 
    if (iterations > 0) dilate(circleImage, circleImage, element, Point(-1, -1), iterations); 
    Mat cannyImage; 
    Canny(circleImage, cannyImage, 100, 20); 
    imshow("Canny", cannyImage); 
    HoughCircles(circleImage, circles, HOUGH_GRADIENT, dp2/10, 5, houghParam1, houghParam2, 300, 5000); 
    cvtColor(circleImage, circleImage, CV_GRAY2BGR); 
    for (size_t i = 0; i < circles.size(); i++) 
    { 
     Scalar color = Scalar(0, 0, 255); 
     Point center2(cvRound(circles[i][0]), cvRound(circles[i][1])); 
     int radius2 = cvRound(circles[i][2]); 
     if (abs(center2.x - x) < 10 && abs((center2.y - y) < 10) && abs(radius - radius2) < 20) color = Scalar(0, 255, 0); 
     circle(circleImage, center2, 3, color, -1, 8, 0); 
     circle(circleImage, center2, radius2, color, 3, 8, 0); 
     circle(origImg, center2, 3, color, -1, 8, 0); 
     circle(origImg, center2, radius2,color, 3, 8, 0); 
    } 
    //Manual circles 
    circle(circleImage, Point(x, y), 3, Scalar(128, 0, 128), -1, 8, 0); 
    circle(circleImage, Point(x, y), radius, Scalar(128, 0, 128), 3, 8, 0); 
    circle(origImg, Point(x, y), 3, Scalar(128, 0, 128), -1, 8, 0); 
    circle(origImg, Point(x, y), radius, Scalar(128, 0, 128), 3, 8, 0); 
    imshow("Circles", circleImage); 
    imshow("Circled Orig", origImg); 
    int x = waitKey(50); 
} 
Mat drawnImage; 
cvtColor(processedImage, drawnImage, CV_GRAY2BGR); 
return 1; 
} 
+0

請問激光曾經相對於光圈移動?光圈的大小是否會改變,或者您的程序是否已知? –

+0

如果您認爲這有幫助,請考慮接受答案。 – m3h0w

回答

2

感謝@jalconvolvon - 這是一個有趣的問題。這是我的結果: enter image description here 我發現重要的是在原型時使用動態參數調整,因此我包含了用於調整Canny檢測的函數。該代碼還對Ransac部分使用this答案。

import cv2 
import numpy as np 
import auxcv as aux 
from skimage import measure, draw 

def empty_function(*arg): 
    pass 

# tune canny edge detection. accept with pressing "C" 
def CannyTrackbar(img, win_name): 
    trackbar_name = win_name + "Trackbar" 

    cv2.namedWindow(win_name) 
    cv2.resizeWindow(win_name, 500,100) 
    cv2.createTrackbar("canny_th1", win_name, 0, 255, empty_function) 
    cv2.createTrackbar("canny_th2", win_name, 0, 255, empty_function) 
    cv2.createTrackbar("blur_size", win_name, 0, 255, empty_function) 
    cv2.createTrackbar("blur_amp", win_name, 0, 255, empty_function) 

    while True: 
     trackbar_pos1 = cv2.getTrackbarPos("canny_th1", win_name) 
     trackbar_pos2 = cv2.getTrackbarPos("canny_th2", win_name) 
     trackbar_pos3 = cv2.getTrackbarPos("blur_size", win_name) 
     trackbar_pos4 = cv2.getTrackbarPos("blur_amp", win_name) 
     img_blurred = cv2.GaussianBlur(img.copy(), (trackbar_pos3 * 2 + 1, trackbar_pos3 * 2 + 1), trackbar_pos4) 
     canny = cv2.Canny(img_blurred, trackbar_pos1, trackbar_pos2) 
     cv2.imshow(win_name, canny) 

     key = cv2.waitKey(1) & 0xFF 
     if key == ord("c"): 
      break 

    cv2.destroyAllWindows() 
    return canny 

img = cv2.imread("sphere.jpg") 

#resize for convenience 
img = cv2.resize(img, None, fx = 0.2, fy = 0.2) 

#closing 
kernel = np.ones((11,11), np.uint8) 
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) 

#sharpening 
kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]]) 
img = cv2.filter2D(img, -1, kernel) 

#test if you use different scale img than 0.2 of the original that I used 
#remember that the actual kernel size for GaussianBlur is trackbar_pos3*2+1 
#you want to get as full circle as possible here 
#canny = CannyTrackbar(img, "canny_trakbar") 

#additional blurring to reduce the offset toward brighter region 
img_blurred = cv2.GaussianBlur(img.copy(), (8*2+1,8*2+1), 1) 

#detect edge. important: make sure this works well with CannyTrackbar() 
canny = cv2.Canny(img_blurred, 160, 78) 

coords = np.column_stack(np.nonzero(canny)) 

model, inliers = measure.ransac(coords, measure.CircleModel, 
           min_samples=3, residual_threshold=1, 
           max_trials=1000) 

rr, cc = draw.circle_perimeter(int(model.params[0]), 
           int(model.params[1]), 
           int(model.params[2]), 
           shape=img.shape) 

img[rr, cc] = 1 

import matplotlib.pyplot as plt 
plt.imshow(img, cmap='gray') 
plt.scatter(model.params[1], model.params[0], s=50, c='red') 
plt.axis('off') 
plt.savefig('sphere_center.png', bbox_inches='tight') 
plt.show() 

現在,我可能會嘗試計算其中像素statisticaly brigher他們在哪裏調光器來調節激光位置(如果我理解你正在試圖做正確的)

如果RANSAC仍然不夠。我想嘗試調整坎尼,只能檢測到圓上(在那裏的良好概述)之上的完美的弧線,也比使用以下依存嘗試(我懷疑,這應該是可能的):

enter image description here

+0

這是一些gr8工作 –

+1

謝謝@JeruLuke,這意味着很多,因爲我剛剛開始與OpenCV的旅程 - 回答這些問題是一個學習的好機會! – m3h0w