Discover MakerZone

MATLAB and Simulink resources for Arduino, LEGO, and Raspberry Pi

Learn more

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply Today

Matlab: find the contour and straighten a nearly rectangular image

Asked by Carine on 6 Mar 2013


I have got this image which is rectangle-like but not exactly.

I would like to find rectangular contours and I have tried to use edge and hough but it doesn't find continuous line. I have also tried to erode and dilate but it can't erase the difference of one pixel on the horizontal upper most line for example so it still doesn't find the continuous line. The second part of the problem would be to straighten it.

Another idea I got was to find the corners to link them with straight lines but it doesn't work either.

I am sure it shouldn't be complicated but I can't make it. Any ideas will be welcome!




3 Answers

Answer by Teja Muppirala on 7 Mar 2013
Accepted answer

I've done this before:

This is the strategy that I used and it seemed to work on your image as well. Basically you search for the corners, and then use IMTRANSFORM to transorm the corners into a rectangle. (I'll assume your image is called I).

%% 1. Get rid of the white border
I2 = imclearborder(im2bw(I));
%% 2. Find each of the four corners
[y,x] = find(I2);
[~,loc] = min(y+x);
C = [x(loc),y(loc)];
[~,loc] = min(y-x);
C(2,:) = [x(loc),y(loc)];
[~,loc] = max(y+x);
C(3,:) = [x(loc),y(loc)];
[~,loc] = max(y-x);
C(4,:) = [x(loc),y(loc)];
%% 3. Plot the corners
imshow(I); hold all
plot(C([1:4 1],1),C([1:4 1],2),'r','linewidth',3);
%% 4. Find the locations of the new  corners
L = mean(C([1 4],1));
R = mean(C([2 3],1));
U = mean(C([1 2],2));
D = mean(C([3 4],2));
C2 = [L U; R U; R D; L D];
%% 5. Do the image transform
T = cp2tform(C ,C2,'projective');
IT = imtransform(im2bw(I),T); %IM2BW is not necessary
imshow(IT); hold all


Teja Muppirala
Answer by Image Analyst on 7 Mar 2013


You can straighten your image with imtransform(), like in this demo I wrote for you using your uploaded image:

clc;    % Clear the command window.
close all;  % Close all figures (except those of imtool.)
imtool close all;  % Close all imtool figures if you have the Image Processing Toolbox.
clear;  % Erase all existing variables. Or clearvars if you want.
workspace;  % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 20;
% Check that user has the Image Processing Toolbox installed.
hasIPT = license('test', 'image_toolbox');
if ~hasIPT
	% User does not have the toolbox installed.
	message = sprintf('Sorry, but you do not seem to have the Image Processing Toolbox.\nDo you want to try to continue anyway?');
	reply = questdlg(message, 'Toolbox missing', 'Yes', 'No', 'Yes');
	if strcmpi(reply, 'No')
		% User said No, so exit.
% Read in a standard MATLAB gray scale demo image.
folder = 'C:\Users\Carine\Documents';
baseFileName = 'rect.jpg';
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
	fullFileName = baseFileName; % No path this time.
	if ~exist(fullFileName, 'file')
		% Still didn't find it.  Alert user.
grayImage = imread(fullFileName);
% Get the dimensions of the image.  
% numberOfColorBands should be = 1.
[rows columns numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
	grayImage = grayImage(:,:,2); % Take green channel
% Binarize the image
binaryImage1 = grayImage > 128;
% Get rid of the white border
binaryImage1 = imclearborder(binaryImage1);
% Display the original gray scale image.
subplot(2, 2, 1);
imshow(binaryImage1, []);
title('Binary Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
% Give a name to the title bar.
set(gcf,'name','Demo by ImageAnalyst','numbertitle','off') 
% Fill the object
binaryImage2 = imfill(binaryImage1, 'holes');
% Find the boundaries
boundaries = bwboundaries(binaryImage2);
xCoords = boundaries{1}(:, 2);
yCoords = boundaries{1}(:, 1);
% Find out which index is closest to the upper left corner
distances = sqrt((xCoords - 1).^2 + (yCoords - 1).^2);
[minDistanceUL, minIndexUL] = min(distances)
% Find out which index is closest to the upper right corner
distances = sqrt((xCoords-columns).^2 + (yCoords - 1).^2);
[minDistanceUR, minIndexUR] = min(distances)
% Find out which index is closest to the lower left corner
distances = sqrt((xCoords - 1).^2 + (yCoords - rows).^2);
[minDistanceLL, minIndexLL] = min(distances)
% Find out which index is closest to the lower right corner
distances = sqrt((xCoords - columns).^2 + (yCoords - rows).^2);
[minDistanceLR, minIndexLR] = min(distances)
% Plot circles over the corners, just for visualization purposes - for fun.
xCorners = [xCoords(minIndexUL), xCoords(minIndexUR), xCoords(minIndexLR), xCoords(minIndexLL)]
yCorners = [yCoords(minIndexUL), yCoords(minIndexUR), yCoords(minIndexLR), yCoords(minIndexLL)]
hold on;
plot(xCorners, yCorners, 'ro');
title('Bad image with corners located', 'FontSize', fontSize);
% Determine ideal corner locations - aligned with raster lines
x1 = mean([xCorners(1), xCorners(4)])
x2 = mean([xCorners(2), xCorners(3)])
y1 = mean([yCorners(1), yCorners(2)])
y2 = mean([yCorners(3), yCorners(4)])
% Show this
subplot(2, 2, 2);
hold on;
plot([x1 x2 x2 x1 x1], [y1 y1 y2 y2 y1], 'r-', 'LineWidth', 3);
title('Bad image with perfect rectangular overlaid', 'FontSize', fontSize);
% Warp the image to straighten it.
badXY = [xCorners; yCorners]'
desiredXY = [x1 x2 x2 x1; y1 y1 y2 y2]'
% Transform to a quadrilateral with vertices badXY
% into a quadrilateral with vertices desiredXY.
tform = maketform('projective', badXY, desiredXY);
% Fix/warp the image.
[binaryImage3, xdata, ydata] = imtransform(binaryImage1, ...
	tform, 'bicubic', 'size', size(binaryImage1));
% Display the fixed image.
subplot(2, 2, 3);
title('Fixed image', 'FontSize', fontSize);


Image Analyst
Answer by Carine on 11 Mar 2013

Thank you to the two of you, your answers have been very useful and the tutorial on sudoku solver impressive! I have mainly used the answer of Teja Muppirala but both give nearly the same result. To get rid off the white border I have inverted the colors first:

   I2 = imclearborder(1-I); I2 = 1-I2;

Now I've still got a problem as the area size is important in my application. Here is a picture of nearly undeformed image (taken with an angle 0º more or less): <> and here is another one taken from an angle of 30º more or less: <>. I would not only like to "straighten" it but also to recover more or less the same size (area). So what I do from Teja Muppirala's code is modifying the coordinates of the new corners in the following way:

      C21 = C2;
    % 5. Scale the new corners so that the size of the rectangle is the size of the largest borders of the original picture
    % Ratio between horizontal borders
    dh1 =  distance(C(1,:),C(2,:));
    dh2 =  distance(C(4,:),C(3,:));
    if dh1<dh2
      dmh = dh1; dMh = dh2;
      imax = 2;
      dmh = dh2; dMh = dh1;
      imax = 1;
    C21(:,1) = C21(:,1)*dMh/dmh;
    % Ratio between vertical borders
    dh1 =  distance(C(1,:),C(4,:));
    dh2 =  distance(C(2,:),C(3,:));
    if dh1<dh2
      dmv = dh1; dMv = dh2;
      if imax == 1
        imax = 2; %imaxv = 23, imaxh = 12
        imax = 3; %imaxv = 23, imaxh = 34
      dmv = dh2; dMv = dh1;
      if imax == 1
        imax = 1; %imaxv = 14, imaxh = 12
        imax = 4; %imaxv = 14, imaxh = 34
    C21(:,2) = C21(:,2)*dMv/dmv;
    %imax is the index of the largest and longest border. Translate the new rectangle so that it passes by C2 of that index
    for index = 1
      C22(:,index) = C21(:,index)-C21(imax,index)+C2(imax,index);

where distance is just d = sqrt(sum(C1-C2).^2);.

What do you think of the idea? Any better one?

Thank you again!



Contact us