In MATLAB, how do I plot to an image and save the result without displaying it? - graphics

This question kind of starts where this question ends up. MATLAB has a powerful and flexible image display system which lets you use the imshow and plot commands to display complex images and then save the result. For example:
im = imread('image.tif');
f = figure, imshow(im, 'Border', 'tight');
rectangle('Position', [100, 100, 10, 10]);
print(f, '-r80', '-dtiff', 'image2.tif');
This works great.
The problem is that if you are doing a lot of image processing, it starts to be real drag to show every image you create - you mostly want to just save them. I know I could start directly writing to an image and then saving the result. But using plot/rectangle/imshow is so easy, so I'm hoping there is a command that can let me call plot, imshow etc, not display the results and then save what would have been displayed. Anyone know any quick solutions for this?
Alternatively, a quick way to put a spline onto a bitmap might work...

When you create the figure you set the Visibile property to Off.
f = figure('visible','off')
Which in your case would be
im = imread('image.tif');
f = figure('visible','off'), imshow(im, 'Border', 'tight');
rectangle('Position', [100, 100, 10, 10]);
print(f, '-r80', '-dtiff', 'image2.tif');
And if you want to view it again you can do
set(f,'visible','on')

The simple answer to your question is given by Bessi and Mr Fooz: set the 'Visible' setting for the figure to 'off'. Although it's very easy to use commands like IMSHOW and PRINT to generate figures, I'll summarize why I think it's not necessarily the best option:
As illustrated by Mr Fooz's answer, there are many other factors that come into play when trying to save figures as images. The type of output you get is going to be dependent on many figure and axes settings, thus increasing the likelihood that you will not get the output you want. This could be especially problematic if you have your figures set to be invisible, since you won't notice some discrepancy that could be caused by a change in a default setting for the figure or axes. In short, your output becomes highly sensitive to a number of settings that you would then have to add to your code to control your output, as Mr Fooz's example shows.
Even if you're not viewing the figures as they are made, you're still probably making MATLAB do more work than is really necessary. Graphics objects are still created, even if they are not rendered. If speed is a concern, generating images from figures doesn't seem like the ideal solution.
My suggestion is to actually modify the image data directly and save it using IMWRITE. It may not be as easy as using IMSHOW and other plotting solutions, but I think it is more efficient and gives more robust and consistent results that are not as sensitive to various plot settings. For the example you give, I believe the alternative code for creating a black rectangle would look something like this:
im = imread('image.tif');
[r,c,d] = size(im);
x0 = 100;
y0 = 100;
w = 10;
h = 10;
x = [x0:x0+w x0*ones(1,h+1) x0:x0+w (x0+w)*ones(1,h+1)];
y = [y0*ones(1,w+1) y0:y0+h (y0+h)*ones(1,w+1) y0:y0+h];
index = sub2ind([r c],y,x);
im(index) = 0;
im(index+r*c) = 0;
im(index+2*r*c) = 0;
imwrite(im,'image2.tif');

