Image scaling inside a canvas - image

Let's assume we have a container with the size of 500 x 300 (w x h).
Inside this container we have a canvas with the same size, but with a different reference system inside it, with the size of 700 x 1000.
When I put an image of 700 x 1000 in this canvas it will obviously appear distorted, because the canvas occupies the entire 500 x 300 pixels of its parent container - even if inside it is still 700 x 1000.
Now, I am trying to figure out a formula to scale the image in the interlal reference system so the image doesn't appear distorted then loaded in the canvas.
Can anybody help?

First of all, if you have w = 700, h = 1000, i.e. w_container / w_canvas = 5/7 != h_container / h_canvas = 3/10, you will not be able to load you image not being distorted and taking entire space of the container. I just can tell you how to resize your image properly.
To save the width-height ratio of your image (equals 7/10, I think this is what you mean under "distorted image") you should calculate how your image's ratio rescales after putting the image on the canvas.
canvas_ratio(7/10) * x = container_ratio(5/3), hence x = 50/21. So if your image has the ratio y, then it will become y*x = y*50/21.
So you should just resize your image before putting it on the canvas such a way that after multiplying this ratio by x it would be 7/10 (the ratio when your image looks perfect). We have an equation y*50/21 = 7/10, hence y = 147/500. That's the ratio your image should have!
For example, you can make image size 294x1000, and after putting it to the canvas it will have height of 300 pixels in your container's coordinate system and 294*(500/700) = 210 pixel width (unfortunately, not 500). Hope this will be useful information for you.
Good luck!

Related

Calculate Object Size for Different Screens

I'm writing an app in Appcelerator that will be running different mobile screens. So, there will be many screen sizes and ratios.
Let me simply ask like this; I have an image object and I want it to protect its ratio on different screens.
How can I calculate the width & height of this object for different screens?
Object Sizes in 1920 X 1080 screen is 885 X 109.
(For example) What should be the sizes of 812 X 375(iPhone X size) screen?
Thanks in advance.
You can use
Alloy.Globals.WIDTH = (OS_ANDROID) ? Ti.Platform.displayCaps.platformWidth / Ti.Platform.displayCaps.logicalDensityFactor : Ti.Platform.displayCaps.platformWidth;
Alloy.Globals.HEIGHT = (OS_ANDROID) ? Ti.Platform.displayCaps.platformHeight / Ti.Platform.displayCaps.logicalDensityFactor : Ti.Platform.displayCaps.platformHeight;
to get the proper device size.
But I'm not sure if I understand the question. You don't need to do anything special about the aspect ration of your image on different devices. You can use Ti.UI.SIZE on width or height and a fixed value on the other property to keep the aspect ratio. E.g. if you image should always be 300dp width set the height to Ti.UI.SIZE.
If you want to adjust the image width to the device screen width but keep a left/right margin you can use
image.width = Ti.UI.FILL;
image.left = 10;
image.right = 10;
imahe.height = Ti.UI.SIZE;
For other calculations you can use the Alloy.Globals.WIDTH or Alloy.Globals.WIDTH from above (put it into alloy.js) and multiply it by the image ratio to get the other side.

Scale image object to match another object's scale

