How to fit images within fixed aspect ratio without resizing or cropping with ImageMagick? - image

My photography website is connected to print services, but they only offer a few standard aspect ratios (2:3, 4:5, 1:1, etc.). Many of my photos use other aspect ratios, and are not offered as prints at all as a result.
To fix this, I'd like to use ImageMagick CLI or another tool to put the images on a canvas with a standard aspect ratio, say, 4:5. There must be no resampling or cropping of the source image at any stage, only the outer dimensions (canvas) may grow.
The concept I've come up with is:
Take a source image with non-standard aspect ratio, and expand its canvas by 20% in all directions with white. This must be in relative terms due to varying source image sizes, as pixel dimensions would require resampling of the image.
Set the resulting matted image inside another white canvas in 4:5 aspect ratio. Either vertical or horizontal sides would be cropped in most cases, but the crop would only affect the 20% white border, not the source image.
The output should be images of varying pixel dimensions, in a fixed 4:5 aspect ratio, with white borders around all four edges of varying thickness. I've created a sample page with before and after views on my website.
Due to wildly varying aspect ratios, I would have to run all my photos multiple times through the script with varying destination aspect ratios, and pick and choose the most balanced aspect ratio for each. Tedious, but I don't think there's a way to automate that.
Any idea how to accomplish this? Or better suggestions?
I'm using 6.x of IM in either Windows or Linux, not on a website.

I tend to work in php and am off to bed now but here is an example using php and version 7. As you can see in version 7 you can have some of the calculations within the command. On version 6 it will have to be a separate line saved into a variable and then the variable will be used in the command.
Only tested quickly to see if it worked and I may have the landscape/portrate logic the wrong way around. But it should give you an idea how it could work.
<?php
// Setup the image to use
$image = '_MG_4949.jpg';
// Get the dimensions of the image into an array
$size = getimagesize("$image");
// Aspect array
$aspect = array(.87, 1.45);
// If landscape original image do this
If ($size[0] > $size[1]) {
foreach ( $aspect as $value ) {
exec("magick $image -background white -gravity center -extent \"%[fx:w*1.2]\"x\"%[fx:w*$value]\" $value.jpg");
}
}
// If portrate image do this
else {
foreach ( $aspect as $value ) {
exec("magick $image -background white -gravity center -extent \"%[fx:h*$value]\"x\"%[fx:h*1.2]\" $value.jpg");
}
}
?>
EDIT the above code should be OK now
Here is a php version for V6 ( no php getimagesize function this time ) and both versions you should be able to convert to bash or batch files.
// Setup the image to use
$image = '_MG_6790.jpg';
// Get the dimensions of the image into an array
$height = exec("identify $image -ping -format %[fx:h] info:");
$width = exec("identify $image -ping -format %[fx:w] info:");
// Aspect array
$aspect = array(.87, 1.45);
// If landscape original image do this
If ($width > $height) {
foreach ( $aspect as $value ) {
$newWidth = $width*1.2;
$newHeight = $height*$value;
exec("convert $image -background white -gravity center -extent {$newWidth}x{$newHeight} $value.jpg");
}
}
// If portrate image do this
else {
foreach ( $aspect as $value ) {
$newWidth = $width*$value;
$newHeight = $height*1.2;
exec("convert $image -background white -gravity center -extent {$newWidth}x{$newHeight} $value.jpg");
}
}

Related

powershell image transformation

I want to perform image transformation in powershell. Basically I want to insert a circle that contains different ratios of red/blue/green and yellow (this varies from picture to picture) onto another picture.
Right now I've stubmeld upon PSImageTools (http://psimagetools.start-automating.com/), but as far as I can tell, they only allow me to overlay one picture on to another, but since the ratios of the 4 colours vary, I have to dynamically create a circle that can be mapped onto the existing picture.
How can I perform the hardcore pixel that I require, not just pasting 2 images together, but defining the colour of the single pixel in powershell?
The following makes edits to an image:
$imageOld = "C:\My\File.jpg"
$imagenew = "C:\My\File2.jpg"
# Load the System.Windows.Forms library
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
$image = [System.Drawing.Image]::FromFile($imageOld)
$graphics = [System.Drawing.Graphics]::FromImage($image)
# 50% transparent white
$color = [System.Drawing.Color]::FromArgb(128, 255, 255, 255)
$brush = New-Object System.Drawing.SolidBrush($color)
# Draw a 500px circle located at (300, 300)
$graphics.FillEllipse($brush, 300, 300, 500, 500)
$image.Save($imageNew)