I'm expanding on Bessi's solution here a bit. I've found that it's very helpful to know how to have the image take up the whole figure and to be able to tightly control the output image size.
% prevent the figure window from appearing at all
f = figure('visible','off');
% alternative way of hiding an existing figure
set(f, 'visible','off'); % can use the GCF function instead
% If you start getting odd error messages or blank images,
% add in a DRAWNOW call. Sometimes it helps fix rendering
% bugs, especially in long-running scripts on Linux.
%drawnow;
% optional: have the axes take up the whole figure
subplot('position', [0 0 1 1]);
% show the image and rectangle
im = imread('peppers.png');
imshow(im, 'border','tight');
rectangle('Position', [100, 100, 10, 10]);
% Save the image, controlling exactly the output
% image size (in this case, making it equal to
% the input's).
[H,W,D] = size(im);
dpi = 100;
set(f, 'paperposition', [0 0 W/dpi H/dpi]);
set(f, 'papersize', [W/dpi H/dpi]);
print(f, sprintf('-r%d',dpi), '-dtiff', 'image2.tif');
If you'd like to render the figure to a matrix, type "help #avifile/addframe", then extract the subfunction called "getFrameForFigure". It's a Mathworks-supplied function that uses some (currently) undocumented ways of extracting data from figure.

Here is a completely different answer:
If you want an image file out, why not just save the image instead of the entire figure?
im = magic(10)
imwrite(im/max(im(:)),'magic.jpg')
Then prove that it worked.
imshow('magic.jpg')
This can be done for indexed and RGB also for different output formats.

You could use -noFigureWindows to disable all figures.

Related

Apply texture on a certain part of a mesh

I am using Pandas3D 1.10 (with Python 3.6) and I am trying to generate terrain on the fly.
Currently, I was able to perform this:
Now, my idea is to add textures to this terrain. I plan to add different textures for different kinds of ground and biome, but when I try to add a texture, this is added on the whole terrain.
I only want to add the texture to certain parts of the mesh, so I can combine different textures (dirt, grass, sand, etc) and make a better terrain.
From this Panda3D documentation you can see an example of how to make the terrain:
from panda3d.core import ShaderTerrainMesh, Shader, load_prc_file_data
# Required for matrix calculations
load_prc_file_data("", "gl-coordinate-system default")
# ...
terrain_node = ShaderTerrainMesh()
terrain_node.heightfield_filename = "heightfield.png" # This must be in gray scale, also you can make an image with PNMImage() with code.
terrain_node.target_triangle_width = 10.0
terrain_node.generate()
terrain_np = render.attach_new_node(terrain_node)
terrain_np.set_scale(1024, 1024, 60)
terrain_np.set_shader(Shader.load(Shader.SL_GLSL, "terrain.vert", "terrain.frag"))
In that link, there is also an example of both terrain.vert and terrain.frag.
I tried to apply this guide but it seem that doesn't work on ShaderMeshTerrain, or I think.
ts = TextureStage('ts')
myTexture = loader.loadTexture("textures/Grass.png")
terrain_np.setTexture(ts, myTexture)
terrain_np.setTexScale(ts, 10, 10)
terrain_np.setTexOffset(ts, -25, -25)
The output is the same. It doesn't matter how much I change the numbers of setTextScale and setTexOffset the output is always all with grass.
How can I only implement the texture on a certain part of the model?
Obviously, I can make the image on the fly and do all the modifications with PNMImage(), but it will be slow and difficult, and I am very sure it may be possible to do without re-made the texture each time.
EDIT
I've discovered that I can do this in order to only put a texture in a place:
ts = TextureStage('ts')
myTexture = loader.loadTexture("textures/Grass.png")
myTexture.setWrapU(Texture.WM_border_color)
myTexture.setWrapV(Texture.WM_border_color)
myTexture.setBorderColor(VBase4(0, 0, 0, 0))
terrain_np.setTexture(ts, myTexture)
The problem is that I am not able to change the location of this texture nor its size. Also, note that I don't want to reduce the scale of the texture, when I want to make a texture smaller I mean "cut" or "erase" all the parts that don't fit on the place, not reduce the overall texture size.
Sadly these commands aren't working:
myTexture.setTexScale(ts, LVecBase2(0.5, 250))
myTexture.setTexOffset(ts, LVecBase2(0.15, 0.5))

OpenCV store image in Object (OOP)

I'm programming in C++ using OpenCV in an object oriented approach. Basically I have an array of object called People[8]. For each array, I want to allocate an image to it by taking a picture using webcam. I did something like this:
for (int i=0; i<8; i++){
cvWaitKey(0); //wait for input then take picture
Mat grabbed = cam1.CamCapture();
People[i].setImage(grabbed);
imshow("picture", grabbed);
cvWaitKey(1);
}
I face 2 problems here:
1) The imshow does not display the 'latest' image captured, it display the image previously taken i.e (i-1) instead of i.
2) When I display all the images together, 8 windows appear and all of them are displaying the last image captured on the camera.
I do not have any clue what is wrong, could anyone please advice? Thank you in advance.
"all of them are displaying the last image captured on the camera."
the images you get from the capture point to driver memory. so the former image gets overwritten by the latter.
you need to store a clone() of the mat you get, like:
People[i].setImage( grabbed.clone() );
I have not worked with OpenCV for a while but I would move around cvWaitKey( 1 ), I also would not have 2 calls to it, from what I remember it is similar to glFlush(). Also I would change 1 to 10. For some reason I remember 1 not working.

Creating a graph with overlapping histograms and saving it to a single file

