Crop an image based on its binary - Matlab - image

I wish to crop this original image
to a new image which will contain only the bag with minimum white pixels (essentially reducing the size image to the bag borders)
.
Therefore I decided to first convert it to a binary image
but I do not know how to find the bag corner coordinates [xmin ymin width height] in order to use them with imcrop(I,rect).
Any help will be great.
The script:
clc;
close all;
url='http://oi65.tinypic.com/i19md1.jpg' ;
rgbImage = imread(url);
grayImage = rgb2gray(rgbImage);
binaryImage = grayImage < 250;
imshow(binaryImage);

That's a very easy task to perform. Since binaryImage contains a mask that you want to use to crop the image, you can find the top-left corner (xmin,ymin) of where you want to crop by finding the smallest column and row coordinate respectively that is non-zero in the mask, then to find width and height, find the bottom-right corner that is non-zero, then subtract the two x coordinates for the width and the two y coordinates for the height. You'll need to add 1 to each difference to account for self-distances (i.e. if you had a width that was 1 pixel wide, you should get a width of 1, not 0). You can use find to help you find the row and column locations that are non-zero. However, imcrop requires that the x coordinates reflect horizontal behaviour and y coordinates reflect vertical behaviour where find returns row and column locations respectively. That's why you'll have to flip them when you call find:
[y,x] = find(binaryImage); %// Find row and column locations that are non-zero
%// Find top left corner
xmin = min(x(:));
ymin = min(y(:));
%// Find bottom right corner
xmax = max(x(:));
ymax = max(y(:));
%// Find width and height
width = xmax - xmin + 1;
height = ymax - ymin + 1;
You can now go ahead and crop the image:
out = imcrop(rgbImage, [xmin ymin width height]);
imshow(out);
I get this for your cropped image:

Related

how to find the height and width of an alphabet/letter

I want to find the height and width of a letter in generalize form but I dont know what algorithm should I apply so that I can find the height and width of any letter
I am using MATLAB as well as openCv. Anyone can suggest me anything how to approach
this image is my test image
my main task is to find the height and width of words say for eg "Football"
You read the image with imread. You find the first instance where pixels are not white (255). You can sum rows and columns to get it quick, but you have to hide the add in the corner for this to work. You can then use the difference between y and x to get the width and height.
img = imread('unKDO.jpg');
% hide lettergenerator add
img(565:end,448:end) = 255;
% see when pixels are less than white
y(1) = find(mean(img) < 255,1);
y(2) = find(mean(img) < 255,1,'last');
x(1) = find(mean(img,2) < 255,1);
x(2) = find(mean(img,2) < 255,1,'last');
figure;
imshow(img)
hold on
plot(y(1),x(1),'*r')
plot(y(2),x(2),'*g')

Finding Circle Boundary Pixels Coordinates and RGB Intensity Values from An RGB Input Image in Matlab

I would like to get pixels coordinates and RGB Intensity values from points that only lie on the specified circle boundary in a given RGB input image without drawing the circle and considering Its specified color. As far as I did is just draw the circle on a particular point. Any help? Thanks.
yellow = uint8([255 255 0]);
% Create the shape inserter object.
shapeInserter = vision.ShapeInserter('Shape','Circles','BorderColor','Custom','CustomBorderColor',yellow);
% Read RGB input image.
I = imread('3.jpg');
% Define the circle dimensions
x1=80;
y1=80;
circle = int32([x1 y1 3]); % [x1 y1 radius]
% Draw the circle and display the result.
J = step(shapeInserter, I, circle);
imshow(J);
You don't need the Computer Vision toolbox for that. Simply define your centre coordinate of your circle and the specified radius, then use a combination of meshgrid and logical indexing to determine (x,y) coordinates as well as the corresponding RGB values along the perimeter of the circle.
Something like this:
%// Define parameters
x1 = 80; y1 = 80;
radius = 3;
tol = 0.1;
%// Get dimensions of image
rows = size(I,1); cols = size(I,2);
%// Define grid of coordinates
[x,y] = meshgrid(1:cols, 1:rows);
%// Define mask for valid pixels
mask = ((x - x1).^2 + (y - y1).^2) >= (radius - tol)^2;
mask = mask & ((x - x1).^2 + (y - y1).^2) <= (radius + tol)^2;
%// Get row and column locations
col = x(mask); row = y(mask);
%// Get pixel values
R = I(:,:,1); G = I(:,:,2); B = I(:,:,3);
red = R(mask); green = G(mask); blue = B(mask);
These statements:
mask = ((x - x1).^2 + (y - y1).^2) >= (radius - tol)^2;
mask = mask & ((x - x1).^2 + (y - y1).^2) <= (radius + tol)^2;
defines the equation of the circle of the form:
(x - x0)^2 + (y - y0)^2 == r^2
The centre coordinate is defined at (x0, y0) with a radius of r. However, due to discrete coordinates in the image, you'll need to get values within some tolerance of the radius. I set this tolerance to be 0.1. This equation defines the boundary of the circle. Therefore, we want to find those locations in the image that allow the above expression to be true.
Once you're done, we can get the row and column locations of the pixels along the boundary and finally we can use the logical mask itself to index into each channel and grab the corresponding red, green and blue pixels for each of the locations that are defined along the boundary.
Here's an example with a radius of 30, centered at (100,100) with rows and columns set to 300 respectively. We visualize what the mask looks like. White denotes that we sample at this point and black means we don't:

