实验内容1.拍摄一张包含硬币、橡皮等物品的照片,通过Hough 变换检测出圆形的硬币个数并区分不同半径的硬币。
最终计算出照片中的总钱数。
解:Hough 变换的实质是对图像进行坐标的变换,将图像空间的线条变为参数空间的聚集点,从而将原始图像中检测给定形状的曲线问题,变成寻找参数空间中的峰点的问题。
它不仅可以检测直线,而且可以很方便地检测圆、椭圆和抛物线等形状。
由于这里需要检测圆形的硬币,所以下面给出检测圆的具体方法:因为圆的图像空间方程为:222()()x a y b r -+-=,我们需要通过Hough 变换,将图像空间(,)x y 对应到参数空间(,,)a b r ,然后对其进行累加完成检测。
但是显然这种方法的计算量是非常大的,所以一般都是先对灰度图像进行边缘提取,利用边界像素的灰度梯度信息估计出下式中的角度θ,以此来降低计算量:cos cos a x r b y r θθ=-*⎧⎨=-*⎩ (1)一般在检测过程中需要对图像进行预处理,使得检测更加准确和容易。
检测过程如下所示:○1真彩色图像转为灰度图像; ○2去除噪声,进行中值滤波; ○3转为二值图像,利用边缘算子进行图像边缘提取; ○4最后进行图像的平滑和填充。
这里处理的图像并没有太多噪声,所以处理的时候略去了中值滤波的步骤,直接对边缘提取后的图像进行Hough 变换检测圆形。
根据式(1),我们需要对半径r 和角度θ进行搜索,所以这里应该首先设置半径和角度方向的搜索步长step_r 和step_angle ,接着给出半径搜索的最大和最小值,当然这两个数值需要根据经验来自己确定。
最后就可以根据这些确定半径和角度的最大搜索次数。
由于Hough 变换需要用到稀疏矩阵,也即首先得找到图像矩阵中的非零量,针对这些非零量进行进一步的处理。
这个操作可以直接通过Matlab 中的find 语句来实现:>> [rows, cols] = find(BW); %找出矩阵中的非零值接着只需要根据式(1)计算出对应参数空间的参数a b和,根据,a b的范围确定hough空间累加器hough_adder是否自增。
由于这里的图像中不止一个圆形,所以不能直接利用hough_adder中的最大值来确定半径,需要设置一定的阈值thre来确定一定半径的圆,当阈值设置合理后才可以找出图像中的圆。
接着对阈值范围内的点进行搜索,当满足圆周内外各5个像素点时,该点可以认为是我们搜寻的圆上的点。
对其相应的坐标赋1即可。
经过上述的步骤,便通过hough变换的思想将图像的中的圆给检测出来了。
并且可以将圆心坐标和圆的半径都显示出来。
那接下来按照题目的意思是需要我们计算总的钱数,所以首先需要区分一元和五角的硬币,也就是区分圆的半径。
由于这里我们设置搜索出来的圆的半径是逐渐递增的,所以只需要判断在两个相邻点之间圆的半径是否存在跳变即可。
因为如果是不同的一元硬币,那么他们的半径即使相差一点也不会很多。
这样我们便可以得出首先将圆分成两类(这里只考虑一元和五角的两种情况),然后在每一类中再寻找半径相似的圆位于不同坐标的个数,也就是同样面值的硬币的个数了。
可以通过如下代码实现:是不同的硬币了。
接着在相同的硬币中,通过循环搜索当前半径和之前像素点的半径,当横纵坐标相差都小于10的时候可以认为是同一个点,也即是同一个硬币,否则将硬币数加一。
最后通过统计得到的总硬币数计算出总钱数。
下面先给出具体代码:clc,clear allImage = imread('final.bmp');imshow(Image),title('原始图像');Gray = rgb2gray(Image);bw = im2bw(Gray); % 先转为二值图像BW = edge(bw); % 然后提取边缘figureimshow(BW),title('图像边缘);step_r = 1; % 设定半径搜索步长step_angle = 0.1; % 设定角度搜索步长r_min = 50; % 最小半径和最大半径,确定搜索范围r_max = 100;thre = 0.8; % 设定阈值%***************hough变换*********************************[m, n] = size(BW);Count_r = round((r_max-r_min)/step_r)+1; % °半径最大搜索次数hough_adder = zeros(m, n, Count_r);Count_angle = round(2*pi/step_angle); % 角度最大搜索次数[rows, cols] = find(BW); % 找出矩阵中的非零值count = size(rows); % 计算非零值的个数for Count_Num = 1 : countfor Count_R = 1 : Count_rfor Count_Angle = 1 : Count_anglea =round(rows(Count_Num)-(r_min+(Count_R-1)*step_r)*cos(Count_Angle*step_angle ));b =round(cols(Count_Num)-(r_min+(Count_R-1)*step_r)*sin(Count_Angle*step_angle) );isTrue = (a>0 && a<=m && b>0 && b<=n);if(isTrue)hough_adder(a,b,Count_R) = hough_adder(a,b,Count_R) + 1;% 参数空间累加器加1endendendendmax_hough_adder = max(max(max(hough_adder))); % 计算最大值index = find(hough_adder >= max_hough_adder * thre); % 设定阈值length = size(index);hough_circle=zeros(m,n);for Count_Num = 1 : countfor Count_Length = 1 : lengthpar3 = floor(index(Count_Length)/(m*n));par2 = floor((index(Count_Length)-par3*(m*n))/m);par1 = index(Count_Length)-par3*(m*n)-par2*m;isTrue =(rows(Count_Num)-par1)^2+(cols(Count_Num)-par2-1)^2<(r_min+par3*step_r)^2+5 &&(rows(Count_Num)-par1)^2+(cols(Count_Num)-par2-1)^2>(r_min+par3*step_r)^2-5 ;if(isTrue)hough_circle(rows(Count_Num),cols(Count_Num)) = 1;endendendfigureimshow(hough_circle),title('圆形检测结果');for Count_Length = 1 : lengthpar3 = floor(index(Count_Length)/(m*n));par2 = floor((index(Count_Length)-par3*(m*n))/m);par1 = index(Count_Length)-par3*(m*n)-par2*m;par3 = r_min+par3*step_r;para(:,Count_Length) = [par1,par2,par3]; % 存储符合条件的圆坐标以及半径end%***************计算总钱数*********************************total_wu = 0;for idx = 1 : length-1total_wu = total_wu + para(3,idx);if (para(3,idx+1) - para(3,idx) > 5)break;endendtotal_yi = sum(para(3,:)) - total_wu;Ave_wu = total_wu / idx;Ave_yi = total_yi / (length(1,1)-idx);% 计算五角硬币的个数count_wu = 1;j = 1;for i = 2 : idxif j<= i-1if abs(para(1,i) - para(1,j)) < 10 && abs(para(2,i) - para(2,j)) < 10count_wu = count_wu;elsecount_wu = count_wu + 1;break ;endendend% 计算一元硬币的个数count_yi = 1;j = idx+1;for i = (idx+2) : lengthif j <= i-1if abs(para(1,i) - para(1,j)) < 10 && abs(para(2,i) - para(2,j)) < 10 count_yi = count_yi;elsecount_yi = count_yi + 1;break ;endendendtotal_money = count_wu * 0.5 + count_yi * 1 % 计算总钱数下面给出处理的图像:原始图像图1 原始图像图像边缘最后给出检测出的图像如下:从图3可以看出此时检测结果还是很准确的,当然这个和我们所选取的阈值thre 有关,如果thre 选择偏大,则无法将三个圆都检测出来,相反,如果thre 偏小,那么可能检测出来图形中其他的圆(比如该图像中的U 盘上的圆圈)。
所以说这里的关键就是阈值的选择,我是通过不断调试得出这个比较理想的结果的。
最后,在屏幕上打印总的钱数为:>> total_money =2.5000一、 实验小结由于老师上课只讲述了直线的hough 变换,所以在一开始做这题的时候感觉有点无从下手。
后来查阅了冈萨雷斯的图像处理书对hough 变换有了一个更深刻图2 提取的图像边缘检测结果图3 检测出圆轮廓的了解。
其实质是将图像空间转为参数空间,然后进行累加计数,累加的最大值便是所求的半径。