Good way to fit a .png file on Canvas? - image

It seems to be a very common application, but I could not make it work:
def Img2Canvas(Img,Canv): # this function will put image on a canvas by stretching it
Canv.update()
H=Canv.winfo_height()
W=Canv.winfo_width()
print([W,H])
temp=ImageTk.PhotoImage(Img.resize((W,H)))
Canv.create_image(1,1,anchor=tk.NW,image=temp)
Then I called this function in main program:
cv1=tk.Canvas(root,width=200,height=200,bg='yellow')
Img2Canvas(p1.Img,cv1)
1) this does not work, The canvas is not updated, and I just got a Yellow background. It only works if I do not do temp=ImageTk.PhotoImage(Img.resize((W,H))) inside the function, but resize the image outside of function and input temp directly...
2) the W and H seems to be 204 instead of 200, so is winfo_height() always give you 4 more pixels?
3) is there a better way to display a figure file (jpg, png, etc.) in Tkinter?

You need to keep a reference to the image temp as it is a local variable which will be garbage collected after the function ends. Suggest to return temp and assign it to a variable:
def Img2Canvas(Img, Canv):
...
return temp
...
tkimg = Img2Canvas(p1.Img, cv1)
The extra pixels in the width and height is the size of highlightthickness, set it to 0 when creating the canvas:
cv1 = tk.Canvas(root, width=200, height=200, highlightthickness=0, bg='yellow')

Related

Making a long image without resizing

I need to put many images together side by side but without changing the height or width of any of them. That is to say, it will just be one image of a constant height but very long width as the image are sitting horizontally.
I've been using Python and the PIL library but what I've tried so far is producing an image that makes all the images smaller to concatenate into one long image.
Image.MAX_IMAGE_PIXELS = 100000000 # For PIL Image error when handling very large images
imgs = [ Image.open(i) for i in list_of_images ]
widths, heights = zip(*(i.size for i in imgs))
total_width = sum(widths)
max_height = max(heights)
new_im = Image.new('RGB', (total_width, max_height))
# Place first image
new_im.paste(imgs[0],(0,0))
# Iteratively append images in list horizontally
hoffset=0
for i in range(1,len(imgs),1):
hoffset=imgs[i-1].size[0]+hoffset # update offset**
new_im.paste(imgs[i],(hoffset,0))
new_im.save('row.jpg')
The result I'm getting now is one image made up of concatenated images in a horizontal row. This is what I want, except the images are being made smaller and smaller in the concatenation process. I want the end result to not make the images smaller and instead produce an image made of the input images with their original size. So the output image will just have to have a very long width.
It seems you have a bug while updating the offsets.
You should replace your iteration block with:
imgs = [Image.open(i) for i in list_of_images]
widths, heights = zip(*(i.size for i in imgs))
new_img = Image.new('RGB', (sum(widths), max(heights)))
h_offset = 0
for i, img in enumerate(imgs):
new_img.paste(img, (h_offset, 0))
h_offset += img.size[0]

imshow command does not show the same binary image with original

I read the image with:
W=double(imread('rose32.bmp'));
Then:
imshow(W,[]);
or
imshow(W);
But the shown image seems to be inverted with respect to the original image. How can I solve this problem ? Is it a MATLAB problem?
The problem is probably caused by the formatting the the imagefile!
When you use imread what it returns depends of the formatting of the image in the image file. imread returns tree values [A,map,transparency] = imread(___), where A might be hxw-matrix or a hxwx3-matrix (h and w are short for height and width) of several different possible classes (eg. double or uint8).
In the case of the hxwx3-matrix the output-variable map will be empty, and you can show the image directly using imshow(A). This is called an RGB-image.
The other possibility (called an indexed image) is the hxw-matrix. In this case map is a colormap, and you can show the image by imshow(A,map).
You can easily convert between these two types of images by ind2rgb(A,map) and rgb2ind(A).
The other thing you need to be careful with is the class of the image.
If you have an rgb-image of class uint8, then the values of image will be integers between 0 and 255, whereas rgb-images of type double have values between 0 and 1. You should never convert an image to double-class by the double-function like you do; in stead use im2double.
So to solve your problem try the following code:
[img,map] = imread('rose32.bmp');
if ~isempty(map)
img = ind2rgb(img,map);
end
img = im2double(img);
Now imshow(img) should show the image correctly. Or you can simply use the following code:
[W,map] = imread('rose32.bmp');
imshow(W,map);

How to make gif images from a set of images in matlab?

