How do you scale down an image until one side reaches it's goal dimension with Carrierwave and rmagick?
Example:
Goal dimensions: 600x400
Picture being uploaded: 700x450
I want this image to be scaled down until the height reaches 400 pixels keeping the original aspect ratio.
That would result in a image with the following dimensions: 622x400
You might take a look at resize_to_limit. From the carrierwave docs:
Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
So you could do something like this in your uploader:
process :resize_to_fill => [600, 400]
If you don't mind to crop the image, you could go for resize_to_fit instead, and use the gravity value that you desire:
From the RMagick documentation: “Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.“
Edit:
You can read the documentation for these processors for more options on resizing
For a resize_to_min implementation that would only enforce minimum dimensions for your width and height, you can take resize_to_limit as base and just modify the geometry setting to MinimumGeometry to create a custom processor:
process :resize_to_min => [600, 400]
def resize_to_min(width, height)
manipulate! do |img|
geometry = Magick::Geometry.new(width, height, 0, 0, Magick::MinimumGeometry)
new_img = img.change_geometry(geometry) do |new_width, new_height|
img.resize(new_width, new_height)
end
destroy_image(img)
new_img = yield(new_img) if block_given?
new_img
end
end
Use algebra: http://www.algebrahelp.com/lessons/proportionbasics/pg2.htm
Since 622px > 600px, you need to set the width to 600px and calculate the correct height which maintains aspect ratio:
700/450 = 600/x
(700/450)*x = 600
x = 600/(700/450)
x ~= 386
Your desired size is: 600px x 386px
This will fit within the goal dimensions, maximizing size, while maintaining aspect ratio.
Related
I'm running a script that resizes images that are too large. I've used "resize_to_fit" to reduce images to a specific pixel size depending on the longer side, but I'm wondering if it's possible to do it with this logic instead: for any image whose width x height product is greater than a set value, resize the image so that the new width and height values are as large as possible while still being under that value. In other words, I don't want to arbitrarily resize the dimensions more than necessary, and I'd want to retain aspect ratio in this conversion. This may be more of a math question than a ruby one, but in any case, this is what I've tried:
image = Magick::Image.read(image_file)[0];
dimensions = image.columns, image.rows
resolution = dimensions[0] * dimensions[1]
if resolution > 4000000
resolution_ratio = 4000000 / resolution.to_f
dimension_ratio = dimensions[0].to_f * resolution_ratio
img = img.resize_to_fit(dimension_ratio,dimension_ratio)
img.write("#{image}")
end
So let's say an image has a width of 2793px and a height of 1970px. The resolution would be 5,502,210. It thus goes through the conditional statement, and as of right now, outputs a new width of 2030 and height of 1432. The product of these two is 2,906,960—which is obviously well under 4,000,000. But there are other possible width x height combinations whose product could be much closer to 4,000,000 pixels than 2,906,960 is. Is there a way of determining that information, and then resizing it accordingly?
You need to properly calculate the ratio, which is a square root from your desired dimension divided by (row multiplied by col):
row, col = [2793, 1970]
ratio = Math.sqrt(4_000_000.0 / (row * col))
[row, col].map &ratio.method(:*)
#⇒ [
# [0] 2381.400006266842,
# [1] 1679.6842149465374
#]
[row, col].map(&ratio.method(:*)).reduce(:*)
#∞ 3999999.9999999995
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!
I need to generate new dimensions for an image to match a ratio of a given width and height ...but without increasing the size of the original.
The concept seems oh so simple yet I can't seem to join the dots.
Also, for code samples the language is PHP.
Update:
This is what I have so far:
http://codepad.org/fTdCNhQf
This is the output I need:
Example Image • (can't embed yet)
Since enlarging is not an option, your only options are cropping and extending.
Try this: let's say your image is W*H, and the desired aspect ratio of width to height is R.
Using the width and the aspect ratio, calculate the target height TH = W/R
Using the height and the aspect ratio, calculate the target width TW = H*R
Calculate area changes aH = ABS(TH-H)*W and aW = ABS(TW-W)*H
if aH is less than aW, use target width; pad or crop the image horizontally based on the sign of TH-H
Otherwise, use target height; pad or crop the image vertically based on the sigh of TW-W
Here is a quick example:
Target R: 5/6
Image: W=200, H= 300;
TH = 200/5*6 = 240
TW = 300*5/6 = 250
aH = 60*200=12000
aW = 50*300=15000
Resulting action: since aH is less than aW, crop image vertically to 240
Are you using something like ImageMagick libraries to generate an image or do you just need to generate the new dimensions based on a known ratio? Also, do you need to discover the ratio from the existing image?
This may be useful then:
http://www.zedwood.com/article/119/php-resize-an-image-with-gd
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 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...