我们在进行数字图像处理时,通常需要获取目标的一些参数,例如,形状,大小,位置等信息。而获取这些信息的常用方法是计算该区域的最小外接矩形,除此之外,我还想到了另一种方法——求目标的最小外接圆。但matlab中并没有计算多边形最小外接圆的函数,所以今天我给大家分享一下关于绘制二值图像中目标区域最小外接圆的思路和方法。
1.首先将任意多边形变成凸多边形。这样做的目的是使该多边形的最小外接圆的圆心落在多边形内部。可以利用regionprops中的convexhull函数实现将任意多边形变成凸多边形。
2.一个多边形最少有两个顶点与外接圆的圆弧接触,而且这两个点应该是距离最远的两个点。所以下一步就是要找到凸多边形的距离最远的两个顶点。
3.用直线连接圆上的任取两点,然后做这条线段的垂直平分线L。可以确定圆心的位置就在L上。这正是做多边形的最小外接圆的理论依据。(如下图所示,绿线为L,红点为圆心O)
4.最后一步就是确定圆心。确定圆心位置的依据是:以该点为圆心,以该点到距离它最远的多边形顶点的距离为半径画圆,此圆若能刚好包围所有的多边形顶点。那么此点便是圆心,所画圆即是多边形最小外接圆。
以上就是全部思路,下面是完整的代码,以及实际测试结果 。
函数代码
function [circle_x,circle_y,R] = FindCircle( bw )
% 此函数用于计算二值区域的最小外接圆,并画在图上
% 输入为一个二值图像,输出为圆心坐标和圆的半径
stats = regionprops(bw,'ConvexImage');%将原始多边形补成一个凸多边形
%%%确定凸多边形的顶点
A=ones(3); %卷积核
vertex=conv2(stats.ConvexImage,A,'same');%用卷积的方法查找多边形顶点
image_point=vertex<=5;%二值图像image_point用来显示多边形顶点
image_point=image_point.*stats.ConvexImage;%去除伪顶点
[x0,y0]=find(image_point==1);%查找并返回顶点坐标
F1=[];%设置空数组,用来存储顶点坐标
F1(:,1)=x0;
F1(:,2)=y0;
%%%确定距离最远的两个顶点
G=pdist(F1);%计算点与点之间的欧里几德距离
H=squareform(G);%将距离转换为矩阵形式
H=tril(H);%生成下三角矩阵,减少计算量
mx=max(max(H));%进行比较,选出最大的数值,即最大的距离
[h,L]=find(H==mx);%找到最大距离的点在H矩阵中的位置。注:此处可能产生不止一对点
point_1=F1(h(1),:);
point_2=F1(L(1),:);
%%%计算两个点的连线的垂直平分线,多边形外接圆的圆心就在此线上
x1=point_1(1);y1=point_1(2);
x2=point_2(1);y2=point_2(2);
k=(y1-y2)/(x1-x2);
b=((y1+y2)/2)+(x1+x2)/(2*k);%垂直平分线表达式:y=kx+b
X1=round(b*k); Y1=0;
X2=0; Y2=round(b);%确定垂直平分线经过的点的坐标
image_line=zeros(size(stats.ConvexImage));%生成一个空白图用作画垂直平分线
[image_line] = BresenhamDrawLine( image_line ,Y1,X1,Y2,X2);%调用直线插补函数,画垂直平分线
[x,y]=find(image_line==1);%查找并返回垂直平分线的坐标
F2=[];%设置空数组,用来存储垂直平分线坐标点
F2(:,1)=x;
F2(:,2)=y;
%%%确定圆心及半径
F=cat(1,F2,F1);%将垂直平分线的坐标数组与多边形顶点坐标数组合并
DIS=pdist(F);%计算垂直平分线坐标点与各个顶点之间的欧里几德距离
matrix_R=squareform(DIS);
matrix_R=tril(matrix_R);
matrix_R=matrix_R(:,1:length(x));
[R,num]=min(max(matrix_R));%进行比较,返回最小外接圆半径和圆心坐标的索引
%以上计算所得的圆心坐标是基于stats.ConvexImage的,
%但是由于stats.ConvexImage与原二值图像大小不一致,所以实际圆心坐标需要做出修改。
[bwx,bwy]=find(bw==1);
startpoint=min(bwx,bwy);%找到原始多边形上的点的坐标值中最小的x和y
circle_x=x(num)+startpoint(1)-1;
circle_y=y(num)+startpoint(2)-1;
end
这其中有关BresenhamDrawLine函数是一个直线插补函数。
原文链接——https://blog.csdn.net/u012526003/article/details/51510633?utm_source=app&app_version=4.17.2&code=app_1562916241&uLinkId=usr1mkqgl919blen
测试程序
clc
img=zeros(20,20);%首先生成一个20×20像素的二值图像
for i=8:15
for j=8:i
img(i,j)=1;
end
end
img(16,9)=1;
img(17,9)=1;
img(18,9)=1;
img(18,10)=1;%在二值图像中随意设计一个多边形区域
figure(1)
subplot(131)
imshow(img)
stats = regionprops(img,'conveximage');%将该区域补成一个凸多边形
subplot(132)
imshow(stats.ConvexImage)
[point_x,point_y,r]=FindCircle(img);%调用findcircle函数,计算区域最小外接圆的半径和圆心坐标
subplot(133)
imshow(img)
hold on
viscircles([point_y,point_x],r,'color','b');%画出该圆
text(point_y,point_x,'o');%标记圆心
hold off
测试结果如图所示:
初次创作,或有不足之处,欢迎批评指正,欢迎转发、交流。