2017-03-07 40 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