Find the new position of rectangle for resized image

I have a four element position vector [xmin ymin width hight] that specifies the size and position of crop rectangle from image I. How can i find the new position and size for the resized image I?
It is not entirely clear, what you want, as we don't know your coordinate system. Assuming x is the horizontal axis and y is the vertical axis and your point (1,1) is at the top left corner, you can use the following snippet:
p = [xmin ymin width height];
I = I_orig(p(2):p(2)+p(4)-1,p(1):p(1)+p(3)-1);
The size is of course your specified width and height.
You can convert your original bounding box to relative values (that is assuming the image size is 1x1)
[origH origW] = size( origI(:,:,1) );
relativeBB = [xmin / origW, ymin / origH, width / origW, hight / origH];
Now, no matter how you resized your origI, you can recover the bounding box w.r.t the new size from the relative representation:
[currH currW] = size(I(:,:,1));
currBB = relativeBB .* [currW, currH, currW, currH];
You might need to round things a bit: you might find floor better for xmin and ymin and ceil more suitable for width and height.

Convert angle quantitative data into qualitative images

I am a crystallographer trying to analyse crystals orientations from up to 5000 files. Can Matlab convert angle values in a table that look like this:
Into a table that looks like this?:
Here's a more concrete example based on Lakesh's idea. However, this will handle any amount of rotation. First start off with a base circular image with a strip in the middle. Once you do this, simply run a for loop that stacks all of these rotated images in a grid that resembles the angles seen in your rotation values matrix for every rotation angle that we see in this matrix.
The trick is to figure out how to define the base orientation image. As such, let's define a white square, with a black circle in the middle. We will also define a red strip in the middle. For now, let's assume that the base orientation image is 51 x 51. Therefore, we can do this:
%// Define a grid of points between -25 to 25 for both X and Y
[X,Y] = meshgrid(-25:25,-25:25);
%// Define radius
radius = 22;
%// Generate a black circle that has the above radius
base_image = (X.^2 + Y.^2) <= radius^2;
%// Make into a 3 channel colour image
base_image = ~base_image;
base_image = 255*cast(repmat(base_image, [1 1 3]), 'uint8');
%// Place a strip in the middle of the circle that's red
width_strip = 44;
height_strip = 10;
strip_locs = (X >= -width_strip/2 & X <= width_strip/2 & Y >= -height_strip/2 & Y <= height_strip/2);
base_image(strip_locs) = 255;
With the above, this is what I get:
Now, all you need to do is create a final output image which has as many images as we have rows and columns in your matrix. Given that your rotation matrix values are stored in M, we can use imrotate from the image processing toolbox and specify the 'crop' flag to ensure that the output image is the same size as the original. However, with imrotate, whatever values don't appear in the image after you rotate it, it defaults to 0. You want this to appear white in your example, so we're going to have to do a bit of work. What you'll need to do is create a logical matrix that is the same size as the input image, then rotate it in the same way like you did with the base image. Whatever pixels appear black (which are also false) in this rotated white image, these are the values we need to set to white. As such:
%// Get size of rotation value matrix
[rows,cols] = size(M);
%// For storing the output image
output_image = zeros(rows*51, cols*51, 3);
%// For each value in our rotation value matrix...
for row = 1 : rows
for col = 1 : cols
%// Rotate the image
rotated_image = imrotate(base_image, M(row,col), 'crop');
%// Take a completely white image and rotate this as well.
%// Invert so we know which values were outside of the image
Mrot = ~imrotate(true(size(base_image)), M(row,col), 'crop');
%// Set these values outside of each rotated image to white
rotated_image(Mrot) = 255;
%// Store in the right slot.
output_image((row-1)*51 + 1 : row*51, (col-1)*51 + 1 : col*51, :) = rotated_image;
end
end
Let's try a few angles to be sure this is right:
M = [0 90 180; 35 45 60; 190 270 55];
With the above matrix, this is what I get for my image. This is stored in output_image:
If you want to save this image to file, simply do imwrite(output_image, 'output.png');, where output.png is the name of the file you want to save to your disk. I chose PNG because it's lossless and has a relatively low file size compared to other lossless standards (save JPEG 2000).
Edit to show no line when the angle is 0
If you wish to use the above code where you want to only display a black circle if the angle is around 0, it's just a matter of inserting an if statement inside the for loop as well creating another image that contains a black circle with no strip through it. When the if condition is satisfied, you'd place this new image in the right grid location instead of the black circle with the red strip.
Therefore, using the above code as a baseline do something like this:
%// Define matrix of sample angles
M = [0 90 180; 35 45 60; 190 270 55];
%// Define a grid of points between -25 to 25 for both X and Y
[X,Y] = meshgrid(-25:25,-25:25);
%// Define radius
radius = 22;
%// Generate a black circle that has the above radius
base_image = (X.^2 + Y.^2) <= radius^2;
%// Make into a 3 channel colour image
base_image = ~base_image;
base_image = 255*cast(repmat(base_image, [1 1 3]), 'uint8');
%// NEW - Create a black circle image without the red strip
black_circle = base_image;
%// Place a strip in the middle of the circle that's red
width_strip = 44;
height_strip = 10;
strip_locs = (X >= -width_strip/2 & X <= width_strip/2 & Y >= -height_strip/2 & Y <= height_strip/2);
base_image(strip_locs) = 255;
%// Get size of rotation value matrix
[rows,cols] = size(M);
%// For storing the output image
output_image = zeros(rows*51, cols*51, 3);
%// NEW - define tolerance
tol = 5;
%// For each value in our rotation value matrix...
for row = 1 : rows
for col = 1 : cols
%// NEW - If the angle is around 0, then draw a black circle only
if M(row,col) >= -tol && M(row,col) <= tol
rotated_image = black_circle;
else %// This is the logic if the angle is not around 0
%// Rotate the image
rotated_image = imrotate(base_image, M(row,col), 'crop');
%// Take a completely white image and rotate this as well.
%// Invert so we know which values were outside of the image
Mrot = ~imrotate(true(size(base_image)), M(row,col), 'crop');
%// Set these values outside of each rotated image to white
rotated_image(Mrot) = 255;
end
%// Store in the right slot.
output_image((row-1)*51 + 1 : row*51, (col-1)*51 + 1 : col*51, :) = rotated_image;
end
end
The variable tol in the above code defines a tolerance where anything within -tol <= angle <= tol has the black circle drawn. This is to allow for floating point tolerances when comparing because it's never a good idea to perform equality operations with floating point values directly. Usually it is accepted practice to compare within some tolerance of where you would like to test for equality.
Using the above modified code with the matrix of angles M as seen in the previous example, I get this image now:
Notice that the top left entry of the matrix has an angle of 0, which is thus visualized as a black circle with no strip through it as we expect.
General idea to solve your problem:
1. Store two images, 1 for 0 degrees and 180 degrees and another for 90 and 270 degrees.
2. Read the data from the file
3. if angle = 0 || angle == 180
image = image1
else
image = image2
end
To handle any angle:
1. Store one image. E.g image = imread('yourfile.png')
2. angle = Read the data from the file
3. B = imrotate(image,angle)