Image::Magick (perlmagick) resizing aspect ratio and quality issues (different than convert command line utility)

I am attempting to do some bulk resizing operations of images using ImageMagick and perlmagick (Image::Magick). All of the images I have as sources are large images and I want to resize them down to various intervals or either height or width. I want to always preserve the aspect ratio.
Given an example image with dimensions of 3840 pixels × 2160 pixels (3840x2160) I want to create the following resized images:
?x1000
?x500
?x100
1600x?
1200x?
800x?
400x?
I can do this very simply using the convert command line utility with the following commands (in order):
convert input_filename.jpg -resize x1000 output_wx1000.jpg
convert input_filename.jpg -resize x500 output_wx500.jpg
convert input_filename.jpg -resize x100 output_wx100.jpg
convert input_filename.jpg -resize 1600 output_1600xh.jpg
convert input_filename.jpg -resize 1200 output_1200xh.jpg
convert input_filename.jpg -resize 800 output_800xh.jpg
convert input_filename.jpg -resize 400 output_400xh.jpg
Since I am attempting to perform these operations in bulk in conjunction with other operations I am attempting to perform these same operations in perl using Image::Magick. I have tried several different methods with the following results:
#METHOD 1
my $image = Image::Magick->new();
$image->Read($input_filename);
$image->Resize(
($width ? ('width' => $width) : ()),
($height ? ('height' => $height) : ()),
);
$image->Write(filename => $output_filename);
This results in images that do not maintain aspect ratio. For example, if a height of 100 is supplied, the output image will be the original width by 100 (3840x100). A comparable effect is had when supplying a width -- the height is maintained, but the aspect ratio is not.
#METHOD 2
my $image = Image::Magick->new();
$image->Read($input_filename);
die "Only one dimension can be supplied" if $width && $height;
$image->Resize(geometry => $width) if $width;
$image->Resize(geometry => "x$height") if $height;
$image->Write(filename => $output_filename);
This results in images that maintain aspect ratio, and if the geometry operation is based on height, the output is exactly what is intended. However, if a width is supplied the output is terribly blurry.
#METHOD 3
`convert "$input_filename" -resize $width "$output_filename"` if $width;
`convert "$input_filename" -resize x$height "$output_filename"` if $height;
This results in images that are all correct, but forks outside of the perl process leading to efficiency issues.
Is there a better way in perl to make this resize operation produce the same results as the command-line convert utility?
My command line utility reports version 6.7.9-10, and Image::Magick reports version 6.79.
Your method #2 is on the right track. To preserve aspect ratio, supply the width and height via the geometry keyword. Your procedure can be made more general by performing the resize in one call instead of two:
$image->Resize(geometry => "${width}x${height}");
This ensures that Resize will only be called once, even if you supply both $width and $height. Just make sure that if either value is not supplied, you set it to the empty string. If you supplied both a width and height to your procedure in method #2, that could have been the cause of the blurriness you saw.
Another possible source of blurriness is the filter used by the resize operator. The best filter to use for a given operation depends on both the color characteristics of the image and the relationship between the original dimensions and the target dimensions. I recommend reading through http://www.imagemagick.org/script/command-line-options.php#filter for information about that. In PerlMagick, you can specify the filter for Resize to use via the filter keyword.
That said, I did not find particular problems with blurriness with images that I tried, so if the problem persists, a test image would be most helpful.
I might be a little late to this party, but as I had a very similar goal - resizing an image and maintaining a balance between image quality and the amount of disc space it takes up - I came up with the following code. I started out with OPs code and followed this very interesting article: https://www.smashingmagazine.com/2015/06/efficient-image-resizing-with-imagemagick/
This is the result:
sub optimize_image_size
{
my $imagePath = shift();
my $height = shift(); #720
my $width = shift(); #1080
my $image = Image::Magick->new();
$image->Read($imagePath);
die "Only one dimension can be supplied" if $width && $height;
$image->Thumbnail(geometry => "$width",filter=>'Triangle') if $width;
$image->Thumbnail(geometry => "x$height",filter=>'Triangle') if $height;
$image->Colorspace(colorspace=>'sRGB');
$image->Posterize(levels=>136, dither=>'false');
$image->UnsharpMask(radius=>0.25, sigma=>0.25, threshold=>0.065, gain=>8);
$image->Write(filename => $imagePath
, quality=>'82'
, interlace=>'None'
);
}
At least for me it produces very satisfactory size reduction (my 6MB sample images were reduced to about 90Kb), while keeping a quality similar to Photoshops "for web" settings and of course maintaining aspect ratio no matter if you provide width or height.
Too late for OP but maybe it helps other people.

