MATLAB自带有关于计算二值区域最小外接矩形的函数——regionprops。但是此函数只能计算平行于坐标轴的矩形,而这样求得的外接矩形有时并非真正意义上的‘最小’。
解决此问题用的最多的应该就是旋转卡壳法,想了解的朋友可以阅读下面这篇文章https://blog.csdn.net/wang_heng199/article/details/74477738
旋转卡壳法的原理就是旋转外接矩形的边,去‘卡’这个多边形区域。但是我认为旋转矩形边并做起来并不容易,所以我想到的是用旋转坐标的方法代替旋转矩形的两个边。旋转坐标法请看:https://blog.csdn.net/qq_36424540/article/details/81347920?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164154490316781683927925%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164154490316781683927925&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-81347920.pc_search_insert_ulrmf&utm_term=%E5%9D%90%E6%A0%87%E6%97%8B%E8%BD%AC&spm=1018.2226.3001.4187
据推断,最小外接矩形的边与多边形的一条边是重合的。所以我想可以通过旋转多边形的坐标,使多边形其中的一条边与Y轴垂直,这样在新的坐标下就可以很容易的确定多边形坐标点中x和y的最小值以及最大值。如下图所示。
如此重复,遍历多边形的每一个边,对比所得到的数据就可以确定该多边形最小外接矩形的面积和顶点坐标。然后再通过‘逆旋转’确定最小外接矩形的真实位置。
函数代码如下:
function [ real_point ] = MinRectangleFind( bw )
%此函数为确定任意多边形的最小外接矩形的函数。
%输入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;
Lx0=length(x0);
F2=PointSort(F1,2);%给顶点排序,顺时针排序
%%%多边形的边转换为向量
F3=[Lx0,2];%设置空数组用来存储向量
for i=1:Lx0-1
F3(i,:)=F2(i+1,:)-F2(i,:);%将凸多边形的每条边都转变成向量
end
F3(Lx0,:)=F2(1,:)-F2(Lx0,:);
%%%坐标旋转,计算不同旋转角度下最小外接矩形的面积
rectangle_data=[Lx0,6];%设置空数组,存储不同旋转角度、矩形面积、边长信息。
syms theta
for j=1:Lx0
eq=F3(j,1)*cos(theta)-F3(j,2)*sin(theta)==0;%计算旋转角度,使向量垂直于x轴
h=solve(eq);%注:可能有多个解
h=eval(h);%将sym转换成double,否则会导致运算速度慢甚至可能出现运算错误
turn_x=F2(:,1).*cos(h(1))-F2(:,2).*sin(h(1));
turn_y=F2(:,1).*sin(h(1))+F2(:,2).*cos(h(1));%将多边形进行坐标旋转
MIN_X=min(turn_x);MIN_Y=min(turn_y);
MAX_X=max(turn_x);MAX_Y=max(turn_y);
L=MAX_Y-MIN_Y;%计算矩形的长
W=MAX_X-MIN_X;%计算矩形的宽
S=L*W;%计算矩形的面积
rectangle_data(j,1)=S;
rectangle_data(j,2)=h(1);
rectangle_data(j,3)=MIN_X;
rectangle_data(j,4)=MIN_Y;
rectangle_data(j,5)=MAX_X;
rectangle_data(j,6)=MAX_Y;%存储矩形信息
end
%%%确定矩形最小外接矩形的旋转角度和四个顶点的坐标
[~,num]=min(rectangle_data(:,1));
angle=rectangle_data(num,2);%记录最小方框的角度
point(1,:)=rectangle_data(num,3:4);
point(2,1)=rectangle_data(num,5);point(2,2)=rectangle_data(num,4);
point(3,:)=rectangle_data(num,5:6);
point(4,1)=rectangle_data(num,3);point(4,2)=rectangle_data(num,6);
%%%返回最小外接矩形的坐标
C=[cos(angle),-sin(angle);sin(angle),cos(angle)];
tip_point=C\point.';%因为(xn,yn)=A(x,y),所以(x,y)=inv(A)(xn,yn)
tip_point=tip_point.';
[x,y]=find(bw==1);
start_x=min(x);
start_y=min(y);
real_point(:,1)=tip_point(:,1)+start_x-1;
real_point(:,2)=tip_point(:,2)+start_y-1;%因ConvexImage与bw大小不一
end
这其中的PointSort函数详见我的上一篇文章:https://blog.csdn.net/a_peng1/article/details/122328979
测试程序
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(121)
imshow(img)
[point]=MinRectangleFind(img);
point=[point;point(1,:)];
subplot(122)
imshow(img)
hold on
plot(point(:,2),point(:,1),'LineWidth',3,'color','r');
hold off
测试结果