I have two sets of images of different size for each set. The first set is images of 400x400 pixels with real picture objects.
The second set is 319x319, with image silhouettes of different scale than the real picture objects.
What I want to achieve, is basically to have the silhouettes replaced by the real picture objects (i.e. beaver) of the first set. So the end result will be 319x319 resolution images with real picture objects. Here is an example:
The first set images cannot simply be resized to 319x319, since the beaver will not match the silhouette. There are about 100 images with different "beaver size to beaver's silhouette size" relationships. Is there a way to automate this procedure?
So far, I've tried #cxw suggestion up to step 2. Here is the code of EllipseDirectFit I used. And here is my code to plot the images with the ellipse fits. I don't know how to proceed to steps 3-5.. I think from EllipseDirectFit function -> 2*abs(A(1)) should be the ellipsi's major axes. (NOTE: 'a1.bmp' is the real image and 'b1.bmp' is the silhouette).
In case anyone else has the same problem as me, I post the code that solved my problem. I actually followed cxw's suggestion and fitted an ellipse for both real and silhouette pictures, then resized the real picture based on the ratio of the silhouette-ellipse's major axis to the real-ellipse major axis. This made the image object match in size the silhouette image object (i.e. the beaver). Then I either cropped, or added border pixels to match the resolution I needed (i.e. 319x319).
% fetching the images
realList = getAllFiles('./real_images'); % getAllFiles => StackOverflow function
silhList = getAllFiles('./silhouettes');
for qq = 1:numel(realList)
% Name of the file to save
str = realList{qq}(15:end);
a = imread(realList{qq}); % assign real image
background_Ra = a(1,1,1); % getting the background colors
background_Ga = a(1,1,2);
background_Ba = a(1,1,3);
% finding the points (x,y) to pass to fit_ellipse
[x1,y1]=find(a(:,:,1)~=background_Ra | a(:,:,2)~=background_Ga | a(:,:,3)~=background_Ba);
% fitting an ellipse to these points
z1 = fit_ellipse(x1,y1); % Mathworks file exchange function
b = imread(silhList{qq}); % assign silhouette image
background_R2b = b(1,1,1); % getting the background colors
background_G2b = b(1,1,2);
background_B2b = b(1,1,3);
% finding the points (x,y) to pass to fit_ellipse
[x2,y2]=find(b(:,:,1)~=background_R2b & b(:,:,2)~=background_G2b & b(:,:,3)~=background_B2b);
% fitting an ellipse to these points
z2 = fit_ellipse(x2,y2);
% ratio of silhouette's ellipse major axis to real image's ellipse
% major axis
ellaxratio = z2.long_axis/z1.long_axis;
% resizing based on ellaxratio, so that the real image object size will
% now fit the silhouette's image object size
c = imresize(a,ellaxratio); c = rgb2gray(c);
bordercolor = c(end,end);
% if the resulting image is smaller, add pixels around it until they
% match with the silhouette image resolution
if size(c) < 319
while size(c) < 319
% 'addborder' is a Mathworks file exchange function
c = addborder(c(:,:,1),1, bordercolor ,'outer');
end
% if the resulting image is larger, crop pixels until they match
else size(c) > 319
while size(c) > 319
c = c(2:end-1,2:end-1);
end
end
% in a few cases, the resulting resolution is 318x318, instead of
% 319x319, so a small adjustment won't hurt.
if size(c) ~= 319
c = imresize(c,[319 319]);
end
% saving..
imwrite(c,['./good_fits/' str '.bmp'])
end
I don't have code for this, but here's how I would proceed, just off-hand. There's almost certainly a better way :) .
For each of the real image and the silhouette image:
Get the X, Y coordinates of the pixels that aren't the background. Edit Example tested in Octave:
background_R = img(1,1,1)
background_G = img(1,1,2)
background_B = img(1,1,3)
[xs,ys]=find(img(:,:,1)~=background_R | img(:,:,2)~=background_G | img(:,:,3)~=background_B)
The logical OR is because the image can differ from the background in any color component.
Fit an ellipse to the X, Y coordinate pairs you found. E.g., use this routine from File Exchange. (Actually, I suppose you could use a circle fit or any other shape fit you wanted, as long as size and position are the only differences between the non-background portions of the images.)
Now you have ellipse parameters for the real image and the silhouette image. Assuming the aspect ratios are the same, those ellipses should differ only in center and scale.
Resize the real image (imresize) based on the ratio of silhouette ellipse major axis length to real image ellipse major axis length. Now they should be the same size.
Find the centers. Using the above fit routine,
A=EllipseDirectFit(...)
% switch to Mathworld notation from http://mathworld.wolfram.com/Ellipse.html
ma=A(1); mb=A(2)/2; mc=A(3); md=A(4)/2; mf=A(5)/2; mg=A(6);
center_x = (mc*md-mb*mf)/(mb**2-ma*mc)
center_y = (ma*mf-mb*md)/(mb**2-ma*mc)
Move the real image data in a 3-d matrix so that the ellipse centers
coincide. For example,
cx_silhouette = ... (as above, for the silhouette image)
cy_silhouette = ...
cx_real = ... (as above, for the *resized* real image)
cy_real = ...
shifted = zeros(size(silhouette_image)) % where we're going to put the real image
deltax = cx_silhouette - cx_real
deltay = cy_silhouette - cy_real
% if deltax==deltay==0, you're done with this step. If not:
portion = resized_real_image(max(deltay,0):319-abs(deltay), max(deltax,0):319-abs(deltax), :); % or something like that - grab the overlapping part of the resized real image
shifted(max(deltay,0):min(deltay+319,319), max(deltax,0):min(deltax+319,319), :) = portion; % or something like that - slide the portion of the resized real image in x and y. Now _shifted_ should line up with the silhouette image.
Using the background color (or the black silhouette — same difference) as a mask, copy pixels from the resized, moved real image into the silhouette image.
Hope this helps!

