Compute contour of a binary image using matlab - image

I am trying to compute contour of a binary image. Currently i identify the first non zero and the last non zero pixel in the image through looping. Is there a better way? i have encountered few functions:
imcontour(I)
bwtraceboundary(bw,P,fstep,conn,n,dir)
But the first doesn't return the x and y coordinates of the contour. The second function requires a seed point which i cannot provide. An example of the image is shown below. Thanks.

I'm surprised you didn't see bwperim. Did you not try bwperim? This finds the perimeter pixels of all closed objects that are white in a binary image. Using your image directly from StackOverflow:
im = im2bw(imread('http://i.stack.imgur.com/yAZ5L.png'));
out = bwperim(im);
imshow(out);
We get:

#rayryeng have already provided the correct answer. As another approach (might be that bwperim performs this operations internally) boundaries of a binary image can be obtained by calculating the difference between the dilated and the eroded image.
For a given image:
im = im2bw(imread('http://i.stack.imgur.com/yAZ5L.png'));
and a given binary structural element:
selem = ones(3,3); %// square, 8-Negihbours
% selem = [0 1 0; 1 0 1; 0 1 0]; %// cross, 4-Neighbours
The contour of the object can be extracted as:
out = imerode(im, selem) ~= imdilate(im, selem);
Here, however, the boundary is thicker than using bwperim, as the pixels are masked in both inside and outside of the object.

I had the same problem, stumbled across this question and just wanted to add that imcontour(Img); does return a matrix. The first row contains the x-values, the second row contains the y-values.
contour = imcontour(Img); x = contour(1,:); y = contour(2,:);
But I would discard the first column.

Related

How to detect a portion of an image based upon the matrix values?