how to resize an image to a fit within max width and max height?

I have a CI image upload form, and I want to make sure the images I upload will fit in a given rectangle. If they are too big - I want to downsize them. If they fit within that rectangle - I don't need resizing.
I know CI supports max-height and max-width (as a limitation on how big the uploaded images can be), and it supports resize, but I couldn't find how to resize the image to a set max-height and max-width (while maintining the ratio).
Well, it was easy enough, as expected from CI (-:
When using $this->image_lib->resize(), you set the desired width & height. If you also set maintain_ratio to true, the new image will be resized to the closest possible values of your set width & height, while preserving the original aspect ratio.
So this is the code I used, after uploading the image:
$file_data = $this->upload->data();
$max_height = 115;
$max_width = 225;
if ($file_data['image_width']>$max_width || $file_data['image_height']>$max_height)
{
$configResize = array(
'source_image' => $file_data['full_path'],
'width' => $max_width,
'height' => $max_height,
'maintain_ratio' => TRUE
);
$this->load->library('image_lib',$configResize);
$this->image_lib->resize())
}
You need to calculate the height and width you would like the resized image to be. For example:
Suppose your input image is 1200x1000 and you want your resized image to be 300x200.
Find the size ratios 1200/1000=1.2 and 300/200=1.5.
If the original ratio is larger than the targeted resized size ratio you want to match the width, else you want to math the height. In this case we want to match the height.
Find the scale factor to change the image to the target size. Scale factor is 200/1000=0.2.
Use the scale factor to find the new size 1200x1000 scaled by 0.2 (1200*0.2 and 1000*0.2) = 240x200.
Resize the image to 240x200 as it is your best fit for the 300x200 box.
Take from the website provided by Silviu G http://ellislab.com/codeigniter/user-guide/libraries/image_lib.html
$this->load->library('image_lib');
$config['image_library'] = 'gd2';
$config['source_image'] = 'originalImage.jpg';
$config['create_thumb'] = TRUE;
$config['width'] = 240;
$config['height'] = 200;
$this->load->library('image_lib', $config);
$this->image_lib->resize();
Perhaps a better way to handle this is through CSS (in conjunction with the CI image manipulation library for resizing):
.media img{
max-width:100px;
max-height:100px;
}
Use the Image Manipulation Class form CI

Cut circle out of image with RMagick

I want to cut a circle out of an image using rmagick.
Here's an example of what I'd like to be able to accomplish:
-->
It seems like I want to use http://studio.imagemagick.org/RMagick/doc/draw.html#circle to cut a circle, and then clip_path to mask it, but the docs aren't very clear. Would anyone be able to point me in the right direction?
require 'rmagick'
im = Magick::Image.read('walter.jpg').first
circle = Magick::Image.new 200, 200
gc = Magick::Draw.new
gc.fill 'black'
gc.circle 100, 100, 100, 1
gc.draw circle
mask = circle.blur_image(0,1).negate
mask.matte = false
im.matte = true
im.composite!(mask, Magick::CenterGravity, Magick::CopyOpacityCompositeOp)
im.write 'walter_circle.png'
This is how I would do it with Imagemagick and php:
// Canvas the same size as the final image
exec("convert -size 800x533 xc:white white.jpg");
// The mask
exec("convert -size 800x533 xc:none -draw \"fill black circle 400,265 400,50\" write_mask.png");
// Cut the whole out of the canvas
exec("composite -compose Dst_Out write_mask.png white.jpg -matte step.png");
// Put the canvas over the image and trim off excess white background
exec("convert IMG_5745.jpg step.png -composite -trim final.jpg");
You should be able to follow the process?
Cleanup tempory images afterwards - I tend to save the tempory images in a .miff format and then write a loop to delete all .miff images afterwards. Alternativly just leave them and if you use the same name for the tempory images they will be overwritten every time the code is run.

Magento resize() image quality: dirty white background

