graphicsmagick in perl shrink image to size - image

The graphicsmagick package for perl is very extensive but the documentation appears light (or confusing to me) If I have an image and I want to ensure the size is within 800x200 (like https://via.placeholder.com/800x200) while maintaining the aspect ratio of the image, which commands should I use?
In php I have
$tmpImageFile->scaleImage(100,0);
$imageGeometry = $tmpImageFile->getImageGeometry();
$imageHeight = $imageGeometry['height'];
if ( $imageHeight > 100 ) {
$tmpImageFile->scaleImage(0,100);
}

Maybe you could do something like this:
use feature qw(say);
use strict;
use warnings;
use Graphics::Magick;
my $img = Graphics::Magick->new;
$img->ReadImage('gm.png');
my $height = $img->Get('height');
my $width = $img->Get('width');
say "Height = ", $img->Get('height');
say "Width = ", $img->Get('width');
my $target_width = 800;
my $target_height = 200;
my $factor1 = $target_width/$width;
my $factor2 = $target_height/$height;
say "factor1 = $factor1";
say "factor2 = $factor2";
say "$width x $factor1 = ", $width * $factor1;
my $height1 = $height * $factor1;
say "$height x $factor1 = ", $height1;
my $width2 = $width * $factor2;
say "$width x $factor2 = ", $width2;
say "$height x $factor2 = ", $height * $factor2;
$target_height = $height1 if $height1 < $target_height;
$target_width = $width2 if $width2 < $target_width;
$img->Scale(height => $target_height, width => $target_width);
say "Height = ", $img->Get('height');
say "Width = ", $img->Get('width');
$img->Write('out.jpg');
Here I have created an input image "gm.png" :
When running the script I get output:
Height = 404
Width = 1548
factor1 = 0.516795865633075
factor2 = 0.495049504950495
1548 x 0.516795865633075 = 800
404 x 0.516795865633075 = 208.785529715762
1548 x 0.495049504950495 = 766.336633663366
404 x 0.495049504950495 = 200
Height = 200
Width = 766
And the saved output file out.jpg:

The algorithm of a solution for the problem is quite simple.
It is required to find the smallest scale for the image between scale for width and height. Once scale identified apply it to the image.
use strict;
use warnings;
use Graphics::Magick;
my $fname = shift || die "Provide filename";
my($image,$img);
my($scale,$scaleH,$scaleW);
my $holder = {
width => 800,
height => 200
};
$image = Graphics::Magick->new;
$image->ReadImage($fname);
$img->{height} = $image->Get('height');
$img->{width} = $image->Get('width');
$scaleH = $holder->{height} / $img->{height};
$scaleW = $holder->{width} / $img->{width};
$scale = $scaleH < $scaleW ? $scaleH : $scaleW;
$image->Scale( height => $img->{height}*$scale, width => $img->{width}*$scale );
$image->write('image_new.jpg');
exit 0;
For a test was taken a random file from internet
Result verification
$ file image.png
image.png: PNG image data, 2250 x 585, 8-bit/color RGB, non-interlaced
$ file image_new.jpg
image_new.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, baseline, precision 8, 769x200, components 3

Related

html5 canvas, how to export a 2x image?

I designed a web app with html5 canvas. To export an image, the code will be below:
var img = canvas.toDataURL("image/png");
Is there any way to export a 2x image?
It is for hdpi display like apple retina display.
Yes there are a few ways but every time you stretch a non vector image you will get some pixel distortion. However if its only two times the size you could get away with it using nearest neighbor. The below example shows two different methods, one is just stretching the image, the other uses nearest neighbor with a zoom factor of two.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
canvas2 = document.getElementById("canvas2"),
ctx2 = canvas2.getContext("2d"),
tempCtx = document.createElement('canvas').getContext('2d'),
img = document.getElementById("testimg"),
zoom = 2;
tempCtx.drawImage(img, 0, 0);
var imgData = tempCtx.getImageData(0, 0, img.width, img.height).data;
canvas.width = img.width * zoom;
canvas.height = img.height * zoom;;
// nearest neighbor
for (var x = 0; x < img.width; ++x) {
for (var y = 0; y < img.height; ++y) {
var i = (y * img.width + x) * 4;
var r = imgData[i];
var g = imgData[i + 1];
var b = imgData[i + 2];
var a = imgData[i + 3];
ctx.fillStyle = "rgba(" + r + "," + g + "," + b + "," + (a / 255) + ")";
ctx.fillRect(x * zoom, y * zoom, zoom, zoom);
}
}
// stretched
ctx2.drawImage(img, 0, 0, 140, 140);
#phrogz has a great example of this here as well, showing a few different ways you can accomplish image re-sizing.

math/algorithm Fit image to screen retain aspect ratio

I need help with math / algorithm to take an image of known size and fit to one of two screen dimensions:
720 x 480 or 1280 x 1024.
The image dimensions are coming from an XML file, however those dimensions are the web dimensions, I also get a selection of images from the XML that may be of higher and lower resolution than the web dimensions.
What I want is to use the aspect ration of the web dimensions to display the higher resolution image, if available, on an HD (1280x720) screen, or, if the user is on an SD screen (720x480) display the image on that screen.
Other things that would be useful for this, but lower priority, would be, if I know the resolution of the image is smaller in both dimensions than an SD screen (in this case, all I know is the web dimension, and the horizontal dimension of the image file), to display it as actual size on that screen.
Generic as can be:
Image data: (wi, hi) and define ri = wi / hi
Screen resolution: (ws, hs) and define rs = ws / hs
Scaled image dimensions:
rs > ri ? (wi * hs/hi, hs) : (ws, hi * ws/wi)
So for example:
20
|------------------|
10
|---------|
-------------------- --- ---
| | | | 7 |
| | | | | 10
|---------- | --- |
| | |
-------------------- ---
ws = 20
hs = 10
wi = 10
hi = 7
20/10 > 10/7 ==> (wi * hs/hi, hs) = (10 * 10/7, 10) = (100/7, 10) ~ (14.3, 10)
Which as you can see clearly scales to the screen size, because the height is that of the screen but clearly keeps aspect ratio since 14.3/10 ~ 10/7
UPDATE
Center the image as follows:
call (wnew, hnew) the new dimensions.
top = (hs - hnew)/2
left = (ws - wnew)/2
I understand the accepted answer and it works, but I've always found the following method to be simpler and succinct for "best fit":
// prep
let maxWidth = 190,
maxHeight = 150;
let imgWidth = img.width,
imgHeight = img.height;
// calc
let widthRatio = maxWidth / imgWidth,
heightRatio = maxHeight / imgHeight;
let bestRatio = Math.min(widthRatio, heightRatio);
// output
let newWidth = imgWidth * bestRatio,
newHeight = imgHeight * bestRatio;
... which of course can be distilled down to:
const maxWidth = 190, maxHeight = 150;
const bestRatio = Math.min(maxWidth / img.width, maxHeight / img.height);
img.width *= bestRatio;
img.height *= bestRatio;
Here it is in straightforward C.
You want to scale both coordinates by the returned scale factor.
/* For a rectangle inside a screen, get the scale factor that permits the rectangle
to be scaled without stretching or squashing. */
float
aspect_correct_scale_for_rect(const float screen[2], const float rect[2])
{
float screenAspect = screen[0] / screen[1];
float rectAspect = rect[0] / rect[1];
float scaleFactor;
if (screenAspect > rectAspect)
scaleFactor = screen[1] / rect[1];
else
scaleFactor = screen[0] / rect[0];
return scaleFactor;
}
Aspect ratio correction with letterboxing or fit-to-screen
I wrote up a method recently to handle this exact problem in iOS. I'm using the Eigen matrix library to do scaling, but the the principle (scaling factor) is the same without matrices.
Eigen::Matrix4x4f aspectRatioCorrection(bool fillScreen, const Eigen::Vector2f &screenSize, const Eigen::Vector2f &imageSize)
{
Eigen::Matrix4x4f scalingMatrix(Eigen::Matrix4x4f::Identity());
float screenWidth = screenSize.x();
float screenHeight = screenSize.y();
float screenAspectRatio = screenWidth / screenHeight;
float imageWidth = imageSize.x();
float imageHeight = imageSize.y();
float imageAspectRatio = imageWidth / imageHeight;
float scalingFactor;
if (fillScreen) {
if (screenAspectRatio > imageAspectRatio) {
scalingFactor = screenWidth / imageWidth;
} else {
scalingFactor = screenHeight / imageHeight;
}
} else {
if (screenAspectRatio > imageAspectRatio) {
scalingFactor = screenHeight / imageHeight;
} else {
scalingFactor = screenWidth / imageWidth;
}
}
scalingMatrix(0, 0) = scalingFactor;
scalingMatrix(1, 1) = scalingFactor;
return scalingMatrix;
}

MATLAB coding problem

Hey guys, I got this error message when I tried to trigger the function below. Can anybody help me out? Thanks!
>> changeYuv('tilt.yuv',352,288,1:40,40);
??? Index exceeds matrix dimensions.
Error in ==> changeYuv at 32
j=histogram(imgYuv(:,:,1,k+1));
>> [x,y,z,a]=size(imgYuv)
x =
288
y =
352
z =
3
a =
40
The source code:
function [imgYuv, S]= changeYuv(fileName, width, height, idxFrame, nFrames)
% load RGB movie [0, 255] from YUV 4:2:0 file
fileId = fopen(fileName, 'r');
subSampleMat = [1, 1; 1, 1];
nrFrame = length(idxFrame);
for f = 1 : 1 : nrFrame
% search fileId position
sizeFrame = 1.5 * width * height;
fseek(fileId, (idxFrame(f) - 1) * sizeFrame, 'bof');
% read Y component
buf = fread(fileId, width * height, 'uchar');
imgYuv(:, :, 1,f) = reshape(buf, width, height).';
% read U component
buf = fread(fileId, width / 2 * height / 2, 'uchar');
imgYuv(:, :, 2,f) = kron(reshape(buf, width / 2, height / 2).', subSampleMat); % reshape and upsample
% read V component
buf = fread(fileId, width / 2 * height / 2, 'uchar');
imgYuv(:, :, 3,f) = kron(reshape(buf, width / 2, height / 2).', subSampleMat); % reshape and upsample
%histogram difference of Y component
for k=1:(nFrames-1)
h=histogram(imgYuv(:,:,1,k));
j=histogram(imgYuv(:,:,1,k+1));
X=abs(h-j)/256;
S(k)=sum(X);
end
end
fclose(fileId);
On every iteration of the outer loop, you appear to be growing imgYuv by one in the 4th dimension, starting from empty. But your inner loop always loops from 1 to nFrames-1. Therefore, it would seem to me like you're trying to access beyond the extent of imgYuv.
On an unrelated note, growing an array like this is typically very slow. You're much better off initialising imgYuv before you start, i.e. imgYuv = zeros([height,width,3,nFrames]).

How can I crop/scale user images so I can display fixed sized thumbnails without skewing and stretching?

I'm working on allowing users to upload profile pictures for my site. The classic example of what I'm trying to avoid is plentyoffish.com where each users image is skewed and looks very ugly:
So, how can I progmatically crop/create standard sized versions of an image without the skewing demonstrated above?
Well, you must have a maximum height and width, lets assume the image size you have available is square, say 100x100.
When a user uploads an image get the dimensions of it, then work out which is greater, the height or the width.
Then take the greatest measurement and get the ratio of that measurement to your target measurement, then use that ratio to scale both the height and width.
So if the user uploads a picture of 500 height and 450 width, as the height is greatest you'd divide 100 by 500, your thumbnail size. This gives us .2 as the ratio. which means the width will become 90, so you would shrink to 100x90, and no distortion would occur.
Here's some code (C#) I used to do a resize, similar to the method suggested by blowdart. Just replace the "300"s with the maximum size of one side in your case:
private Bitmap ScaleImage(Image oldImage)
{
double resizeFactor = 1;
if (oldImage.Width > 300 || oldImage.Height > 300)
{
double widthFactor = Convert.ToDouble(oldImage.Width) / 300;
double heightFactor = Convert.ToDouble(oldImage.Height) / 300;
resizeFactor = Math.Max(widthFactor, heightFactor);
}
int width = Convert.ToInt32(oldImage.Width / resizeFactor);
int height = Convert.ToInt32(oldImage.Height / resizeFactor);
Bitmap newImage = new Bitmap(width, height);
Graphics g = Graphics.FromImage(newImage);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(oldImage, 0, 0, newImage.Width, newImage.Height);
return newImage;
}
OR: If you would still like fixed dimensions, you follow blowdart's instructions but calculate the greatest ratio instead: 100px / 450px = .22..
width: 100px
height: 111.11..px -> crop from floor((111.11 - 100) / 2) on top and 100px down.
EDIT: Or let the user select how to crop the greatest dimension.
Use ImageMagick. On the command line use:
convert -thumbnail geometry
I made this function for PHP a while ago that works great for this and some other scenarios:
<?php
function Image($source, $crop = null, $resize = null)
{
$source = ImageCreateFromString(file_get_contents($source));
if (is_resource($source) === true)
{
$width = imagesx($source);
$height = imagesy($source);
if (isset($crop) === true)
{
$crop = array_filter(explode('/', $crop), 'is_numeric');
if (count($crop) == 2)
{
if (($width / $height) > ($crop[0] / $crop[1]))
{
$width = $height * ($crop[0] / $crop[1]);
$crop = array((imagesx($source) - $width) / 2, 0);
}
else if (($width / $height) < ($crop[0] / $crop[1]))
{
$height = $width / ($crop[0] / $crop[1]);
$crop = array(0, (imagesy($source) - $height) / 2);
}
}
else
{
$crop = array(0, 0);
}
}
else
{
$crop = array(0, 0);
}
if (isset($resize) === true)
{
$resize = array_filter(explode('*', $resize), 'is_numeric');
if (count($resize) >= 1)
{
if (empty($resize[0]) === true)
{
$resize[0] = round($resize[1] * $width / $height);
}
else if (empty($resize[1]) === true)
{
$resize[1] = round($resize[0] * $height / $width);
}
}
else
{
$resize = array($width, $height);
}
}
else
{
$resize = array($width, $height);
}
$result = ImageCreateTrueColor($resize[0], $resize[1]);
if (is_resource($result) === true)
{
ImageCopyResampled($result, $source, 0, 0, $crop[0], $crop[1], $resize[0], $resize[1], $width, $height);
ImageDestroy($source);
header('Content-Type: image/jpeg');
ImageJPEG($result, null, 90);
ImageDestroy($result);
}
}
return false;
}
Image('/path/to/your/image.jpg', '1/1', '100*');
Image('/path/to/your/image.jpg', '1/1', '100*100');
Image('/path/to/your/image.jpg', '1/1', '100*500');
?>
Here's a bash command I threw together to accomplish this using ImageMagick's convert tool. For a set of images sitting in the parent directory, some portrait, some landscape, to create images in the current directory scaled to 600x400, cropping portrait images from the centre and simply scaling the landscape images:
for f in ../*jpg; do
echo $f;
size=`identify $f|cut -d' ' -f 3`;
w=`echo $size|cut -dx -f 1`;
h=`echo $size|cut -dx -f 2`;
if [ $w -gt $h ]; then
convert $f -thumbnail 600x400 `basename $f`;
else
convert $f -scale 600x -crop 600x400+0+`echo "((600*($h/$w))/2)" | bc | sed 's/\..*//'` `basename $f`;
fi;
done;

What's a more elegant rephrasing of this cropping algorithm? (in Python)

I want to crop a thumbnail image in my Django application, so that I get a quadratic image that shows the center of the image. This is not very hard, I agree.
I have already written some code that does exactly this, but somehow it lacks a certain ... elegance. I don't want to play code golf, but there must be a way to express this shorter and more pythonic, I think.
x = y = 200 # intended size
image = Image.open(filename)
width = image.size[0]
height = image.size[1]
if (width > height):
crop_box = ( ((width - height)/2), 0, ((width - height)/2)+height, height )
image = image.crop(crop_box)
elif (height > width):
crop_box = ( 0, ((height - width)/2), width, ((height - width)/2)+width )
image = image.crop(crop_box)
image.thumbnail([x, y], Image.ANTIALIAS)
Do you have any ideas, SO?
edit: explained x, y
I think this should do.
size = min(image.Size)
originX = image.Size[0] / 2 - size / 2
originY = image.Size[1] / 2 - size / 2
cropBox = (originX, originY, originX + size, originY + size)
The fit() function in the PIL ImageOps module does what you want:
ImageOps.fit(image, (min(*image.size),) * 2, Image.ANTIALIAS, 0, (.5, .5))
width, height = image.size
if width > height:
crop_box = # something 1
else:
crop_box = # something 2
image = image.crop(crop_box)
image.thumbnail([x, x], Image.ANTIALIAS) # explicitly show "square" thumbnail
I want to a content analysis of a jepg image. I wish to take a jpeg imafe say 251 x 261 and pass it through an algorithm to crop it to say 96 x 87. Can this program do that like t write an intelligent cropping algorithm, with a prompt to rezie the image.

Resources