I have a simple pcolor plot in Matlab (Version R 2016b) which I have uploaded as shown in the image below. I need to get only the blue sloped line which extends from the middle of the leftmost corner to the rightmost corner without hard-coding the matrix values.
For instance: One can see that the desired slope line has values somewhere approximately between 20 to 45 from the pcolor plot. (From a rough guess just by looking at the graph)
I'm applying the following code on the matrix named Slant which contains the plotted values.
load('Slant.mat');
Slant(Slant<20|Slant>50)=0;
pcolor(Slant); colormap(jet); shading interp; colorbar;
As one can see I hard-coded the values which I don't want to. Is there any method of detecting certain matrix values while making the rest equal to zero?
I used an other small algorithm of taking half the maximum value from the matrix and setting it to zero. But this doesn't work for other images.
[maxvalue, row] = max(Slant);
max_m=max(maxvalue);
Slant(Slant>max_m/2)=0;
pcolor(Slant); colormap(jet); shading interp; colorbar;
Here is another suggestion:
Remove all the background.
Assuming this "line" results in a Bimodal distribution of the data (after removing the zeros), find the lower mode.
Assuming the values of the line are always lower than the background, apply a logic mask that set to zeros all values above the minimum + 2nd_mode, as demonstrated in the figure below (in red circle):
Here is how it works:
A = Slant(any(Slant,2),:); % save in A only the nonzero data
Now we have A that looks like this:
[y,x] = findpeaks(histcounts(A)); % find all the mode in the histogram of A
sorted_x = sortrows([x.' y.'],-2); % sort them by their hight in decendet order
mA = A<min(A(:))+sorted_x(2,1); % mask all values above the second mode
result = A.*mA; % apply the mask on A
And we get the result:
The resulted line has some holes within it, so you might want to interpolate the whole line from the result. This can be done with simple math on the indices:
[y1,x1] = find(mA,1); % find the first nonzero row
[y2,x2] = find(mA,1,'last'); % find the last nonzero row
m = (y1-y2)/(x1-x2); % the line slope
n = y1-m*x1; % the intercept
f_line = #(x) m.*x+n; % the line function
So we get a line function f_line like this (in red below):
Now we want to make this line thicker, like the line in the data, so we take the mode of the thickness (by counting the values in each column, you might want to take max instead), and 'expand' the line by half of this factor to both sides:
thick = mode(sum(mA)); % mode thickness of the line
tmp = (1:thick)-ceil(thick/2); % helper vector for expanding
rows = bsxfun(#plus,tmp.',floor(f_line(1:size(A,2)))); % all the rows for each coloumn
rows(rows<1) = 1; % make sure to not get out of range
rows(rows>size(A,1)) = size(A,1); % make sure to not get out of range
inds = sub2ind(size(A),rows,repmat(1:size(A,2),thick,1)); % convert to linear indecies
mA(inds) = 1; % add the interpolation to the mask
result = A.*mA; % apply the mask on A
And now result looks like this:
Idea: Use the Hough transform:
First of all it is best to create a new matrix with only the rows and columns we are interested in.
In order to apply matlab's built in hough we have to create a binary image: As the line always has lower values than the rest, we could e.g. determine the lowest quartile of the brightnesses present in the picture (using quantile, and set these to white, everything else to black.
Then to find the line, we can use hough directly on that BW image.

Matlab Code : How can I detect shapes with one or more holes in an image using structure elements and logic [duplicate]

So I have a binary matrix in Matlab.
It is basically a blob (pixels of value 1) surrounded by a neutral background (value 0).
I want to figure out whether this blob is simply connected or not.
Figure below is a straightforward example.
How can this be achieved?
Notably I understand that every path in a pixelated image can be created by choosing from 4 adjacent elements (up, down, left, right) or 8 adjacent elements etc - it doesn't matter in this case.
Code
%// Assuming bw1 is the input binary matrix
[L,num] = bwlabel( ~bw1 );
counts = sum(bsxfun(#eq,L(:),1:num));
[~,ind] = max(counts);
bw2 = ~(L==ind);
%// Output decision
[L,num] = bwlabel( bw1 );
if ~nnz(bw1~=bw2) && num==1
disp('Yes it is a simply connected blob.')
else
disp('Nope, not a simply connected blob.')
end

Resize an image with bilinear interpolation without imresize

I've found some methods to enlarge an image but there is no solution to shrink an image. I'm currently using the nearest neighbor method. How could I do this with bilinear interpolation without using the imresize function in MATLAB?
In your comments, you mentioned you wanted to resize an image using bilinear interpolation. Bear in mind that the bilinear interpolation algorithm is size independent. You can very well use the same algorithm for enlarging an image as well as shrinking an image. The right scale factors to sample the pixel locations are dependent on the output dimensions you specify. This doesn't change the core algorithm by the way.
Before I start with any code, I'm going to refer you to Richard Alan Peters' II digital image processing slides on interpolation, specifically slide #59. It has a great illustration as well as pseudocode on how to do bilinear interpolation that is MATLAB friendly. To be self-contained, I'm going to include his slide here so we can follow along and code it:
Please be advised that this only resamples the image. If you actually want to match MATLAB's output, you need to disable anti-aliasing.
MATLAB by default will perform anti-aliasing on the images to ensure the output looks visually pleasing. If you'd like to compare apples with apples, make sure you disable anti-aliasing when comparing between this implementation and MATLAB's imresize function.
Let's write a function that will do this for us. This function will take in an image (that is read in through imread) which can be either colour or grayscale, as well as an array of two elements - The image you want to resize and the output dimensions in a two-element array of the final resized image you want. The first element of this array will be the rows and the second element of this array will be the columns. We will simply go through this algorithm and calculate the output pixel colours / grayscale values using this pseudocode:
function [out] = bilinearInterpolation(im, out_dims)
%// Get some necessary variables first
in_rows = size(im,1);
in_cols = size(im,2);
out_rows = out_dims(1);
out_cols = out_dims(2);
%// Let S_R = R / R'
S_R = in_rows / out_rows;
%// Let S_C = C / C'
S_C = in_cols / out_cols;
%// Define grid of co-ordinates in our image
%// Generate (x,y) pairs for each point in our image
[cf, rf] = meshgrid(1 : out_cols, 1 : out_rows);
%// Let r_f = r'*S_R for r = 1,...,R'
%// Let c_f = c'*S_C for c = 1,...,C'
rf = rf * S_R;
cf = cf * S_C;
%// Let r = floor(rf) and c = floor(cf)
r = floor(rf);
c = floor(cf);
%// Any values out of range, cap
r(r < 1) = 1;
c(c < 1) = 1;
r(r > in_rows - 1) = in_rows - 1;
c(c > in_cols - 1) = in_cols - 1;
%// Let delta_R = rf - r and delta_C = cf - c
delta_R = rf - r;
delta_C = cf - c;
%// Final line of algorithm
%// Get column major indices for each point we wish
%// to access
in1_ind = sub2ind([in_rows, in_cols], r, c);
in2_ind = sub2ind([in_rows, in_cols], r+1,c);
in3_ind = sub2ind([in_rows, in_cols], r, c+1);
in4_ind = sub2ind([in_rows, in_cols], r+1, c+1);
%// Now interpolate
%// Go through each channel for the case of colour
%// Create output image that is the same class as input
out = zeros(out_rows, out_cols, size(im, 3));
out = cast(out, class(im));
for idx = 1 : size(im, 3)
chan = double(im(:,:,idx)); %// Get i'th channel
%// Interpolate the channel
tmp = chan(in1_ind).*(1 - delta_R).*(1 - delta_C) + ...
chan(in2_ind).*(delta_R).*(1 - delta_C) + ...
chan(in3_ind).*(1 - delta_R).*(delta_C) + ...
chan(in4_ind).*(delta_R).*(delta_C);
out(:,:,idx) = cast(tmp, class(im));
end
Take the above code, copy and paste it into a file called bilinearInterpolation.m and save it. Make sure you change your working directory where you've saved this file.
Except for sub2ind and perhaps meshgrid, everything seems to be in accordance with the algorithm. meshgrid is very easy to explain. All you're doing is specifying a 2D grid of (x,y) co-ordinates, where each location in your image has a pair of (x,y) or column and row co-ordinates. Creating a grid through meshgrid avoids any for loops as we will have generated all of the right pixel locations from the algorithm that we need before we continue.
How sub2ind works is that it takes in a row and column location in a 2D matrix (well... it can really be any amount of dimensions you want), and it outputs a single linear index. If you're not aware of how MATLAB indexes into matrices, there are two ways you can access an element in a matrix. You can use the row and column to get what you want, or you can use a column-major index. Take a look at this matrix example I have below:
A =
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
If we want to access the number 9, we can do A(2,4) which is what most people tend to default to. There is another way to access the number 9 using a single number, which is A(11)... now how is that the case? MATLAB lays out the memory of its matrices in column-major format. This means that if you were to take this matrix and stack all of its columns together in a single array, it would look like this:
A =
1
6
11
2
7
12
3
8
13
4
9
14
5
10
15
Now, if you want to access element number 9, you would need to access the 11th element of this array. Going back to the interpolation bit, sub2ind is crucial if you want to vectorize accessing the elements in your image to do the interpolation without doing any for loops. As such, if you look at the last line of the pseudocode, we want to access elements at r, c, r+1 and c+1. Note that all of these are 2D arrays, where each element in each of the matching locations in all of these arrays tell us the four pixels we need to sample from in order to produce the final output pixel. The output of sub2ind will also be 2D arrays of the same size as the output image. The key here is that each element of the 2D arrays of r, c, r+1, and c+1 will give us the column-major indices into the image that we want to access, and by throwing this as input into the image for indexing, we will exactly get the pixel locations that we want.
There are some important subtleties I'd like to add when implementing the algorithm:
You need to make sure that any indices to access the image when interpolating outside of the image are either set to 1 or the number of rows or columns to ensure you don't go out of bounds. Actually, if you extend to the right or below the image, you need to set this to one below the maximum as the interpolation requires that you are accessing pixels to one over to the right or below. This will make sure that you're still within bounds.
You also need to make sure that the output image is cast to the same class as the input image.
I ran through a for loop to interpolate each channel on its own. You could do this intelligently using bsxfun, but I decided to use a for loop for simplicity, and so that you are able to follow along with the algorithm.
As an example to show this works, let's use the onion.png image that is part of MATLAB's system path. The original dimensions of this image are 135 x 198. Let's interpolate this image by making it larger, going to 270 x 396 which is twice the size of the original image:
im = imread('onion.png');
out = bilinearInterpolation(im, [270 396]);
figure;
imshow(im);
figure;
imshow(out);
The above code will interpolate the image by increasing each dimension by twice as much, then show a figure with the original image and another figure with the scaled up image. This is what I get for both:
Similarly, let's shrink the image down by half as much:
im = imread('onion.png');
out = bilinearInterpolation(im, [68 99]);
figure;
imshow(im);
figure;
imshow(out);
Note that half of 135 is 67.5 for the rows, but I rounded up to 68. This is what I get:
One thing I've noticed in practice is that upsampling with bilinear has decent performance in comparison to other schemes like bicubic... or even Lanczos. However, when you're shrinking an image, because you're removing detail, nearest neighbour is very much sufficient. I find bilinear or bicubic to be overkill. I'm not sure about what your application is, but play around with the different interpolation algorithms and see what you like out of the results. Bicubic is another story, and I'll leave that to you as an exercise. Those slides I referred you to does have material on bicubic interpolation if you're interested.
Good luck!

Vector decomposition in matlab

this is my situation: I have a 30x30 image and I want to calculate the radial and tangent component of the gradient of each point (pixel) along the straight line passing through the centre of the image (15,15) and the same (i,j) point.
[dx, dy] = gradient(img);
for i=1:30
for j=1:30
pt = [dx(i, j), dy(i,j)];
line = [i-15, j-15];
costh = dot(line, pt)/(norm(line)*norm(pt));
par(i,j) = norm(costh*line);
tang(i,j) = norm(sin(acos(costh))*line);
end
end
is this code correct?
I think there is a conceptual error in your code, I tried to get your results with a different approach, see how it compares to yours.
[dy, dx] = gradient(img);
I inverted x and y because the usual convention in matlab is to have the first dimension along the rows of a matrix while gradient does the opposite.
I created an array of the same size as img but with each pixel containing the angle of the vector from the center of the image to this point:
[I,J] = ind2sub(size(img), 1:numel(img));
theta=reshape(atan2d(I-ceil(size(img,1)/2), J-ceil(size(img,2)/2)), size(img))+180;
The function atan2d ensures that the 4 quadrants give distinct angle values.
Now the projection of the x and y components can be obtained with trigonometry:
par=dx.*sind(theta)+dy.*cosd(theta);
tang=dx.*cosd(theta)+dy.*sind(theta);
Note the use of the .* to achieve point-by-point multiplication, this is a big advantage of Matlab's matrix computations which saves you a loop.
Here's an example with a well-defined input image (no gradient along the rows and a constant gradient along the columns):
img=repmat(1:30, [30 1]);
The results:
subplot(1,2,1)
imagesc(par)
subplot(1,2,2)
imagesc(tang)
colorbar

Detect a rectangle bound of an character or object in black/white or binary image

I'm developing a handwriting recognition project. one of the requirements of this project is getting an image input, this image only contains some character object in a random location, and firstly I must extract this characters to process in next step.
Now I'm confusing a hard problem like that: how to extract one character from black/white (binary)image or how to draw a bound rectangle of a character in black - white (binary) image?
Thanks very much!
If you are using MATLAB (which I hope you are, since it it awesome for tasks like these), I suggest you look into the built in function bwlabel() and regionprops(). These should be enough to segment out all the characters and get their bounding box information.
Some sample code is given below:
%Read image
Im = imread('im1.jpg');
%Make binary
Im(Im < 128) = 1;
Im(Im >= 128) = 0;
%Segment out all connected regions
ImL = bwlabel(Im);
%Get labels for all distinct regions
labels = unique(ImL);
%Remove label 0, corresponding to background
labels(labels==0) = [];
%Get bounding box for each segmentation
Character = struct('BoundingBox',zeros(1,4));
nrValidDetections = 0;
for i=1:length(labels)
D = regionprops(ImL==labels(i));
if D.Area > 10
nrValidDetections = nrValidDetections + 1;
Character(nrValidDetections).BoundingBox = D.BoundingBox;
end
end
%Visualize results
figure(1);
imagesc(ImL);
xlim([0 200]);
for i=1:nrValidDetections
rectangle('Position',[Character(i).BoundingBox(1) ...
Character(i).BoundingBox(2) ...
Character(i).BoundingBox(3) ...
Character(i).BoundingBox(4)]);
end
The image I read in here are from 0-255, so I have to threshold it to make it binary. As dots above i and j can be a problem, I also threshold on the number of pixels which make up the distinct region.
The result can be seen here:
https://www.sugarsync.com/pf/D775999_6750989_128710
The better way to extract the character in my case was the segmentation for histogram i only can share with you some papers.
http://cut.by/j7LE8
http://cut.by/PWJf1
may be this can help you
One simple option is to use an exhaustive search, like (assuming text is black and background is white):
Starting from the leftmost column, step through all the rows checking for a black pixel.
When you encounter your first black pixel, save your current column index as left.
Continue traversing the columns until you encounter a column with no black pixels in it, save this column index as right.
Now traverse the rows in a similar fashion, starting from the topmost row and stepping through each column in that row.
When you encounter your first black pixel, save your current row index as top.
Continue traversing the rows until you find one with no black pixels in it, and save this row as `bottom.
You character will be contained within the box defined by (left - 1, top - 1) as the top-left corner and (right, bottom) as the bottom-right corner.

Resources