I have a client who is seriously unhappy with the way their product thumbnails are rendering on Magento.
The dodgy appearance is noticeable on two accounts:
there is a dirty white background which has very light grey horizontal lines
and secondly there is ever so slight color loss (loses contrast and saturation).
I have removed ALL compression, set ALL quality to 100%, flushed image cache, experimented, broken, and fixed it all dozens of times, and nothing seems to work.
This version of Magento is ver. 1.4.2.0
Is anyone out here experiencing the same problems, and if so have you managed to fix it?
The problem has to do with the php function imagecopyresampled in the resize function inside lib/Varien/Image/Adapter/Gd2.php, there are some rounding issues that occur to make a smoothly resized image.
My solution is to simply change any very light grey pixels in the image to pure white after the image has been resized. To do so first copy lib/Varien/Image/Adapter/Gd2.php to app/code/local/Varien/Image/Adapter/Gd2.php
Next find the following code inside the resize function (around line 312)
// resample source image and copy it into new frame
imagecopyresampled(
$newImage,
$this->_imageHandler,
$dstX, $dstY,
$srcX, $srcY,
$dstWidth, $dstHeight,
$this->_imageSrcWidth, $this->_imageSrcHeight
);
Then add the following code underneath:
// Clean noise on white background images
if ($isTrueColor) {
$colorWhite = imagecolorallocate($newImage,255,255,255);
$processHeight = $dstHeight+$dstY;
$processWidth = $dstWidth+$dstX;
//Travel y axis
for($y=$dstY; $y<($processHeight); ++$y){
// Travel x axis
for($x=$dstX; $x<($processWidth); ++$x){
// Change pixel color
$colorat=imagecolorat($newImage, $x, $y);
$r = ($colorat >> 16) & 0xFF;
$g = ($colorat >> 8) & 0xFF;
$b = $colorat & 0xFF;
if(($r==253 && $g == 253 && $b ==253) || ($r==254 && $g == 254 && $b ==254)) {
imagesetpixel($newImage, $x, $y, $colorWhite);
}
}
}
}
Flush the images cache from the Cache management in Magento, and you should have nicer images for the new displays. Few things to note when implementing this, there is a small performance hit the first time you generate the images again, and images with shadows may have sharper edges as the very light greys have been removed.
try following example
echo Mage::helper('catalog/image')->init($product, 'small_image')->resize(180, 210)->setQuality(50);
You can put your own Gd2.php file in local (app/code/local/Varien/Image/Adapter/Gd2.php) and hard-wire the quality to 100%.
Quality is working for me so I have not done that.
You can also put an image convolution in there to sharpen your images, in that way you get the blur of a resize compensated for with a sharpen, e.g. put the following just inside the end of the 'resize' function:
$sharpenMatrix = array(array(-1,-1,-1),array(-1,24,-1),array(-1,-1,-1));
$divisor = 16;
$offset = 0;
imageconvolution($newImage, $sharpenMatrix, $divisor, $offset);
I had problems with images quality on one of projects. But the problem was not on back-end, but on the front-end. Images had bad quality because images width and height given in the CSS was not the same as the image file had.
quick grep shows that you are able to set this on product_image object
app/code/core/Mage/Catalog/Helper/Image.php:105: * Set image quality, values in percentage from 0 to 100
app/code/core/Mage/Catalog/Helper/Image.php:107: * #param int $quality
app/code/core/Mage/Catalog/Helper/Image.php:110: public function setQuality($quality)
app/code/core/Mage/Catalog/Helper/Image.php:112: $this->_getModel()->setQuality($quality);
app/code/core/Mage/Catalog/Model/Product/Image.php:38: protected $_quality = 90;
app/code/core/Mage/Catalog/Model/Product/Image.php:88: * Set image quality, values in percentage from 0 to 100
app/code/core/Mage/Catalog/Model/Product/Image.php:90: * #param int $quality
app/code/core/Mage/Catalog/Model/Product/Image.php:93: public function setQuality($quality)
app/code/core/Mage/Catalog/Model/Product/Image.php:95: $this->_quality = $quality;
app/code/core/Mage/Catalog/Model/Product/Image.php:100: * Get image quality
app/code/core/Mage/Catalog/Model/Product/Image.php:106: return $this->_quality;
app/code/core/Mage/Catalog/Model/Product/Image.php:331: 'quality' . $this->_quality
app/code/core/Mage/Catalog/Model/Product/Image.php:387: $this->_processor->quality($this->_quality);
I had the same issue with some of my images, later i realized that those images with lower resolution were getting distorted, try using an image more than 1100 X 1100 and it should work just fine !
Upload the images as PNG's. They may not be as small as JPG, but it allowed us to avoid some image quality issues created by Magento's resizing functionality.

Resources