Generate a checkerboard pattern of a particular size when printed - matlab

I was trying to generate a checkerboard pattern in MATLAB in such a way that when it is printed on a US letter sized paper, the width of each box will be 30 mm.
I tried to change various properties of gcf such as 'Units' and 'PaperSize' etc, but the PDF doesn't print as desired.
I came across several other online methods to generate what I need, but I would like to generate one in MATLAB. Any ideas? Below is sample code.
width = 30;
I = checkerboard(width,5,4);
I = I>0.5;
imshow(I);
saveas(gcf,'checkerboard.pdf')

Matlab can only generate the width based on pixel since Matlab doesn't know the physical dimensions of your screen.
One way is to find the Pixel Per Inch (PPI) of your screen. Here is a website to calculate the PPI by screen dimension and resolution. Then we can calculate the width (the pixel for 30mm) = PPI * 1.1811. (1.1811 inch is equal to 30 mm )
Example, for a screen of PPI = 220 (15.4 in, 2880 * 1800), width=220*1.1811=260.

There are figure properties that start with 'Paper' that specify the size and location of the figure on paper when printed (to PDF or to printer). Here is a list of all figure properties. You'll find these properties under the heading "Printing and Exporting" (see the table on contents on the left pane).
We can use these properties as follows. Note that we don't care about the size of the figure on the screen, so we leave it to the default size. What we do is set the 'PaperPosition' property so that the figure has a certain size when printed. We then create a drawing of a checkerboard pattern that fills the figure. The drawing will thus have the size of the figure itself when printed.
Note how we set the 'PaperSize' property in inches (8.5" x 11" is US Letter), but the 'PaperPosition' property in centimeters (so that we can easily figure out the 3x3 cm squares to draw).
The drawing itself could probably be simplified, but this works. It is much more efficient to draw the pattern using patches (i.e. create a vector drawing) than using an image (i.e. the checkerboard function in OP) when printing to PDF. The output is prettier and the file is smaller. But the same could be done displaying an image.
% Grid size (number of squares)
m = 6; n = 8;
% Square size (in centimeters)
side = 3;
% Create figure and set sizes for printing
figh = figure;
set(figh,'PaperUnits','inches','PaperSize',[8.5 11]);
set(figh,'PaperUnits','centimeters','PaperPosition',[1,1,side*m,side*n]);
% Create axes that fill the figure
axh = axes('Parent',figh,'Units','normalized','Position',[0,0,1,1]);
set(axh,'Color',[1,1,1])
set(axh.XAxis,'Visible','off');
set(axh.YAxis,'Visible','off');
% Draw rectangles
set(axh,'XLim',[0,m],'YLim',[0,n]);
for ii=0:m
for jj=0:n
if mod(ii+jj,2)==0
rectangle('Position',[ii,jj,1,1],'FaceColor',[0,0,0],'LineStyle','none');
end
end
end
% Print to PDF
print(figh,'-dpdf','checkerboard.pdf')

Related

How can I detect a rectangle with a certain dimensions in this picture, and compare the rectangle dimensions with predetermined dimensions in Matlab?

This is a real image of a battery. I want to detect the smallest rectangular which contains the full battery. Why do I want this? Because I want to know the size of this rectangular so I can compare this size with predetermined dimensions. After comparing, the result has to be if this battery is an AA-battery for example. Thanks.
You can apply a bit of pre-processing to your image (threshold on a particular channel, here the green channel) and a median filter to remove unwanted background signal.
Then it's a matter of using regionprops to identify objects in the image. The battery is the object with the largest area, so you can use the appropriate index from the structure returned by regionprops to get the size of the enclosing bounding box.
clear
clc
%/ Read and pre-process the image to clear unwanted signal
Im = imread('Battery.jpg');
ImBW = im2bw(Im(:,:,2),.25);
ImBW = medfilt2(ImBW,[7 7]);
%// Detect objects in cleaned image
S = regionprops(ImBW,'BoundingBox','Area');
%// Identify battery as largest object
[MaxArea,MaxIndex] = max(vertcat(S.Area));
imshow(Im,'InitialMagnification',20)
%// Display results and message
hold on
rectangle('Position',S(MaxIndex).BoundingBox,'LineWidth',2,'EdgeColor','y')
Length = S(MaxIndex).BoundingBox(3);
Height = S(MaxIndex).BoundingBox(4);
message = sprintf('The height is %0.2f pixels \nand length is %0.2f pixels',Height,Length);
h = msgbox(message);
Cropped output:
Then you simply need to convert the pixel values into real units. I'll let that part up to you.
Have fun!
No need to pay for Matlab, do it simply and for free at the commandline with ImageMagick which is installed on most Linux distros and availble for OSX and Windows.
convert battery.jpg -fuzz 50% -format "%#" info:
1474x406+653+986
That tells us that, if we trimmed the background off, the remaining image (i.e. your battery) would be 1474 pixels wide and 406 pixels tall and located at coordinates 653,986 with respect to the top-left corner of the image.
Or this will actually extract it:
convert battery.jpg -fuzz 50% -trim result.jpg

