matlab hough圆检测检测一个圆,结果有多个

OpenCV中Hough直线检测和圆检测
霍夫变换是图像处理中的一个检测直线、圆等简单几何形状的方法。它最初是用于在二值化的图像中进行直线检测的。
1、霍夫直线检测
(1)基本理论
Hough直线检测的基本理论是二值图像中的任何点都可能是一些候选直线集合的一部分,所选的参数方式是每一行代表极坐标中的一个点,并且隐含的直线是通过象征点的,垂直于原点到此点的半径,即:检测的过程可以看成从图像中的一个个像素点出发,寻找每个点成为直线一部分的可能,再把这条线上可能的点连起来形成直线。在实际检测中,当一条线出现凹陷或是弯曲度时,也会检测出直线,只是不是一条完整长度直线,而是断断续续重叠相近的很多直线。
而对于图像中的一条直线而言,利用直角坐标系,可以表示为:的形式。那么,该直线上任意一点(x,y)变换到k-b参数空间将变成一个“点”。也就是说,将图像空间中所有的非零像素转换到k-b参数空间,那么它们将聚焦在一个点上。如此一来,参数空间中的一个局部峰值点就很有可能对应着原图像空间中的一条直线。不过,由于直线的斜率可能为无穷大,或者无穷小,那么,在k-b参数空间就不便于对直线进行刻画和描述。所以,研究人员提出采用极坐标参数空间进行直线检测。在极坐标系中,直线可以表述为以下形式:
(2)OpenCV的实现
OpenCV中(2.0版本以下)的函数cvHoughLines2()实现了用于直线(线段)检测的霍夫变换(Hough transform)方法,将极坐标空间中局部峰值点予以返回。它支持两种不同的霍夫直线变换:标准霍夫变换(Standard&Hough&Transform,SHT)和累计概率霍夫变换(Progressive&ProbabStandard&Hough&Transform,PPHT)。其中标准霍夫变换就是刚才所述的在极坐标空间进行参数表示的方法。而PPHT是SHT的改进,它是在一定的范围内进行霍夫变换,从而减少计算量,缩短计算时间。cvHoughLines2()函数原型如下:
CvSeq* cvHoughLines2(CvArr* image,
void* lineStorage,
int method,
double rho,
double theta,
int threshold,
double param1=0,
double param2=0);
参数的说明如下:
输入 8-比特、单通道 (二值) 图像,当用CV_HOUGH_PROBABILISTIC方法检测的时候其内容会被函数改变。
line_storage:
检测到的线段存储仓. 可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回),或者是包含线段参数的特殊类型(见下面)的具有单行/单列的矩阵(CvMat*)。矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的线段。如果 line_storage 是矩阵,而实际线段的数目超过矩阵尺寸,那么最大可能数目的线段被返回(线段没有按照长度、可信度或其它指标排序)。
Hough 变换变量,是下面变量的其中之一:
CV_HOUGH_STANDARD - 传统或标准 Hough 变换(SHT)。每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是直线与原点 (0,0) 之间的距离,θ 线段与 x-轴之间的夹角。因此,矩阵类型必须是 CV_32FC2 type;
CV_HOUGH_PROBABILISTIC- 概率 Hough 变换(PPHT)。如果图像包含一些长的线性分割,则效率更高。它返回线段分割而不是整个线段。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4 type;
CV_HOUGH_MULTI_SCALE- 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致。
SHT相对而言,找的线参数比较准,但是画的位置不准。具体表现为检测的线端点和图片实际端点有一定偏差;PPHT检测结果相对SHT来说,更靠近图片中的线位置,具体表现为PPHT检测的起始端点比SHT检测的更靠近图片线端点。
假如知道图片中线的端点,你把它带入SHT内的参数,更准。
设置极坐标系的精度:与像素相关的距离精度(单位为像素)。
设置极坐标系的精度:弧度测量的角度精度(单位为弧度)。
threshold:
阈值参数。如果相应的累计值大于 threshold, 则函数返回的这个线段。
第一个方法相关的参数:
对传统 Hough 变换,不使用(0);
对概率 Hough 变换,它是最小线段长度;
对多尺度 Hough 变换,它是距离精度 rho 的分母。首先根据用户设定的rho和theta来检测直线,之后按照param1和param2的值对检测结果进行优化,即:大致的距离精度是
rho,而精确的应该是 rho / param1。
第二个方法相关参数:
对传统 Hough 变换,不使用 (0);
对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一;
对多尺度 Hough 变换,它是角度精度 theta 的分母。首先根据用户设定的rho和theta来检测直线,之后按照param1和param2的值对检测结果进行优化,即:大致的角度精度是
theta,而精确的角度应该是 theta / param2。
返回值说明如下:
返回的是CvSeq*类型,存储了检测到的直线参数序列。其中在使用cvLine()函数绘制直线时有个line[0]和line[1],选择不同形式的hough变换直线检测类型,line[0]和line[1]表示的意思是不同的。
a.在标准hough变换中,对于返回的每条直线:
float* line = ( float* ) cvGetSeqElem( lines,i);
float rho = line[0];
float theta = line[1];
line[ 0 ] 表示矢量长度(rho_x来表示),line[1]表示直线的倾斜角(theta_x来表示)
b.在概率hough变换中,line[0]和line[1]表示返回直线的两个端点:
CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
代码参考如下:
#define CV_NO_BACKWARD_COMPATIBILITY
/* This is a standalone program. Pass an image name as a first parameter of the program.
Switch between standard and probabilistic Hough transform by changing &#if 1& to &#if 0& and back */
#include &cv.h&
#include &highgui.h&
#include &math.h&
int main(int argc, char** argv)
const char* filename = argc &= 2 ? argv[1] : &.\\pic1.png&;
IplImage* src = cvLoadImage( filename, 0 );
IplImage* color_
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
if( !src )
printf(&Load image error!\n&);
return -1;
dst = cvCreateImage( cvGetSize(src), 8, 1 );
color_dst = cvCreateImage( cvGetSize(src), 8, 3 );
cvCanny( src, dst, 50, 200, 3 );
//cvThreshold(src, dst, 50, 255, CV_THRESH_BINARY_INV);
cvCvtColor( dst, color_dst, CV_GRAY2BGR );
#if 0 //标准hough变换
printf(&标准hough变换\n&);
lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 100, 0, 0 );
for( i = 0; i & MIN(lines-&total,100); i++ )
float* line = (float*)cvGetSeqElem(lines,i);
float rho = line[0];
float theta = line[1];
CvPoint pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
cvLine( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, CV_AA, 0 );
#else //累积概率hough变换
printf(&累积概率hough变换\n&);
lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );
for( i = 0; i & lines-& i++ )
CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
cvNamedWindow( &Source&, 1 );
cvShowImage( &Source&, src );
cvNamedWindow( &Hough&, 1 );
cvShowImage( &Hough&, color_dst );
cvWaitKey(0);
结果如下:
2、霍夫圆检测
(1)基本理论
已知圆的一般方程为:(x - a)^2 + (y - b)^2 = r^2。其中,(a, b)为圆心,r为圆的半径。把X-Y平面上的圆转换到a-b-r参数空间,则图像空间中过(x, y)点圆对应参数空间中,高度r变化下的一个三维锥面,如图:
同理,过图像空间中任意一点的圆对应于参数空间中的一个三维锥面。因此,过图像空间上同一圆上的点,对应的参数空间中的三维锥面,在r高度必然相交于一点(a, b, r)。这样通过检测这一点可以得到圆的参数,相应的圆也可求得了。图像平面的方程转化为参数平面上的示意图如图:
相比霍夫直线检测,圆形检测的过程很类似:只是参数方程有变化,而且参数空间增加了一个维度(圆心坐标x,y和半径r)。霍夫变换的一个好处就是不需要图像中出现完整的圆,只要落在一个圆上的像素数量足够多,就能正确识别。同理,霍夫变换检测椭圆如果使用椭圆的标准式,那么将会有五个参数,它们通过标准式相关,检测圆就已经相当耗时了,如果再用这中方程形式处理势必失去实际用途。
(2)OpenCV实现
由于比直线检测多出一个维度,使得标准的霍夫圆检测需要大量内存且速度较慢。出于对运算效率的考虑, OpenCV实现的霍夫圆检测是一个比标准霍夫圆变换更为灵活的检测方法:霍夫梯度法,也叫2-1霍夫变换(21HT)。原理是首先对图像进行canny边缘检测,然后考虑边缘图像中的每一个非0点的局部梯度,通过cvSobel()函数计算x,y方向上的sobel一阶导数得到梯度,利用得到的梯度,由斜率指定直线上的每一个点都在累加器中被累加,然后从累加器中这些点中选择候选的中心画圆。
(来自的解释没看太懂:它的原理依据是圆心一定是在圆上的每个点的模向量上,这些圆上点模向量的交点就是圆心,霍夫梯度法的第一步就是找到这些圆心,这样三维的累加平面就又转化为二维累加平面;第二步是根据所有候选中心的边缘非0像素对其的支持程度来确定半径。)
OpenCV中(2.0版本以下)的函数cvHoughCircles()实现了上述方法。cvHoughCircles()函数原型如下:
CvSeq* cvHoughCircles( CvArr* image, void* circle_storage, int method, double dp, double min_dist, double param1=100, double param2=100, int min_radius=0, int max_radius=0 );
参数的说明如下:
输入 8-比特、单通道灰度图像(这点跟直线检测不同)。
circle_storage
检测到的圆存储仓,可以是内存存储仓(此种情况下,一个线段序列在存储仓中被创建,并且由函数返回),或者是包含圆参数的特殊类型的具有单行/单列的CV_32FC3型矩阵(CvMat*). 矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的圆。如果 circle_storage 是矩阵,而实际圆的数目超过矩阵尺寸,那么最大可能数目的圆被返回。每个圆由三个浮点数表示:圆心坐标(x,y)和半径r。
Hough 变换方式,目前只支持CV_HOUGH_GRADIENT(即:21HT)。
累加器图像的分辨率。这个参数允许创建一个比输入图像分辨率低的累加器。(这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小,但也不要太大,越大越易误判,最好为2。
该参数是让算法能明显区分的两个不同圆之间的最小距离。
用于Canny的边缘阀值上限,下限被置为上限的一半。根据图像总体灰度情况确定。
累加器的阀值。较大,只检测较大的圆;较小,小圆也检测。
min_radius
最小圆半径。
max_radius
最大圆半径。
返回值说明如下:
返回的是CvSeq*类型,存储了检测到的圆的参数序列。
float* p = ( float* )cvGetSeqElem( results, i );这里能够得到p[0],p[1]和p[2],分别代表圆心的x坐标,y坐标和半径r。
代码参考如下:
#include &cv.h&
#include &highgui.h&
#include &math.h&
int main(int argc, char* argv[])
const char* filename = argc &= 2 ? argv[1] : &..\\board.jpg&;
IplImage* src = cvLoadImage(filename, 0);
cvSmooth(src, src, CV_GAUSSIAN, 5, 5);
//降噪,不然误检测太高
IplImage* dst = cvCreateImage(cvGetSize(src), src-&depth, 3);
cvCvtColor(src, dst, CV_GRAY2BGR);
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* results = cvHoughCircles(
//cvHoughCircles函数需要估计每一个像素梯度的方向,
//因此会在内部自动调用cvSobel,而二值边缘图像的处理是比较难的
CV_HOUGH_GRADIENT,
//累加器图像的分辨率
1,30); //改变着两个参数检测不同大小的圆
for( int i = 0; i & results-& i++ )
float* p = ( float* )cvGetSeqElem( results, i );
//霍夫圆变换
CvPoint pt = cvPoint( cvRound( p[0] ), cvRound( p[1] ) );
//确定圆心
cvRound( p[2] ),
//确定半径
CV_RGB( 255, 0, 0 )
//画圆函数
cvNamedWindow( &cvHoughCircles&, 1 );
cvShowImage( &cvHoughCircles&, dst );
cvNamedWindow( &source&, 1 );
cvShowImage( &source&, src );
cvWaitKey(0);
cvReleaseMemStorage(&storage);
}结果如下:
效果不好,但是opencv2.0以上版本的demo(\samples\cpp下面的houghcircles.cpp)效果不错:
改进的霍夫圆检测参考:
看过本文的人也看了:
我要留言技术领域:
收藏提示你已经自动关注本知识库了哦!
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?本帖子已过去太久远了,不再提供回复功能。编写健壮(颜色和大小不变),圆检测用opencv实现(基于Hough变换或其他特征)
编写健壮(颜色和大小不变),圆检测用opencv实现(基于Hough变换或其他特征)
编写健壮(颜色和大小不变),圆检测用opencv实现(基于Hough变换或其他特征)
我写了下面很简单的Python代码找到圈中的图像:
import numpy as np
WAITKEY_DELAY_MS = 10
STOP_KEY = 'q'
cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE);
cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE);
key_pressed = False
while key_pressed != STOP_KEY:
# grab image
orig = cv.LoadImage('circles3.jpg')
# create tmp images
grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
processed = cv.CreateImage(cv.GetSize(orig), 8, 1)
cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)
cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)
# do some processing on the grey scale image
cv.Erode(grey_scale, processed, None, 10)
cv.Dilate(processed, processed, None, 10)
cv.Canny(processed, processed, 5, 70, 3)
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
# these parameters need to be adjusted for every single image
# extract circles
cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, HIGH, LOW)
for i in range(0, len(np.asarray(storage))):
print "circle #%d" %i
Radius = int(np.asarray(storage)[i][0][2])
x = int(np.asarray(storage)[i][0][0])
y = int(np.asarray(storage)[i][0][1])
center = (x, y)
# green dot on center and red circle around
cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
cv.Circle(processed, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
print "nothing found"
# show images
cv.ShowImage("image - press 'q' to quit", orig)
cv.ShowImage("post-process", processed)
cv_key = cv.WaitKey(WAITKEY_DELAY_MS)
key_pressed = chr(cv_key & 255)
正如你可以从下面的两个例子看到,“圈子发现质量”变化颇多:
案例1案例2和基本形象,但仍算法检测到不同的圈子。如果我提出的算法的图像与不同大小的圆圈,在圆检测甚至可能这主要是由于HIGH和LOW这需要单独为每个新的图片。
因此,我的问题:什么使这个算法的可能性更应该是大小和颜色不变,让各界有不同的颜色和不同尺寸的检测。 Hough变换是不是做事情的最好方法是什么?有没有更好的办法?
本文地址 :CodeGo.net/435070/
-------------------------------------------------------------------------------------------------------------------------
1. 下面是根据我作为一个愿景研究员体验。从你的问题,你似乎有兴趣在可能的算法的代码,而只是一个工作片。首先,我给你的样本图像和结果的快速和肮脏的Python脚本显示证明它也可能解决您的问题。得到这些闪开之后,我尝试回答你的问题有关检测算法。
样本图像(除了你的所有图像都是从下载并消委会行货)与检测到的圆圈(不改变/调整任何确切下面的代码来提取圈子中的所有图像):
代码(基于MSER的Blob检测器)
这里是代码:
import cv2
import math
import numpy as np
d_red = cv2.cv.RGB(150, 55, 65)
l_red = cv2.cv.RGB(250, 200, 200)
orig = cv2.imread("c.jpg")
img = orig.copy()
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
detector = cv2.FeatureDetector_create('MSER')
fs = detector.detect(img2)
fs.sort(key = lambda x: -x.size)
def supress(x):
for f in fs:
distx = f.pt[0] - x.pt[0]
disty = f.pt[1] - x.pt[1]
dist = math.sqrt(distx*distx + disty*disty)
if (f.size & x.size) and (dist&f.size/2):
return True
sfs = [x for x in fs if not supress(x)]
for f in sfs:
cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), d_red, 2, cv2.CV_AA)
cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), l_red, 1, cv2.CV_AA)
h, w = orig.shape[:2]
vis = np.zeros((h, w*2+5), np.uint8)
vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
vis[:h, :w] = orig
vis[:h, w+5:w*2+5] = img
cv2.imshow("image", vis)
cv2.imwrite("c_o.jpg", vis)
cv2.waitKey()
cv2.destroyAllWindows()
正如你可以看到它的基础上,MSER的blob检测。代码没有预处理的图像除了简单的映射成灰度。缺少这些淡yellow的斑点在你的图像的预期。
总之:你不知道怎么样的问题,除了与他们的描述只给出两个示例图像。在这里,我解释为什么我在我的愚见是问什么是攻击问题之前,有关于这个问题是很重要的。
回到主要问题:什么是这个问题?
让我们来看看这是一个搜索问题。为了简化,我们正在寻找圆与给定尺寸/问题归结为找到中心.each像素是候选中心,因此,在搜索空间中包含的所有像素。
P = {p1, ..., pn}
P: search space
p1...pn: pixels
为了解决这个问题,寻找其他两个函数应该被定义为:
E(P) : enumerates the search space
V(p) : checks whether the item/pixel has the desirable properties, the items passing the check are added to the output list
假设算法无所谓,或将强力搜索可以在其中E需要的每一个像素,并传递到五,在实际应用中,重要的是要减少搜索空间和V的效率
我们正在接近主要问题。我们如何定义V,更准确地说应该如何应做什么候选人的性能解决分割成的两分法的问题可取和不可取。该方法是找到定义基于属性的简单决策规则的属性可以。这是你做的试验和错误是什么。你是从正面和负面的例子,学习编程的分类。这是你不知道你想要做什么。你必须/乐曲的决策规则和/或预处理的数据,即,对于二分法问题所用的属性的变化(所希望的候选)被减少。你的机器学习算法,以找到一组给定的例子的最优值。还有一大堆的从决策树学习算法编程你这个问题。你可以一个学习算法找到几个圆检测算法的最优值,看看哪一个给出了一个更好的精度。这需要你只需要收集样本图像的学习算法的主要负担。
另一种方法来提高它经常被忽视的是利用额外随手如果您知道圆的颜色几乎为零额外的努力,你可以提高检测的准确性显著。如果您知道圆的平面上的位置,你想检测的成像圈,你应该把这两套位置之间的转换是通过一个2D单应说明。和单应可只得到4分。然后,你可以提高到有石头的特定领域的知识的价值往往被低估。看它这样,在第一种方法中,我们尝试基于样本数量有限近似决策规则。在第二种方法中,我们知道了决策规则,只需要找到一种方法来有效地利用他们的算法。
总之,有两种方法来提高解的精度/:
工具型:找到一个更简单的算法/用较少的/扭捏算法/自动化这个过程中机器学习算法号
信息化:都是很容易在你不要的问题“你知道的问题是什么。
对于这两个图像您分享我一个blob检测不是针对背景减除我建议尝试估计的背景颜色的两个图像是没有变化的,而圆圈的颜色有所不同。和大部分区域是光秃秃的。
这是一个伟大的建模问题。我有以下想法:
分割图像为RGB然后处理。
动态搜索。
添加约束。
可以肯定你正在尝试检测一下。
更详细地说:
1:正如在其他的答案,直接转换为灰度丢弃了-一个类似的亮度与背景的任何圈子都将丢失。好得多考虑颜色通道或单独或以不同的颜色空间。有相当多的两种方法去这里:执行HoughCircles在隔离,结果,或者,处理通道,每个通道处理前通道上,其中,然后操作HoughCircles。在下面我尝试,我试图分裂为RGB通道,处理,警惕过度饱和的图像cv.And为了避免这个问题(在这个阶段我的圈子总是在白色背景上的黑色戒指/盘)。
2:前处理是相当棘手的,其往往是最好的玩弄。我已经为AdaptiveThreshold这是一个非常强大的,可以通过阈值处理根据他们当地的平均像素提升图像边缘(类似的过程也发生在哺乳动物视觉系统的早期通路)。这是因为它降低了噪音。我dilate/erode只有一个通行证。我一直保留着你如何让他们的其他。它Canny前HoughCircles的确帮助了很多与发现“实心圆',所以可能最好保持它。这前处理是相当沉重的,并可能导致更多的'滴状圈子'假阳性,但在我们的情况下,这或许是可取的?
3:正如您所指出的HoughCirclesparam2(您的LOW)需要为每个图像,以获得最佳的解决方案,其实从文档:
较小的是,可以检测到更多的假圆圈。
trouble的是甜蜜点将会是不同的每张图片。我觉得这里的最好的办法是让设置一个条件,通过做不同的搜索param2值,直到这个条件你的图片显示不重叠的圆圈,而当param2太低,我们通常会得到重叠的圆圈负荷。所以我建议寻找的:
的非重叠的最大数量,和非包含的圆
因此,我们继续呼吁HoughCircles用不同的值param2直到我做到这一点我下面的例子中,仅仅通过param2直到它达到阈值的假设。这将是更快的方式(而且很容易做到),如果你执行一个二进制搜索找到的时候,但你必须要小心,异常处理作为opencv的经常抛出一个错误,对无辜的值param2(至少在我的安装)。不同的条件,我们将要匹配的将是圆的数量。
4:有没有更多的约束,我们可以添加到模型?越多的东西,我们可以告诉我们的模型的一个简单的任务,我们可以把它检测圈。例如,我们知道:
圈数。-即使是上限或下限是有帮助的。
或背景的圆圈的可能的颜色,或“非圆形”。
它们的大小。
在那里他们可以在一个图像。
5:影像中的斑点可能只是笼统地被称为圈子!考虑两个'非圆形的斑点“在你的第二个形象,我的代码无法找到他们(good!),但...如果我'的Photoshop'他们,让他们有更圆,我的代码中可以找到它们...也许如果你要检测的东西都是不圆,采用不同的方法,如Tim Lukins可能会更好。
通过这样做繁重的预处理AdaptiveThresholding和`康力“可以有很多失真特性的图像,这可能会导致错误的检测圆的,或不正确的报告。例如加工后大量固态盘会出现一个圈,所以HughesCircles可能会发现内环。此外,即使该文档注意:
...通常功能检测的圆圈“中心好,但它可能无法找到正确的半径。
如果您需要更精确的半径的检测,我建议以下方法(不
原始图像,光线追迹从圆的中心报道,在不断扩大的交叉(4条:上/下/左/右)
在每个RGB通道单独做到这一点
在一个合理的方式结合起来,每个通道的每个射线(即翻转,偏移,规模等必要的)
取平均值为每条射线的前几个像素,用它来检测其中射线上的显著发生偏差。
这4点是圆周上的点估计。
使用这四个估计,以确定更精确和中心位置(!)。
这可以推广的膨胀环来代替4射线。
在结束该代码确实很不错了不少的这些例子介绍的都是代码如下所示:
检测各界在你的第一个图像:
如何处理前图像看上去之前精明的过滤器被应用(不同颜色的圆圈是高度可见的):
检测所有,但两(斑点)在第二个图像:
改变第二图像(斑点是圆afied,和大的椭圆形变得更圆,提高检测),所有检测到的:
确实颇能检测这个康定斯基的绘画中心(我无法找到,由于他的边界条件同心环)。
import numpy as np
output = cv.LoadImage('case1.jpg')
orig = cv.LoadImage('case1.jpg')
# create tmp images
rrr=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
ggg=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
bbb=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
processed = cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
def channel_processing(channel):
cv.AdaptiveThreshold(channel, channel, 255, adaptive_method=cv.CV_ADAPTIVE_THRESH_MEAN_C, thresholdType=cv.CV_THRESH_BINARY, blockSize=55, param1=7)
#mop up the dirt
cv.Dilate(channel, channel, None, 1)
cv.Erode(channel, channel, None, 1)
def inter_centre_distance(x1,y1,x2,y2):
return ((x1-x2)**2 + (y1-y2)**2)**0.5
def colliding_circles(circles):
for index1, circle1 in enumerate(circles):
for circle2 in circles[index1+1:]:
x1, y1, Radius1 = circle1[0]
x2, y2, Radius2 = circle2[0]
#collision or containment:
if inter_centre_distance(x1,y1,x2,y2) & Radius1 + Radius2:
return True
def find_circles(processed, storage, LOW):
cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, LOW)#, 0, 100) great to add circle constraint sizes.
print 'try'
find_circles(processed, storage, LOW)
circles = np.asarray(storage)
print 'number of circles:', len(circles)
if colliding_circles(circles):
storage = find_circles(processed, storage, LOW)
print 'c', LOW
return storage
def draw_circles(storage, output):
circles = np.asarray(storage)
print len(circles), 'circles found'
for circle in circles:
Radius, x, y = int(circle[0][2]), int(circle[0][0]), int(circle[0][1])
cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
#split image into RGB components
cv.Split(orig,rrr,ggg,bbb,None)
#process each component
channel_processing(rrr)
channel_processing(ggg)
channel_processing(bbb)
#combine images using logical 'And' to avoid saturation
cv.And(rrr, ggg, rrr)
cv.And(rrr, bbb, processed)
cv.ShowImage('before canny', processed)
# cv.SaveImage('case3_processed.jpg',processed)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.ShowImage('processed', processed)
#find circles, with parameter search
storage = find_circles(processed, storage, 100)
draw_circles(storage, output)
# show images
cv.ShowImage("original with circles", output)
cv.SaveImage('case1.jpg',output)
cv.WaitKey(0)
啊,是的......旧颜色/尺码不变量界问题(又名Hough变换过于具体,不
在过去,我一直依赖更OpenCV的,而不是结构和形状分析等功能。你可以得到一个很好的主意从什么是可能的“样本”文件夹中-尤其是fitellipse.py和squares.py。
为了您的阐发,我介绍的这些实例的混合动力版,并根据您的原始来源。检测到的轮廓是在红,绿和拟合椭圆。
这还没有应用:
预处理步骤需要一些调整,以检测更微弱的圈子。
可以进一步测试该轮廓以确定它是否是一个圆或不...
祝你好运!
import numpy as np
# grab image
orig = cv.LoadImage('circles3.jpg')
# create tmp images
grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
processed = cv.CreateImage(cv.GetSize(orig), 8, 1)
cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)
cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)
# do some processing on the grey scale image
cv.Erode(grey_scale, processed, None, 10)
cv.Dilate(processed, processed, None, 10)
cv.Canny(processed, processed, 5, 70, 3)
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)
#storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
storage = cv.CreateMemStorage(0)
contours = cv.FindContours(processed, storage, cv.CV_RETR_EXTERNAL)
# N.B. 'processed' image is modified by this!
#contours = cv.ApproxPoly (contours, storage, cv.CV_POLY_APPROX_DP, 3, 1)
# If you wanted to reduce the number of points...
cv.DrawContours (orig, contours, cv.RGB(0,255,0), cv.RGB(255,0,0), 2, 3, cv.CV_AA, (0, 0))
def contour_iterator(contour):
while contour:
yield contour
contour = contour.h_next()
for c in contour_iterator(contours):
# Number of points must be more than or equal to 6 for cv.FitEllipse2
if len(c) &= 6:
# Copy the contour into an array of (x,y)s
PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2)
for (i, (x, y)) in enumerate(c):
PointArray2D32f[0, i] = (x, y)
# Fits ellipse to current contour.
(center, size, angle) = cv.FitEllipse2(PointArray2D32f)
# Convert ellipse data from float to integer representation.
center = (cv.Round(center[0]), cv.Round(center[1]))
size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5))
# Draw ellipse
cv.Ellipse(orig, center, size, angle, 0, 360, cv.RGB(255,0,0), 2,cv.CV_AA, 0)
# show images
cv.ShowImage("image - press 'q' to quit", orig)
#cv.ShowImage("post-process", processed)
cv.WaitKey(-1)
刚刚更新的说,我认为一个重要的所有这些答案是,有很多可以应用到你寻求承认为圆形哪些进一步的假设和约束条件。我自己的答案是没有借口在这一点-无论是在低级别的预处理或高级装修。事实上,许多的圆圈是不是真正的圆形,由于它们的绘制方法或图像的non-affine/projective变换,并与它们是如何呈现的其他属性/捕获(颜色,噪声,照明边厚)-所有的结果在短短的一个图像在任何数量的可能的候选圈。
有更复杂的技术。但他们会花费你。我个人喜欢@飞梭想法addaptive门槛。这是快速,可靠,合理,您可以再进一步的测试最终的轮廓(例如使用胡或管件与椭圆轴的一个简单的比率测试-例如,如果((分(大小)/最大(大小))& 0.7)。
一如以往与计算机视觉有实用主义,原则,parsomony之间的紧张关系。由于我喜欢告诉别人谁认为简历是很容易的,它不是-它实际上是一个问题。最好的,你经常可以希望这外面是工作的大部分
翻翻你的代码,我注意到以下几点:
灰度转换。我明白你为什么这样做,但要意识到你扔
那里。当您在“后处理”的图像看,你的yellow圆圈
的强度为背景,只是以不同的颜色。
边缘检测后噪声消除(erae /扩张)。这不应该是必要的;康力应该利用这一服务。
Canny边缘检测。你的“开放式”圆圈有两个边缘,内和外边缘。因为他们是相当接近,在康力过滤器可能加在一起。如果没有,你就会有两条边紧靠在一起。即康力之前,你必须打开和实心圆。之后,你有0/2和1的边缘,分别。由于霍夫再次调用Canny算子,在第一种情况下的两个边缘可能会平滑地(取决于初始宽度),这就是为什么在核心的Hough算法可以治疗开放和实心圆的
所以,我的第一个办法是改变灰度映射。唐的强度,色相/饱和度/值。另外,使用差分方法-你要找的边缘。因此,计算HSV变换,平滑的副本,然后取原和平滑副本之间的差异。这将让你dH, dS, dV值的每个点(在色调,饱和度,数值局部变化)。广场和添加来获得一个图像,附近所有的边(内部和外部)的山峰。
我的第二个是本地的正常化,但我不知道如果这甚至是必要的。这个想法是,你不关心特别多关于你离开了边缘信号的精确值,它应该是二进制反正(边或不)。因此,您可以通过当地的平均水平(其中地方是在你边尺寸的量级)除以正常化的每个值。
霍夫一个“模型”,以找到一个(典型值)边缘检测图像的某些功能,你可知道。在的情况下HoughCircles该模型是一个完美的圆。的,有可能不存在将使得检测更加不稳定和椭圆形的圈里你的照片,而不会增加误报的数量。在另一方面,由于相关的非封闭的正圆或一个完美的圆圈,一个“凹”可能会持续出现。所以根据您的期望输出您可能会或可能不希望
这就是说,有我看这可能会帮助你对你的方式使用此功能的几件事情:HoughCircles电话Canny在内部,所以我想你可以离开了呼叫。param1(你叫HIGH)通常被初始化周围的一个值200。它作为一个到内部调用Canny:cv.Canny(processed, cannied, HIGH, HIGH/2)。这可能有助于运行Canny自己喜欢这个,看看如何设置HIGH受影响的霍夫正在处理与图像变换。param2(你叫LOW)通常被初始化围绕一个值100。这是Hough变换的蓄电池的投票门槛。设置它更多的假阴性,更降低误报。我相信这是你想开始摆弄周围的第一个。
重新更新:实心圆:当你发现圆的形状与Hough变换可以测试,如果他们通过采样边界的颜色它假想圆内的一个或多个点的填补。或者你应该圈子给定的背景色内一个或多个点。如果比较或的情况下,如果它失败圆圈显示为实心。
好吧看的图像。我**Active Contours**活动轮廓模型
关于活动轮廓的好处是,他们几乎完美融入任何给定的形状。无论是正方形或三角形,在你的情况下,他们是完美的人选。
如果你能够提取圆的中心,这是伟大的。活动轮廓总是需要一个点开始从他们既可以增大或缩小以适合。没有必要使中心总是对准到中心。有一点偏移仍然是好的。
和你的情况,如果你让轮廓,从中心向外生长,应休息一圆的边界。
需要注意的是活动轮廓生长或气球的能量可以设置轮廓的方向,向内或向外。
你可能会需要在灰度梯度图像。但你仍然可以在色彩尝试为好。如果它的工作原理!
如果你不提供中心,扔在大量活跃的轮廓,然后让增长/萎缩。该安定下来的轮廓被保留,不安的是扔掉。这是一个暴力的方法。将CPU密集型。但将需要更多细致的工作,以确保你离开正确的轮廓和扔掉坏的。
我希望这种方式可以解决这个问题。
本文标题 :编写健壮(颜色和大小不变),圆检测用opencv实现(基于Hough变换或其他特征)
本文地址 :CodeGo.net/435070/
继续浏览 :
发表评论:
TA的最新馆藏

我要回帖

更多关于 hough变换检测圆 的文章

 

随机推荐