I was given some data that I would like to convert to a pixelized image. From what I understood, the original data was plotted by using the function 'rectangle' in Matlab. However, a 2D image is now required.
This data has four columns and ~1000 rows. The first to fourth columns correspond to //
Matrice = [Left corner X, Left corner Y, Width, Height]
So for instance, the first row is [2,3,5,6], meaning that there is a rectangle with lower left corner position of (2,3), width of 5 and height of 6.
I've been trying to find a method to use this data and turn it into an image of any dimensions with any pixel size. Either there is a function out there that I've been missing, or I am missing an easy method. Everything I've tried so far is overly complicated.
This will create filled black rectangles at the specified positions. Experiment with the optional arguments of rectangle to get different visuals.
Assuming your matrix is named A:
figure
for ii = 1:size(A, 1)
rectangle('Position', A[ii, :], 'FaceColor', 'k')
hold on
end
hold off
axis equal % makes the pixels square
Related
So I have this matrix in MATLAB, 200 deep x 600 wide. It represents an image that is 2cm deep x 6cm wide. How can I plot this image so that it is locked into proper dimensions, i.e. 2cm x 6cm? If I use the image or imagesc commands it stretches it all out of shape and shows it the wrong size. Is there a way to lock it into showing an image where the x and y axes are proportional?
Second question, I need to then set this image into a 640x480 frame (20 pixel black margin on left and right, 280 pixel black margin on bottom). Is there a way to do this?
To keep aspect ratio, you can use axis equal or axis image commands.
Quoting the documentation:
axis equal sets the aspect ratio so that the data units are the same in every direction. The aspect ratio of the x-, y-, and z-axis is adjusted automatically according to the range of data units in the x, y, and z directions.
axis image is the same as axis equal except that the plot box fits tightly around the data`
For second question:
third_dimension_size=1; %# for b&w images, use 3 for rgb
framed_image=squeeze(zeros(640,480,third_dimension_size));
framed_image(20:20+600-1,140:140+200-1)= my_600_200_image;
imagesc(framed_image'); axis image;
set(gca,'DataAspectRatio',[1 1 1])
Second question:
new_image = zeros(480,640);
new_image(20:(200+20-1),20:(600+20-1)) = old_image;
As an alternative to the other answers, you might want:
set(gca, 'Units', 'centimeters', 'Position', [1 1 6 2])
Make sure you do this after plotting the image to get the other axis properties correct.
For the second question, take care with the number of colour channels:
new_image = zeros(480,640, size(old_image));
new_image(20:(200+20-1),20:(600+20-1),:) = old_image;
I'm using regionprops(img,'BoundingBox'); to generate bounding boxes around some objects in an image. The coordinates of the bounding boxes (x, y, width, height) are always by 0.5 off from Integer-values.
Why is that the case?
For me, it is causing two problems:
When using these coordinates for accessing an image array, I get the warning: Warning: Integer operands are required for colon operator when used as index. I can live with that, respectively remove it with floor or ceil, BUT ...
... when these coordinates are close to image borders, they cause
errors since the values 0.5 and 1024.5 don't match with the image
borders 1 and 1024. I get Subscripted assignment dimension mismatch. or Index exceed matrix dimensions., which is plausible.
So can someone explain to me:
Why is it doing this?
How am I supposed to work with it when using the coordinates to crop and replace image regions. I want to replace exactly what was cropped by imcrop and rounding is a bit circumstancial (simply using floor or ceil won't work, I would have to check for the image borders which is not a problem but seems a bit tedious for a rather simple task and certainly questionable whether it is supposed to be used like this...).
Below are some code snippets with which I produced the errors for a 1024x1024 image.
bb_coords = [124.5 979.5 27 45]; % example for bounding box generated by regionprops
subregion = imcrop(img, bb_coords); % works fine with imcrop
% but when I want to use these coordinates for accessing the img array,
% I generally get a warning and in this case an error.
img( bb_coords(2):(bb_coords(2)+bb_coords(4)), ...
bb_coords(1):(bb_coords(1)+bb_coords(3))) = subregion;
Functions in MATLAB that handle image display or processing treat the center of the pixel as lining up with the corresponding coordinate grid points. In other words, for a given dimension of an image, the first pixel center is at 1, the second pixel center is at 2, etc., and the area of each pixel will span +-0.5 on either side of the coordinate. You can see this when you plot an image, turn the axes display on, and zoom in around one of the corners:
img = imread('cameraman.tif'); % Load a sample image
imshow(img); % Display it
set(gca, 'Visible', 'on'); % Make the axes visible
axis([0 5 252 257]); % Zoom in on the bottom left corner
The documentation for regionprops illustrates that the 'BoundingBox' will enclose the entire pixel area, thus leading to a bounding box that appears a full pixel wider (0.5 pixels wider on each side) than the range of center coordinates:
For the 5-by-5 sample image above, the nonzero pixels cover an area that spans the top 4 rows (row coordinates of the pixel centers from 1 to 4) and right 4 columns (column coordinates of the pixel centers from 2 to 5). The bounding box (in green) therefore spans from 0.5 to 4.5 (height of 4) across the rows and 1.5 to 5.5 (width of 4) across the columns.
In short, if you want to use the bounding box values in bb_coords to generate indices into the image, you need to add 0.5 to each corner coordinate and subtract 1 from each width:
ind_coords = bb_coords + [0.5 0.5 -1 -1];
img(ind_coords(2):(ind_coords(2)+ind_coords(4)), ...
ind_coords(1):(ind_coords(1)+ind_coords(3))) = subregion;
I am trying to plot a 3D volume in MATLAB. I am using the slice command.
a(:,:,1)=[1,2; 3,4];
a(:,:,2)=[5,6; 9,8];
figure;
slice (a,0,0,1);
hold on
slice (a,0,0,2);
The figure I get has just one square (pixel). I am expecting 4 squares. How do I plot this? What am I doing wrong?
Relevant part of documentation:
slice(V,sx,sy,sz) draws slices along the x, y, z directions in the volume V at the points in the vectors sx, sy, and sz. V is an m-by-n-by-p volume array containing data values at the default location X = 1:n, Y = 1:m, Z = 1:p. Each element in the vectors sx, sy, and sz defines a slice plane in the x-, y-, or z-axis direction.
So, your command slice (a,0,0,1); is asking Matlab to produce three slices of the cube [1,2]×[1,2]×[1,2] (colored according to the values of your a array), by the following planes
x=0 plane (shown as empty square since it's outside of the cube)
y=0 plane (same story)
z=1 plane (dark blue square).
You could have avoided the extraneous x=0 and y=0 slices with slice(a,[],[],1). Also,
slice(a,[],[],[1,2]) would give you top and bottom
slice(a,[],[1,2],[]) would give two vertical sides
slice(a,[1,2],[],[]) would give two other vertical sides
Or you could just get all six at once with slice(a,[1,2],[1,2],[1,2]). If you don't want, e.g., top and bottom slices, then slice(a,[1,2],[1,2],[]).
Note that the entries of a are not coordinates, they are understood as values of a function of three variables, and are represented by colors.
Working on 2D Rectangular Nesting. Need to find the utilization percentage of the material. Assuming i have the length, breadth, left-bottom position of each rectangle. What is the best way to determine the Boundary-Cut Utilization?
Objective:- To find the AREA under the RED Line.
Sample images attached to depict what i have done and what i need.
What i have done
what i need
Another Example image of rectangles packed with allowance
If you're interested in determining the total "area" underneath the red line, one suggestion I have is if you have access to the Image Processing Toolbox, simply create a binary image where we draw all of the rectangles on the image at once, fill all of the holes, then to determine the area, just determine the total sum of all of the binary "pixels" in the image. You said you have the (x,y) positions of the bottom-left corner of each rectangle, as well as the width and height of each rectangle. To make this compatible in an image context, the y axis is usually flipped so that the top-left corner of the space is the origin instead of the bottom-left. However, this shouldn't affect our analysis as we are simply reflecting the whole 2D space downwards.
Therefore, I would start with a blank image that is the same size as the grid you are dealing with, then writing a loop that simply sets a rectangular grid of coordinates to true for each rectangle you have. After, use imfill to fill in any of the holes in the image, then calculate the total sum of the pixels to get the area. The definition of a hole in an image processing context is any black pixels that are completely surrounded by white pixels. Therefore, should we have gaps that are surrounded by white pixels, these will get filled in with white.
Therefore, assuming that we have four separate variables of x, y, width and height that are N elements long, where N is the number of rectangles you have, do something like this:
N = numel(x); %// Determine total number of rectangles
rows = 100; cols = 200; %// Define dimensions of grid here
im = false(rows, cols); %// Declare blank image
%// For each rectangle we have...
for idx = 1 : N
%// Set interior of rectangle at location all to true
im(y(idx)+1:y(idx)+height(idx), x(idx)+1:x(idx)+width(idx)) = true;
end
%// Fill in the holes
im_filled = imfill(im, 'holes');
%// Determine total area
ar = sum(im_filled(:));
The indexing in the for loop:
im(y(idx)+1:y(idx)+height(idx), x(idx)+1:x(idx)+width(idx)) = true;
Is a bit tricky to deal with. Bear in mind that I'm assuming that y accesses the rows of the image and x accesses the columns. I'm also assuming that x and y are 0-based, so the origin is at (0,0). Because we access arrays and matrices in MATLAB starting at 1, we need to offset the coordinates by 1. Now, the beginning index for the row starts from y(idx)+1. We end at y(idx) + height(idx) because we technically start at y(idx)+1 but then we need to go up to height(idx) but then we also subtract by 1 as your coordinates begin at 0. Take for example a line with the width of 20, from x = 0 to x = 19. This width is 20, but we draw from 0, up to 20-1 which is 19. Because of the indexing starting at 1 for MATLAB, and the subtraction of 1 due to the 0 indexing, the +1 and -1 cancel, which is why we are just left with y(idx) + height(idx). The same can be said with the x coordinate and the width.
Once we draw all of the rectangles in the image, we use imfill to fill up the holes, then we can sum up the total area by just unrolling the whole image into a single vector and invoking sum. This should (hopefully) get what you need.
Now, if you want to find the area without the filled in holes (I suspect this is what you actually need), then you can skip the imfill step. Simply apply the sum on the im, instead of im_filled, and so:
ar = sum(im(:));
This will sum up all of the "white" pixels in the image, which is effectively the area. I'm not sure what you're actually after, so use one or the other depending on your needs.
Boundary-Cut Area without using Image Processing Toolbox.
The Detailed question description and answer could be found here
This solution is applicable only to Rectangular parts.
Hello, I have an image as shown above. Is it possible for me to detect the center point of the cross and output the result using Matlab? Thanks.
Here you go. I'm assuming that you have the image toolbox because if you don't then you probably shouldn't be trying to do this sort of thing. However, all of these functions can be implemented with convolutions I believe. I did this process on the image you presented above and obtained the point (139,286) where 138 is the row and 268 is the column.
1.Convert the image to a binary image:
bw = bw2im(img, .25);
where img is the original image. Depending on the image you might have to adjust the second parameters (which ranges from 0 to 1) so that you only get the cross. Don't worry about the cross not being fully connected because we'll remedy that in the next step.
2.Dilate the image to join the parts. I had to do this twice because I had to set the threshold so low on the binary image conversion (some parts of your image were pretty dark). Dilation essentially just adds pixels around existing white pixels (I'll also be inverting the binary image as I send it into bwmorph because the operations are made to act on white pixels which are the ones that have a value of 1).
bw2 = bwmorph(~bw, 'dilate', 2);
The last parameter says how many times to do the dilation operation.
3.Shrink the image to a point.
bw3 = bwmorph(bw2, 'shrink',Inf);
Again, the last parameter says how many times to perform the operation. In this case I put in Inf which shrinks until there is only one pixel that is white (in other words a 1).
4.Find the pixel that is still a 1.
[i,j] = find(bw3);
Here, i is the row and j is the column of the pixel in bw3 such that bw3(i,j) is equal to 1. All the other pixels should be 0 in bw3.
There might be other ways to do this with bwmorph, but I think that this way works pretty well. You might have to adjust it depending on the picture too. I can include images of each step if desired.
I just encountered the same kind of problem, and I found other solutions that I would like to share:
Assume image file name is pict1.jpg.
1.Read input image, crop relevant part and covert to Gray-scale:
origI = imread('pict1.jpg'); %Read input image
I = origI(32:304, 83:532, :); %Crop relevant part
I = im2double(rgb2gray(I)); %Covert to Grayscale and to double (set pixel range [0, 1]).
2.Convert image to binary image in robust approach:
%Subtract from each pixel the median of its 21x21 neighbors
%Emphasize pixels that are deviated from surrounding neighbors
medD = abs(I - medfilt2(I, [21, 21], 'symmetric'));
%Set threshold to 5 sigma of medD
thresh = std2(medD(:))*5;
%Convert image to binary image using above threshold
BW = im2bw(medD, thresh);
BW Image:
3.Now I suggest two approaches for finding the center:
Find find centroid (find center of mass of the white cluster)
Find two lines using Hough transform, and find the intersection point
Both solutions return sub-pixel result.
3.1.Find cross center using regionprops (find centroid):
%Find centroid of the cross (centroid of the cluster)
s = regionprops(BW, 'centroid');
centroids = cat(1, s.Centroid);
figure;imshow(BW);
hold on, plot(centroids(:,1), centroids(:,2), 'b*', 'MarkerSize', 15), hold off
%Display cross center in original image
figure;imshow(origI), hold on, plot(82+centroids(:,1), 31+centroids(:,2), 'b*', 'MarkerSize', 15), hold off
Centroid result (BW image):
Centroid result (original image):
3.2 Find cross center by intersection of two lines (using Hough transform):
%Create the Hough transform using the binary image.
[H,T,R] = hough(BW);
%ind peaks in the Hough transform of the image.
P = houghpeaks(H,2,'threshold',ceil(0.3*max(H(:))));
x = T(P(:,2)); y = R(P(:,1));
%Find lines and plot them.
lines = houghlines(BW,T,R,P,'FillGap',5,'MinLength',7);
figure, imshow(BW), hold on
L = cell(1, length(lines));
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
% Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
%http://robotics.stanford.edu/~birch/projective/node4.html
%Find lines in homogeneous coordinates (using cross product):
L{k} = cross([xy(1,1); xy(1,2); 1], [xy(2,1); xy(2,2); 1]);
end
%https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
%Lines intersection in homogeneous coordinates (using cross product):
p = cross(L{1}, L{2});
%Convert from homogeneous coordinate to euclidean coordinate (divide by last element).
p = p./p(end);
plot(p(1), p(2), 'x', 'LineWidth', 1, 'Color', 'white', 'MarkerSize', 15)
Hough transform result:
I think that there is a far simpler way of solving this. The lines which form the cross-hair are of equal length. Therefore it in will be symmetric in all orientations. So if we do a simple line scan horizontally as well as vertically, to find the extremities of the lines forming the cross-hair. the median of these values will give the x and y co-ordinates of the center. Simple geometry.
I just love these discussions of how to find something without defining first what that something is! But, if I had to guess, I’d suggest the center of mass of the original gray scale image.
What about this;
a) convert to binary just to make the algorithm faster.
b) Perform a find on the resulting array
c) choose the element which has either lowest/highest row/column index (you would have four points to choose from then
d) now keep searching neighbours
have a global criteria for search that if search does not result in more than a few iterations, the point selected is false and choose another extreme point
e) going along the neighbouring points, you will end up at a point where you have three possible neighbours.That is you intersection
I would start by using the grayscale image map. The darkest points are on the cross, so discriminating on the highest values is a starting point. After discrimination, set all the lower points to white and leave the rest as they are. This would maximize the contrast between points on the cross and points in the image. Next up is to come up with a filter for determining the position with the highest average values. I would step through the entire image with a NxM array and take the mean value at the center point. Create a new array of these means and you should have the highest mean at the intersection. I'm curious to see how someone else may try this!