How to determine Boundary Cut Utilization MATLAB?

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.

Subpixel edge detection for almost vertical edges

I want to detect edges (with sub-pixel accuracy) in images like the one displayed:
The resolution would be around 600 X 1000.
I came across a comment by Mark Ransom here, which mentions about edge detection algorithms for vertical edges. I haven't come across any yet. Will it be useful in my case (since the edge isn't strictly a straight line)? It will always be a vertical edge though. I want it to be accurate till 1/100th of a pixel at least. I also want to have access to these sub-pixel co-ordinate values.
I have tried "Accurate subpixel edge location" by Agustin Trujillo-Pino. But this does not give me a continuous edge.
Are there any other algorithms available? I will be using MATLAB for this.
I have attached another similar image which the algorithm has to work on:
Any inputs will be appreciated.
Thank you.
Edit:
I was wondering if I could do this:
Apply Canny / Sobel in MATLAB and get the edges of this image (note that it won't be a continuous line). Then, somehow interpolate this Sobel edges and get the co-ordinates in subpixel. Is it possible?
A simple approach would be to project your image vertically and fit the projected profile with an appropriate function.
Here is a try, with an atan shape:
% Load image
Img = double(imread('bQsu5.png'));
% Project
x = 1:size(Img,2);
y = mean(Img,1);
% Fit
f = fit(x', y', 'a+b*atan((x0-x)/w)', 'Startpoint', [150 50 10 150])
% Display
figure
hold on
plot(x, y);
plot(f);
legend('Projected profile', 'atan fit');
And the result:
I get x_0 = 149.6 pix for your first image.
However, I doubt you will be able to achieve a subpixel accuracy of 1/100th of pixel with those images, for several reasons:
As you can see on the profile, your whites are saturated (grey levels at 255). As you cut the real atan profile, the fit is biased. If you have control over the experiments, I suggest you do it again again with a smaller exposure time for instance.
There are not so many points on the transition, so there is not so many information on where the transition is. Typically, your resolution will be the square root of the width of the atan (or whatever shape you prefer). In you case this limits the subpixel resolution at 1/5th of a pixel, at best.
Finally, your edges are not stricly vertical, they are slightly titled. If you choose to use this projection method, to increase the accuracy you should look for a way to correct this tilt before projecting. This won't increase your accuracy by several orders of magnitude, though.
Best,
There is a problem with your image. At pixel level, it seems like there are four interlaced subimages (odd and even rows and columns). Look at this zoomed area close to the edge.
In order to avoid this artifact, I just have taken the even rows and columns of your image, and compute subpixel edges. And finally, I look for the best fitting straight line, using the function clsq whose code is in this page:
%load image
url='http://i.stack.imgur.com/bQsu5.png';
image = imread(url);
imageEvenEven = image(1:2:end,1:2:end);
imshow(imageEvenEven, 'InitialMagnification', 'fit');
% subpixel detection
threshold = 25;
edges = subpixelEdges(imageEvenEven, threshold);
visEdges(edges);
% compute fit line
A = [ones(size(edges.x)) edges.x edges.y];
[c n] = clsq(A,2);
y = [1,200];
x = -(n(2)*y+c) / n(1);
hold on;
plot(x,y,'g');
When executing this code, you can see the green line that best aproximate all the edge points. The line is given by the equation c + n(1)*x + n(2)*y = 0
Take into account that this image has been scaled by 1/2 when taking only even rows and columns, so the right coordinates must be scaled.
Besides, you can try with the other tree subimages (imageEvenOdd, imageOddEven and imageOddOdd) and combine the four straigh lines to obtain the best solution.

Making a 2D Matlab figure that contains multiple images which start at a given point (x,y)

Problem
I am trying to make a 2D figure in Matlab which consists of multiple images and a graph with plot data (which I could eventually convert into an image too). For these images and graph, I need to be able to specify where they are located in my cartesion coordinate system.
For my specific case, it is sufficient to be able to "tell" Matlab where the left-bottom corner of the image is.
So for the example above. I would need some "trick" to let "bird1.jpg" start at position (a,b), "bird2.jpg" at position (c,d) and my plot at position (e,f) in one Matlab figure.
Solution to problem
Thanks to chappjc I was able to find a solution for my problem. Here is the code I used such that other people can use it in the future too.
figure_color = [.741 .717 .42];
axe_color = [1 1 1];
Screen.fig = figure('units','pixels',...
'name','Parallel projection',...
'menubar','none',...
'numbertitle','off',...
'position',[100 100 650 720],...
'color',figure_color,...
'busyaction','cancel',...
'renderer','opengl');
Screen.axes = axes('units','pix',...
'position',[420 460 200 200],... % (420,460) is the position of the first image
'ycolor',axe_color,...
'xcolor',axe_color,...
'color',axe_color,...
'xtick',[],'ytick',[],...
'xlim',[-.1 7.1],...
'ylim',[-.1 7.1],...
'visible','On');
Screen.img = imshow(phantom);
Screen.axes2 = axes('units','pix',...
'position',[0 0 200 200],... % (0,0) is the position of the second image
'ycolor',axe_color,...
'xcolor',axe_color,...
'color',axe_color,...
'xtick',[],'ytick',[],...
'xlim',[-.1 7.1],...
'ylim',[-.1 7.1],...
'visible','On');
Screen.img2 = imshow(phantom);
Basically what I do is first creating a (big) figure, and then create a first axe at a certain position in this big picture, and make it the default axe. In this axe I display my first image (made with the phantom function). After that I make a new axe at a another position and make it again the default axe. After I have done that, I place an image there too (the same picture, but you can also use another one if you want). You can also use handles which is the more clean method, as chappjc describes.
Positioning axes in a figure
One approach would be to manipulate the Position property of multiple axes in a figure. To make multiple axes in a figure:
hf = figure;
ha0 = axes('parent',hf,'Position',[x0 y0 w0 h0]);
ha1 = axes('parent',hf,'Position',[x1 y1 w1 h1]);
Then display your images and plots into the axes by specifying the handle (i.e. ha0 or ha1). For example: image(img0,'Parent',ha0) or imshow(img1,'parent',ha1).
Single Large Image
Another approach is to make a single large image and simply display it with image/imshow/etc.
First for the plots, you can use getframe followed by frame2im to get an image in a matrix format.
Next, decide what goes into your combined image and compute the largest box required to circumscribe the images (using their origins and sizes find the largest x and y coordinate), which includes the origin presumably. Use this info to make a blank image (e.g. img = zeros(h,w,3) for and RGB image).

When saving a figure as eps file, Matlab cuts off colormap labels

I have a figure generated using contourf with a colorbar. Most of my plots are fine, but when the values on the colorbar are of the order 10^{-3}, either the numbers 0.005 etc are written by the colorbar, or x10^{-3} is written at the top.
In both cases, part of the label gets cut off - either the 3 in x10^{-3} or half of the 5 in 0.005.
I can fix this using
set(gca, 'ActivePositionProperty', 'OuterPosition')
for the figure onscreen, but I need to save it in eps format. When I do this, the 3 (or 5) is cut off again!
I can also fix this if I manually pull the bottom right corner of the figure window to make it larger. But this changes the sizes of the axis labels etc in comparison to the plot itself so that they're different to all my other figures, i.e. the figures that I don't resize.
Any suggestions?
Matlab uses two sizes for figures: screen size (Position figure property) and the PaperSize. The former is used for displaying on screen, and the latter for printing or exporting to image formats other than .fig. I suspect this is the source of your problem.
Here is what you can try:
size = get(gcf,'Position');
size = size(3:4); % the last two elements are width and height of the figure
set(gcf,'PaperUnit','points'); % unit for the property PaperSize
set(gcf,'PaperSize',size);
This sets the size of the "paper" to export to .eps to the size of the figure displayed on screen.
If this doesn't work, you can try to play a bit with PaperSize or other "paper" related properties. The Figure Properties documentation page gives more info about properties.
Hope this helps!
The former suggestion is partly correct. Here is what i did:
set both, figure and paper units, to the same measure (figure has pixels, not points!)
set(gcf,'Units','points')
set(gcf,'PaperUnits','points')
do the same as suggested before:
size = get(gcf,'Position');
size = size(3:4);
set(gcf,'PaperSize',size)
the thing now is, that it might be shifted off the paper, as in my case, so put it back on
set(gcf,'PaperPosition',[0,0,size(1),size(2)])
I am not sure about the offset of [0,0], but what is a single point cut off :)
Try this to save your file to filename.eps:
set(gcf,'Units','points')
set(gcf,'PaperUnits','points')
size = get(gcf,'Position');
size = size(3:4);
set(gcf,'PaperSize',size)
set(gcf,'PaperPosition',[0,0,size(1),size(2)])
print(gcf,'filename','-depsc','-loose'); % Save figure as .eps file