给您:
我使用了来自Detect semi-circle in opencv的第二个答案,并进行了一些修改。这个版本现在能够检测到最佳的半圆(就完整度而言)。
但是首先,我想告诉您为什么链接到Detect semi-circle in opencv stack overflow question的被接受的答案在这里不起作用(除了噪音):你只有圆的边缘!正如那个问题所述,HoughCircle函数在内部计算梯度,这对于棱角分明的图像效果不好。
但现在我是怎么做的:
使用这个作为输入(您自己的中值滤波图像(我只是裁剪了一下):
![enter image description here](https://istack.dev59.com/2XpcX.webp)
首先我“规范化”了图像。我只是拉伸了值,使最小值为0,最大值为255,导致了这个结果:(也许一些真正的对比度增强更好)
![enter image description here](https://istack.dev59.com/srw54.webp)
之后,我使用一些固定阈值计算该图像的阈值(您可能需要编辑它并找到一种动态选择阈值的方法!更好的对比度增强可能有所帮助)
![enter image description here](https://istack.dev59.com/iokqh.webp)
从这个图像中,我使用了一些简单的RANSAC圆检测(与链接的半圆检测问题中我的答案非常相似),给出了这个结果作为最佳半圆:
![enter image description here](https://istack.dev59.com/AZACd.webp)
这是代码:
int main()
{
cv::Mat color = cv::imread("../inputData/semi_circle_median.png");
cv::Mat gray;
cv::cvtColor(color, gray, CV_BGR2GRAY);
double min, max;
cv::minMaxLoc(gray,&min,&max);
float sub = min;
float mult = 255.0f/(float)(max-sub);
cv::Mat normalized = gray - sub;
normalized = mult * normalized;
cv::imshow("normalized" , normalized);
cv::Mat mask;
cv::threshold(normalized, mask, 100, 255, CV_THRESH_BINARY);
std::vector<cv::Point2f> edgePositions;
edgePositions = getPointPositions(mask);
cv::Mat dt;
cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);
unsigned int nIterations = 0;
cv::Point2f bestCircleCenter;
float bestCircleRadius;
float bestCirclePercentage = 0;
float minRadius = 50;
float minCirclePercentage = 0.05f;
int maxNrOfIterations = edgePositions.size();
for(unsigned int its=0; its< maxNrOfIterations; ++its)
{
unsigned int idx1 = rand()%edgePositions.size();
unsigned int idx2 = rand()%edgePositions.size();
unsigned int idx3 = rand()%edgePositions.size();
if(idx1 == idx2) continue;
if(idx1 == idx3) continue;
if(idx3 == idx2) continue;
cv::Point2f center; float radius;
getCircle(edgePositions[idx1],edgePositions[idx2],edgePositions[idx3],center,radius);
std::vector<cv::Point2f> inlierSet;
float cPerc = verifyCircle(dt,center,radius, inlierSet);
if(cPerc >= bestCirclePercentage)
if(radius >= minRadius)
{
bestCirclePercentage = cPerc;
bestCircleRadius = radius;
bestCircleCenter = center;
}
}
if(bestCirclePercentage >= minCirclePercentage)
if(bestCircleRadius >= minRadius);
cv::circle(color, bestCircleCenter,bestCircleRadius, cv::Scalar(255,255,0),1);
cv::imshow("output",color);
cv::imshow("mask",mask);
cv::waitKey(0);
return 0;
}
利用这些帮助函数:
float verifyCircle(cv::Mat dt, cv::Point2f center, float radius, std::vector<cv::Point2f> & inlierSet)
{
unsigned int counter = 0;
unsigned int inlier = 0;
float minInlierDist = 2.0f;
float maxInlierDistMax = 100.0f;
float maxInlierDist = radius/25.0f;
if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
if(maxInlierDist>maxInlierDistMax) maxInlierDist = maxInlierDistMax;
for(float t =0; t<2*3.14159265359f; t+= 0.05f)
{
counter++;
float cX = radius*cos(t) + center.x;
float cY = radius*sin(t) + center.y;
if(cX < dt.cols)
if(cX >= 0)
if(cY < dt.rows)
if(cY >= 0)
if(dt.at<float>(cY,cX) < maxInlierDist)
{
inlier++;
inlierSet.push_back(cv::Point2f(cX,cY));
}
}
return (float)inlier/float(counter);
}
inline void getCircle(cv::Point2f& p1,cv::Point2f& p2,cv::Point2f& p3, cv::Point2f& center, float& radius)
{
float x1 = p1.x;
float x2 = p2.x;
float x3 = p3.x;
float y1 = p1.y;
float y2 = p2.y;
float y3 = p3.y;
center.x = (x1*x1+y1*y1)*(y2-y3) + (x2*x2+y2*y2)*(y3-y1) + (x3*x3+y3*y3)*(y1-y2);
center.x /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
center.y = (x1*x1 + y1*y1)*(x3-x2) + (x2*x2+y2*y2)*(x1-x3) + (x3*x3 + y3*y3)*(x2-x1);
center.y /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
radius = sqrt((center.x-x1)*(center.x-x1) + (center.y-y1)*(center.y-y1));
}
std::vector<cv::Point2f> getPointPositions(cv::Mat binaryImage)
{
std::vector<cv::Point2f> pointPositions;
for(unsigned int y=0; y<binaryImage.rows; ++y)
{
for(unsigned int x=0; x<binaryImage.cols; ++x)
{
if(binaryImage.at<unsigned char>(y,x) > 0) pointPositions.push_back(cv::Point2f(x,y));
}
}
return pointPositions;
}
编辑:还有一件事:速度表现高度取决于maxNrOfIterations
。如果这很重要,你真的应该了解何时停止RANSAC。因此,您可能能够早期确定找到的圆是正确的,并且不需要测试其他任何一个;)