Matlab最小面积包围四边形

对于存在透视变换的物体,提取时最小面积包围矩形不能满足要求,google到一个求最小面积包围四边形的算法,虽然速度较慢。以提取书本为例,实验结果和代码如下。

Matlab最小面积包围四边形_第1张图片

Matlab最小面积包围四边形_第2张图片

Matlab最小面积包围四边形_第3张图片

booktest.m

I = imread('book1.jpg');
figure(1);
imshow(I);
Ir = I(:,:,1);
Ismall = imresize(Ir,0.25);
bw =~imbinarize(Ismall);
imshow(bw);
bw2 = bwareaopen(bw,1000);
imshow(bw2);

stats = regionprops(bw2,'BoundingBox','ConvexHull');
hold on;
for i=1:numel(stats)
    %凸包
    h1=impoly(gca,stats(i).ConvexHull);
    api = iptgetapi(h1);
    api.setColor('red');
    %包围矩形
    h2=imrect(gca,stats(i).BoundingBox);
    api = iptgetapi(h2);
    api.setColor('blue');
    %包围盒(最小面积包围矩形)
    point = stats(i).ConvexHull;
    c = minBoundingBox(point');
    plot(c(1,[1:end 1]),c(2,[1:end 1]),'g')
    %最小面积包围四边形
    [qx,qy] = minboundquad(point(:,1),point(:,2));
    h3=impoly(gca,[qx',qy']);
    api = iptgetapi(h3);
    api.setColor('yellow');
end
hold off;

%整理四个点位置
%记最左点为1,同最左的话,靠下为1
%       2
%1
%            3
%    4

quadVetices = [qx(1:4)',qy(1:4)'];
quadVetices  = quadVetices .* 4;
[quadVeticesSort,index] = sortrows(quadVetices);
if quadVeticesSort(1,1) == quadVeticesSort(2,1)
    if quadVeticesSort(1,2) < quadVeticesSort(2,2)
        k = index(1);
    else
        k = index(2);
    end
else
    k = index(1);
end
i = mod((k-1):(k+2),4)+1;

rRect = quadVetices(i,:);
%计算边长
len = zeros(4,1);
for i = 1:3
    len(i) = sqrt(power(rRect(i,1)-rRect(i+1,1),2)+power(rRect(i,2)-rRect(i+1,2),2));
end
len(4) = sqrt(power(rRect(4,1)-rRect(1,1),2)+power(rRect(4,2)-rRect(1,2),2));
dsth = (len(1)+len(3))/2;
dstw = (len(2)+len(4))/2;
dstRect  = [0,dsth;0,0;dstw,0;dstw,dsth;];
%纠正透视变换
tform = estimateGeometricTransform(rRect, dstRect,...
    'projective');
imgBp = imwarp(I, tform,...
    'OutputView',imref2d([floor(dsth),floor(dstw)]));

imshow(imgBp);

2D minimal bounding box-minBoundingBox()
http://cn.mathworks.com/matlabcentral/fileexchange/31126-2d-minimal-bounding-box?focused=5188215&tab=function
A suite of minimal bounding objects-minboundquad()
http://cn.mathworks.com/matlabcentral/fileexchange/34767-a-suite-of-minimal-bounding-objects?focused=3820662&tab=function
这个代码包中还包含了最小面积包围三角形,包围圆,椭圆等多种形状。

现在总是报警告,暂时没时间细看了

In minboundquad (line 181)
In booktest (line 27)
警告: 矩阵为奇异工作精度。

minboundquad.m
我稍微改了下,
在第125行增加nedges = size(edges,1);


function [qx,qy,quadarea] = minboundquad(x,y)
% minboundquad: Compute the minimum area bounding quadrilateral of points in the plane
% usage: [qx,qy] = minboundquad(x,y)
%
% arguments: (input)
%  x,y - vectors of points, describing points in the plane as
%        (x,y) pairs. x and y must be the same size.
%
% arguments: (output)
%  qx,qy - 5x1 vectors of points that define the minimum
%        area bounding quadrilateral.
%
% WARNING: Most 2-d convex hulls are fairly small.
% However, this code will be O(N^4), where N is the
% number of distinct edges in the convex hull of
% your data. So finding the bounding quadrilateral
% of 1000 points around the perimeter of a circle 
% will take much time.
%
% Example usage:
%  x = randn(50,1);
%  y = randn(50,1);
%  [qx,qy] = minboundquad(x,y);
%  plot(x,y,'ro',qx,qy,'b-')
%
%
% See also: minboundcircle, minboundrect, minboundtri
%
%
% Author: John D'Errico
% E-mail: [email protected]
% Release: 1.0
% Release date: 3/14/07

% preprocess data
x=x(:);
y=y(:);

% not many error checks to worry about
n = length(x);
if n~=length(y)
  error('MINBOUNDQUAD:size','x and y must be the same sizes')
end

% start out with the convex hull of the points to
% reduce the problem dramatically. Note that any
% points in the interior of the convex hull are
% never needed, so we drop them.
xy = [x,y];
if n>3
  edges = convhull(x,y);
  % convhull returns a list of points around
  % the perimeter. I prefer the convhulln form,
  % where I have an explicit list of edges.
  % Make it so.
  edges = [edges(1:(end-1)),edges(2:end)];
elseif n==3
  % it is a triangle. I don't care how we
  % traverse it. Replicate a vertex into
  % a quadrilateral.
  qx = xy([1 2 3 3 1],1);
  qy = xy([1 2 3 3 1],2);
  quadarea = polyarea(qx,qy);
  return
elseif n==2
  % a single edge
  qx = xy([1 2 2 2 1],1);
  qy = xy([1 2 2 2 1],2);
  % no area to be found
  quadarea = 0;
  return
elseif n==1
  % a single point
  qx = xy([1 1 1 1 1],1);
  qy = xy([1 1 1 1 1],2);
  quadarea = 0;
  return
else
  % empty begets empty
  qx = [];
  qy = [];
  quadarea = 0;
  return
end
nedges = size(edges,1);

% now we must find the bounding quadrilateral of those
% that remain.

% special case small numbers of points. If we trip any
% of these cases, then we are done, so return.
if nedges == 3
  qx = xy([1 2 3 1],1);
  qy = xy([1 2 3 1],2);
  return
elseif nedges == 4
  qx = xy([1 2 3 4 1],1);
  qy = xy([1 2 3 4 1],2);
  return
end
% more than 4 points.

% get the angle of each edge of the hull polygon
edgeangles = atan2(xy(edges(:,2),2) - xy(edges(:,1),2), ...
  xy(edges(:,2),1) - xy(edges(:,1),1));

% (These next two steps are probably superfluous.)
% work with all positive angles
k = edgeangles < 0;
edgeangles(k) = edgeangles(k) + 2*pi;
% sort the edges into increasing order of angle
[edgeangles,tags] = sort(edgeangles);
edges = edges(tags,:);

% Look for consecutive edges that have the same
% angles. This test will generally only trip if
% the data set has multiple collinear points
% around the perimeter.
angletol = eps*100;
k = diff(edgeangles) < angletol;
edges(k,:) = [];
edgeangles(k) = [];

% there are nchoosek(nedges,4) sets of edges to
% worry about
nedges = size(edges,1);
edgelist = nchoosek(1:nedges,4);

% The edges are now sorted in counter-clockwise
% order around the convex hull. We can toss out any
% combination of edges where the last edge angle
% minus the first is less than 180 degrees
% (i.e., pi radians.)
k = (edgeangles(edgelist(:,4)) - edgeangles(edgelist(:,1)) <= pi);
edgelist(k,:) = [];

% how many edges remain that can form a valid
% quadrilateral?
nquads = size(edgelist,1);

% test each set of 4 edges
quadarea = inf;
qxi = zeros(1,5);
qyi = zeros(1,5);
qx = qxi;
qy = qyi;
for i = 1:nquads
  % find the intersections of each consecutive
  % pair of edges.
  edgeind = edgelist(i,:);
  edgesi = edges(edgeind([1 2 3 4 1]),:);

  if any(diff(edgeangles(edgeind)) > pi)
    % if one of the consecutive angles is too
    % large, then this set of edges will be a
    % failed quadrilateral.
    continue
  end

  for j = 1:4
    % Does this pair of edges share a node from
    % the convex hull?
    jplus1 = j + 1;
    shared = intersect(edgesi(j,:),edgesi(jplus1,:));
    if ~isempty(shared)
      % there was a shared node between these edges
      qxi(j) = xy(shared,1);
      qyi(j) = xy(shared,2);
    else
      % no shared node, so we must find the
      % intersection of the edges by extrapolation
      % of the lines that contain these edges to
      % see where they intersect.
      A = xy(edgesi(j,1),:);
      B = xy(edgesi(j,2),:);
      C = xy(edgesi(jplus1,1),:);
      D = xy(edgesi(jplus1,2),:);

      % solve for the line parameters that correspond
      % to the intersection point
      ts = [(A-B)',(D-C)']\(A-C)';
      % recover the intersection point
      Q = A + (B-A)*ts(1);

      qxi(j) = Q(1);
      qyi(j) = Q(2);
    end
  end
  % wrap the polygon
  qxi(5) = qxi(1);
  qyi(5) = qyi(1);

  % compute the area. Simplest is to use polyarea.
  % I might want to test to see if it is faster
  % to compute the area using other methods though.
  A_i = polyarea(qxi,qyi);

  if (A_i < quadarea)
    % keep this one
    quadarea = A_i;

    qx = qxi;
    qy = qyi;
  end

  % plot the points, the current quad, and the best quad
%  plot(xy(:,1),xy(:,2),'ko')
%  hold on
%  plot([xy(edgesi(1:4,1),1),xy(edgesi(1:4,2),1)]', ...
%    [xy(edgesi(1:4,1),2),xy(edgesi(1:4,2),2)]','g*-','linewidth',8)
%  plot(qx,qy,'b-',qxi,qyi,'r:')
%  hold off
end

% plot the points and the quad
plot(xy(:,1),xy(:,2),'r.',qx,qy,'b-')

你可能感兴趣的:(编程)