How can I draw a circle on an image in MATLAB?

I have an image in MATLAB:
im = rgb2gray(imread('some_image.jpg');
% normalize the image to be between 0 and 1
im = im/max(max(im));
And I've done some processing that resulted in a number of points that I want to highlight:
points = some_processing(im);
Where points is a matrix the same size as im with ones in the interesting points.
Now I want to draw a circle on the image in all the places where points is 1.
Is there any function in MATLAB that does this? The best I can come up with is:
[x_p, y_p] = find (points);
[x, y] = meshgrid(1:size(im,1), 1:size(im,2))
r = 5;
circles = zeros(size(im));
for k = 1:length(x_p)
circles = circles + (floor((x - x_p(k)).^2 + (y - y_p(k)).^2) == r);
end
% normalize circles
circles = circles/max(max(circles));
output = im + circles;
imshow(output)
This seems more than somewhat inelegant. Is there a way to draw circles similar to the line function?
You could use the normal PLOT command with a circular marker point:
[x_p,y_p] = find(points);
imshow(im); %# Display your image
hold on; %# Add subsequent plots to the image
plot(y_p,x_p,'o'); %# NOTE: x_p and y_p are switched (see note below)!
hold off; %# Any subsequent plotting will overwrite the image!
You can also adjust these other properties of the plot marker: MarkerEdgeColor, MarkerFaceColor, MarkerSize.
If you then want to save the new image with the markers plotted on it, you can look at this answer I gave to a question about maintaining image dimensions when saving images from figures.
NOTE: When plotting image data with IMSHOW (or IMAGE, etc.), the normal interpretation of rows and columns essentially becomes flipped. Normally the first dimension of data (i.e. rows) is thought of as the data that would lie on the x-axis, and is probably why you use x_p as the first set of values returned by the FIND function. However, IMSHOW displays the first dimension of the image data along the y-axis, so the first value returned by FIND ends up being the y-coordinate value in this case.
This file by Zhenhai Wang from Matlab Central's File Exchange does the trick.
%----------------------------------------------------------------
% H=CIRCLE(CENTER,RADIUS,NOP,STYLE)
% This routine draws a circle with center defined as
% a vector CENTER, radius as a scaler RADIS. NOP is
% the number of points on the circle. As to STYLE,
% use it the same way as you use the rountine PLOT.
% Since the handle of the object is returned, you
% use routine SET to get the best result.
%
% Usage Examples,
%
% circle([1,3],3,1000,':');
% circle([2,4],2,1000,'--');
%
% Zhenhai Wang <zhenhai#ieee.org>
% Version 1.00
% December, 2002
%----------------------------------------------------------------
Funny! There are 6 answers here, none give the obvious solution: the rectangle function.
From the documentation:
Draw a circle by setting the Curvature property to [1 1]. Draw the circle so that it fills the rectangular area between the points (2,4) and (4,6). The Position property defines the smallest rectangle that contains the circle.
pos = [2 4 2 2];
rectangle('Position',pos,'Curvature',[1 1])
axis equal
So in your case:
imshow(im)
hold on
[y, x] = find(points);
for ii=1:length(x)
pos = [x(ii),y(ii)];
pos = [pos-0.5,1,1];
rectangle('position',pos,'curvature',[1 1])
end
As opposed to the accepted answer, these circles will scale with the image, you can zoom in an they will always mark the whole pixel.
Hmm I had to re-switch them in this call:
k = convhull(x,y);
figure;
imshow(image); %# Display your image
hold on; %# Add subsequent plots to the image
plot(x,y,'o'); %# NOTE: x_p and y_p are switched (see note below)!
hold off; %# Any subsequent plotting will overwrite the image!
In reply to the comments:
x and y are created using the following code:
temp_hull = stats_single_object(k).ConvexHull;
for k2 = 1:length(temp_hull)
i = i+1;
[x(i,1)] = temp_hull(k2,1);
[y(i,1)] = temp_hull(k2,2);
end;
it might be that the ConvexHull is the other way around and therefore the plot is different. Or that I made a mistake and it should be
[x(i,1)] = temp_hull(k2,2);
[y(i,1)] = temp_hull(k2,1);
However the documentation is not clear about which colum = x OR y:
Quote: "Each row of the matrix contains the x- and y-coordinates of one vertex of the polygon. "
I read this as x is the first column and y is the second colum.
In newer versions of MATLAB (I have 2013b) the Computer Vision System Toolbox contains the vision.ShapeInserter System object which can be used to draw shapes on images. Here is an example of drawing yellow circles from the documentation:
yellow = uint8([255 255 0]); %// [R G B]; class of yellow must match class of I
shapeInserter = vision.ShapeInserter('Shape','Circles','BorderColor','Custom','CustomBorderColor',yellow);
I = imread('cameraman.tif');
circles = int32([30 30 20; 80 80 25]); %// [x1 y1 radius1;x2 y2 radius2]
RGB = repmat(I,[1,1,3]); %// convert I to an RGB image
J = step(shapeInserter, RGB, circles);
imshow(J);
With MATLAB and Image Processing Toolbox R2012a or newer, you can use the viscircles function to easily overlay circles over an image. Here is an example:
% Plot 5 circles at random locations
X = rand(5,1);
Y = rand(5,1);
% Keep the radius 0.1 for all of them
R = 0.1*ones(5,1);
% Make them blue
viscircles([X,Y],R,'EdgeColor','b');
Also, check out the imfindcircles function which implements the Hough circular transform. The online documentation for both functions (links above) have examples that show how to find circles in an image and how to display the detected circles over the image.
For example:
% Read the image into the workspace and display it.
A = imread('coins.png');
imshow(A)
% Find all the circles with radius r such that 15 ≤ r ≤ 30.
[centers, radii, metric] = imfindcircles(A,[15 30]);
% Retain the five strongest circles according to the metric values.
centersStrong5 = centers(1:5,:);
radiiStrong5 = radii(1:5);
metricStrong5 = metric(1:5);
% Draw the five strongest circle perimeters.
viscircles(centersStrong5, radiiStrong5,'EdgeColor','b');
Here's the method I think you need:
[x_p, y_p] = find (points);
% convert the subscripts to indicies, but transposed into a row vector
a = sub2ind(size(im), x_p, y_p)';
% assign all the values in the image that correspond to the points to a value of zero
im([a]) = 0;
% show the new image
imshow(im)

Resources