How to make '.gif' image from a set of '.jpg' images (say: I1.jpg, I2.jpg,..., I10.jpg) in matlab?
Ok here is a simple example. I got an image with a unicorn on it and remove 2 part to create 3 different images, just for the sake of creating an animated gif. Here is what it looks like:
clear
clc
%// Image source: http:\\giantbomb.com
A = rgb2gray(imread('Unicorn1.jpg'));
B = rgb2gray(imread('Unicorn2.jpg'));
C = rgb2gray(imread('Unicorn3.jpg'));
ImageCell = {A;B;C};
figure;
subplot(131)
imshow(A)
subplot(132)
imshow(B)
subplot(133)
imshow(C)
%// Just to show what the images look like (I removed spots to make sure there was an animation created):
%// Create file name.
FileName = 'UnicornAnimation.gif';
for k = 1:numel(ImageCell)
if k ==1
%// For 1st image, start the 'LoopCount'.
imwrite(ImageCell{k},FileName,'gif','LoopCount',Inf,'DelayTime',1);
else
imwrite(ImageCell{k},FileName,'gif','WriteMode','append','DelayTime',1);
end
end
As you see, its not that different from the example on the Mathworks website. Here my images are in a cell array but yours might be in a regular array or something else.That should work fine; when I open 'UnicornAnimation.gif' it is indeed a nice animation!
Hope that helps!

Writing a greyscale video using Videowriter/avifile

I am writing a function that generates a movie mimicking a particle in a fluid. The movie is coloured and I would like to generate a grayscaled movie for the start. Right now I am using avifile instead of videowriter. Any help on changing this code to get grayscale movie? Thanks in advance.
close all;
clear variables;
colormap('gray');
vidObj=avifile('movie.avi');
for i=1:N
[nx,ny]=coordinates(Lx,Ly,Nx,Ny,[x(i),-y(i)]);
[xf,yf]=ndgrid(nx,ny);
zf=zeros(size(xf))+z(i);
% generate a frame here
[E,H]=nfmie(an,bn,xf,yf,zf,rad,ns,nm,lambda,tf_flag,cc_flag);
Ecc=sqrt(real(E(:,:,1)).^2+real(E(:,:,2)).^2+real(E(:,:,3)).^2+imag(E(:,:,1)).^2+imag(E(:,:,2)).^2+imag(E(:,:,3)).^2);
clf
imagesc(nx/rad,ny/rad,Ecc);
writetif(Ecc,i);
if i==1
cl=caxis;
else
caxis(cl)
end
axis image;
axis off;
frame=getframe(gca);
cdata_size = size(frame.cdata);
data = uint8(zeros(ceil(cdata_size(1)/4)*4,ceil(cdata_size(2)/4)*4,3));
data(1:cdata_size(1),1:cdata_size(2),1:cdata_size(3)) = [frame.cdata];
frame.cdata = data;
vidObj = addframe(vidObj,frame);
end
vidObj = close(vidObj);
For your frame data, use rgb2gray to convert a colour frame into its grayscale counterpart. As such, change this line:
data(1:cdata_size(1),1:cdata_size(2),1:cdata_size(3)) = [frame.cdata];
To these two lines:
frameGray = rgb2gray(frame.cdata);
data(1:cdata_size(1),1:cdata_size(2),1:cdata_size(3)) = ...
cat(3,frameGray,frameGray,frameGray);
The first line of the new code will convert your colour frame into a single channel grayscale image. In colour, grayscale images have all of the same values for all of the channels, which is why for the second line, cat(3,frameGray,frameGray,frameGray); is being called. This stacks three copies of the grayscale image on top of each other as a 3D matrix and you can then write this frame to your file.
You need to do this stacking because when writing a frame to file using VideoWriter, the frame must be colour (a.k.a. a 3D matrix). As such, the only workaround you have if you want to write a grayscale frame to the file is to replicate the grayscale image into each of the red, green and blue channels to create its colour equivalent.
BTW, cdata_size(3) will always be 3, as getframe's cdata structure always returns a 3D matrix.
Good luck!

Matlab: How can I display several outputs in the same image?

Let's say my image is img=zeros(100,100,3), my outputs are several ellipse which i get using a created function [ret]=draw_ellipse(x,y,a,b,angle,color,img), I can display one ellipse using imshow(ret).For the moment, I'm trying to show serval ellipse in the image. But i don't know how to code it. will ‘for loop’ work or I need to hold them?
If this is related to what you were doing in your previous question, then what you need to do is to pass the result of one iteration as input to the next.
So assuming that the function [ret]=draw_ellipse(x,y,a,b,angle,color,img) you mentioned takes as input an image img and returns the same image with an ellipse drawn on it, you could do this:
%# ellipses parameters
%#x = {..}; y = {..};
%#a = {..}; b = {..};
%#angle = {..}; color = {..};
img = zeros(200,100,'uint8'); %# image to start with
for i=1:10
img = draw_ellipse(x{i},y{i}, a{i},b{i}, angle{i}, color{i}, img);
end
imshow(img)
I'm a bit unsure of what you want. You want to show several ellipse in one image, like plotting several graphs with hold on?
There is no equivalent command for images, but a simple solution is to add the ellipses into one image and show that one:
several_ellipse = ellipse1 + ellipse2 + ellipse3;
imshow(several_ellipse)
Presumably you want to pass ret as the final input to the next call to draw_ellipse.

Resources