I am trying to write a Matlab script to analyze two specific sets of data, create histograms for them, and write them to a single file where you can see both histograms overlapped on one plot.
I created a functioning script that created the histogram for 1 set of data that basically went like this:
h1=figure;
hist(data,nbins:;
print(h1,'-dpng','hist.png)
Then I tried to simply add a second line of:
h2=figure;
and changed the print function to include h2. That obviously didn't work. I found that I couldn't have both an h1 and an h2 with the print function.
After searching the internet and looking for ways to get around this I decided to try to use saveas instead. I got to the following:
h=findobj(gca,'Type','patch');
hist(data1,nbins);
hold on;
hist(data2,nbins);
set(h(1),'FaceColor','r','EdgeColor','k');
set(h(2),'FaceColor','b','EdgeColor','k');
saveas(h,'-dpng','hist.png')
But this won't quite work either. I haven't found anything on the Mathworks website that helps me with this problem, and I haven't found anything on any other site either. I am using a Linux computer connecting to a different server via SSH so the only way that I can view plots that I make is by saving them to a file and then opening them. Please let me know if you have any suggestions to accomplish my task as outlined in my first paragraph. Thank you.
One way is to use different axes for different histogram. You can use SUBPLOT for this:
subplot(2,1,1)
hist(data1,nbins);
subplot(2,1,2)
hist(data2,nbins);
Another way is to find a common bins (x) and return the hist output to vectors. Then use BAR function for the plot.
nbins = 20;
x = linspace(min([data1(:);data2(:)]),max([data1(:);data2(:)]),nbins);
h1 = hist(data1, x);
h2 = hist(data2, x);
hb = bar(x,[h1(:),h2(:)],'hist');
% change colors and set x limits
set(hb(1),'FaceColor','r','EdgeColor','k');
set(hb(2),'FaceColor','b','EdgeColor','k');
gap = x(2)-x(1);
xlim([x(1)-gap x(end)+gap])

Matlab GA plot in nodisplay mode

I'm using the matlab GA and the plot option 'gaplotrange'. But I'm running matlab on a Linux server through a terminal. So when I try to save the gaplot, I either keep getting an empty image (if I use saveas) or an error (if I use print, I get a message saying it is not supported in the current platform).
Is there any other way I could save the plot in the nodisplay mode?
Here is a piece of my code
opts = gaoptimset('PopulationSize', 256, 'EliteCount',1,'CrossoverFraction',0.8, ...
'Generation', 3, 'PenaltyFactor',80,'SelectionFcn',{#selectiontournament,4}, ...
'CrossoverFcn', #crossoverscattered ,'Vectorized','off', 'UseParallel','always',...
'OutputFcns',#pop_output,'MutationFcn',{#mutationuniform,0.002},'StallGenLimit',3,...
'TolFun', 1e-4,'PlotFcns',#gaplotbestf);
f = figure('vis','off');
[x,fval, exitflag, output, population, scores] = ga(#plate_fitness,16,[],[],[],[],vlb,vub,[],opts);
hgsave(f,'matlabga_range','png');
Matlab has a solution for this one posted here
hgsave('filename')
hgsave(h,'filename')
I don't have much experience with the genetic algorithms toolbox, but a quick glance at the docs shows this
To display a plot when calling ga from the command line, set the PlotFcns field of options to be a function handle to the plot function. For example, to display the best fitness plot, set options as follows
options = gaoptimset('PlotFcns', #gaplotbestf);
So if you're not passing 'PlotFcns' with a handle to the plotting function in, it looks like it won't generate the plot based on command line interaction. Add it in and see if it fixes your problem, more details here
Update:
Turns out the problem was that the ga method's plot was creating its own figure, so the save needed to be on the gcf, see the discussion below for more details.

best option for background subtraction using emgucv?

can you suggest a good option for background subtraction using emgucv? my project is real time pedestrian detection.
Not sure if you still need this, but...in EmguCV, if you have 2 images of say type Image<Bgr, Byte> or any other type, called img1 and img2, doing img1 - img2 does work! There is a function called AbsDiff as well, I think it works like this: img1.AbsDiff(img2), you could look into that.
If you already have the picture of the background (img1) and you have the current frame (img2), you could do the above.
This is quite possible take a look at the "MotionDetection" example provided with EMGU this should get you started.
Effectively the code that removes the foreground is effectively named "_forgroundDetector" it is the "_motionHistory" that presents stores what movement has occurred.
The example has everything you need if you have trouble running it let me know,
Cheer
Chris
See:Removing background from _capture.QueryFrame()

Resources