zoom an image to fit a screen horizontally - algorithm

This is a general question regarding an algorithm to zoom an image to fit the width of a screen, there are some givens and some constraints. We can assume we are using Java but this question is more mathematical that language dependent.
First of all, the image loads and fits into the dimensions of the screen vertically first, not horizontally.
We can get the dimensions of the screen and the dimensions of the image with methods, but we cannot set the dimensions of either (We only have getters not setters).
imageWidth = image.getWidth(); //integer
imageHeight = image.getHeight(); //integer
screenWidth = screen.getWidth(); //integer
screenHeight = screen.getHeight(); //integer
The only method to resize the image is by setting scale (zooming essentially).
image.setScale(some float); // optionally image.setZoom(integer);
What I would like to know is how to calculate the scale (zoom) level for some l x h image so that it fits a L x H screen horizontally?
All you have to do to make the Image fill your screen is scale along the x axis:
scaling_factor = screen.getWidth()/image.getWidth()
image.setScale(zoom_factor);
The formula is very intuitive:
The image height is irrelevant. The scaling you desire would be the same for a landscape and vertical image, as long as the width of both images are the same
When the image's width increases, your scaling factor decreases
When your screen size increses, the scaling factor increases.

How to lock image dimensions in MATLAB

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;

RMagick: scale and resize image for thumbnail

I want to resize/scale an image. The originals have not the same dimensions like 300x200 or 512x600. I want to resize the image to 100x100 but DONT crop anything from the image or change ratio. Ideally the image will be first scale the long edge to 100 (aspect ratio) and then fill up the smaller edge with white.
.---------.
|- - - - -|
| IMAGE |
|- - - - -|
'---------'
I dont use Paperclip or Rails, just RMagick.
I've done it with merging the resized image with a new 100x100 image. That's for sure not the best way but it works:
img = Magick::Image.read("file.png").first
target = Magick::Image.new(100, 100) do
self.background_color = 'white'
end
img.resize_to_fit!(100, 100)
target.composite(img, Magick::CenterGravity, Magick::CopyCompositeOp).write("file-small.png)
After playing with it for a while I got Fu86's composite trick to work like so:
img = Image.read("some_file").first().resize_to_fit!(width, height)
target = Image.new(width, height) do
self.background_color = 'white'
end
target.composite(img, CenterGravity, AtopCompositeOp).write("some_new_file")
AtopCompositeOp seems to work better than CopyCompositeOp, which turned part of my background black for some reason.
image = Magick::Image.read("filename").first
resized = image.resize_to_fit(width, height) # will maintain aspect ratio, so one of the resized dimensions may be less than the specified dimensions
resized.background_color = "#FFFFFF" # without a default, background color will vary based on the border of your original image
x = (resized.columns - width) / 2 # calculate necessary translation to center image on background
y = (resized.rows - height) / 2
resized = resized.extent(width, height, x, y) # 'extent' fills out the resized image if necessary, with the background color, to match the full requested dimensions. the x and y parameters calculated in the previous step center the image on the background.
resized.write("new_filename")
Note: on heroku, which as of this posting uses imagemagick 6.5.7-8, I needed to multiply the x and y translations by -1 (and send positive numbers). Version 6.8.0-10 expects negative numbers.
It seems you want to use change_geometry...

Resources