I've been trying to figure out how to resize an uploaded image in PHP so that it is not smaller than a given size (650x650). However if a user uploads an image that is already smaller than my 650 minimum on either edge, no action is to be taken.
Scenario 1 - a 2000px width by 371px image is updloaded - this would not be resized as 371px is already less than my minimum.
Scenario 2 - a 2000px by 1823px image is uploaded - here I should resize the image as close to the minimum as possible, but not allow the width or height to be below 650px.
Here's the lines I've been thinking along so far (I'm using the excellent simpleImage script to help with resizing and getting dimensions):
$curWidth = $image->getWidth();
$curHeight = $image->getHeight();
$ratio = $curWidth/$curHeight;
if ($curWidth>$minImageWidth && $curHeight>$minImageHeight)
{
//both dimensions are above the minimum, so we can try scaling
if ($curWidth==$curHeight)
{
//perfect square :D just resize to what we want
$image->resize($minImageWidth,$minImageHeight);
}
else if ($curWidth>$curHeight)
{
//height is shortest, scale that.
//work out what height to scale to that will allow
//width to be at least minImageWidth i.e 650.
if ($ratio < 1)
{
$image->resizeToHeight($minImageWidth*$ratio);
}
else
{
$image->resizeToHeight($minImageWidth/$ratio);
}
}
else
{
//width is shortest, so find minimum we can scale to while keeping
//the height above or equal to the minimum height.
if ($ratio < 1)
{
$image->resizeToWidth($minImageHeight*$ratio);
}
else
{
$image->resizeToWidth($minImageHeight/$ratio);
}
}
However this gives me some strange results, at times it will still scale below the minimum. The only part of it that works as expected is the test for the dimensions being above the minimum - it won't scale anything that's too small.
I think my biggest problem here is that I don't fully understand the relationship between the image aspect ratio and the dimensions, and how to work out what dimensions I'd be able to scale to that are above my minimums. Any suggestions?
try this:
$curWidth = $image->getWidth();
$curHeight = $image->getHeight();
$ratio = min($minImageWidth/$curWidth,$minImageHeight/$curHeight);
if ($ratio < 1) {
$image->resize(floor($ratio*$curWidth),floor($ratio*$curHeight));
}
or this:
$image->maxarea($minImageWidth, $minImageHeight);
Try this:
$curWidth = $image->getWidth();
$curHeight = $image->getHeight();
if ($curWidth>$minImageWidth && $curHeight>$minImageHeight) {
$ratio = $curWidth/$curHeight;
$zoom = image_ratio > 1
? $minImageHeight / $curHeight
: $minImageWidth / $curWidth
;
$newWidth = $curWidth * $zoom;
$newHeight = $curHeight * $zoom;
}
Related
The Instagram image ratio requirements are:
1:1
4:5
1.91:1
16:9
Readed here and here. The documentation also says:
(...) Advise the app user to try again with an image that falls withing a
4:5 to 1.91:1 range.
Althought the documentation doesn't say, I know for a fact that publishing a photo with 1:1 ratio works.
So I would like to check if the image uploaded meets the requirements and if not resize to meet them (either by downsizing or upsizing). I'm using Laravel with Image Intervention library.
To check the aspect ratio, I'm using the following code:
public function isRatioValid($img)
{
$manager = new ImageManager(['driver' => 'imagick']);
$image = $manager->make($img);
$width = $image->width();
$height = $image->height();
for ($i = $height; $i > 1; $i--)
{
if (($width % $i) == 0 && ($height % $i) == 0)
{
$width = $width / $i;
$height = $height / $i;
}
}
$ratio = "{$width}:{$height}";
if (!in_array($ratio, ['1:1', '4:5', '1.91:1', '16:9']))
return false;
return true;
}
Problems:
This code strictly checks for the ratio values it does not entirely meet the Instagram documentation that says "(...) withing a 4:5 to 1.91:1 range". Is this ok?
In case the ratio is not valid, how can I use the Resize functionality to apply the best ratio that fits the Instagram requirements?
Solved.
The code to check the aspect ratio was not correct. To check it properly all we need to do is divide the width for the height and see if it fits the range of Instagram requirements.
function isRatioValid(...)
{
$width = 400;
$height = 711;
// 1:1 - squares
if ($width == $height)
return true;
$ratio = round($width / $height, 3);
// Portrait: min 0.8 | max 0.99
// Landscape: min 1.01 | max 1.91
if ($width < $height)
if ($ratio >= 0.8 && $ratio <= 0.99)
return true;
if ($width > $height)
if ($ratio >= 1.01 && $ratio <= 1.91)
return true;
return false;
}
To resize it, I added 0.10 to either the width (portraits) or height (landscapes) until the aspect ratio was within the range. You should limit the amount of tries so it won't burn the CPU / max execution time.
Can someone help me with an equation. I'd like to resize an image down so that it has a minimum width or height of 200px. So, if the original is 500 x 300 it'd end up being 333 x 200 and if the original was 500 x 900 it'd end up being 200 x 360 and so on. If either of the original dimensions are smaller than 200px, no resizing is done.
I'd appreciate any input on this. I am sure its quite simple, I just can't seem to work it out.
You just need to work out the existing image ratio and do the math from that. The ratio is the images width compared to height or vice versa (usually which ever is longer is divided by the shorter to give you a ratio >= 1). for example width 450 and height 300 has a ratio of 1.5 wide. meaning that the image is 1.5 times wider than it is tall. Then you can multiply your "minimum" value by the ratio to get the other size or as in the example if your height is 200 the other side will be minimum * ratio or 200 * 1.5 which makes the size 300 wide and 200 tall.
$height = 500;
$width = 300;
$min = 200;
if($width > $height){
$ratio = $width/$height;
$height = $min;
$width = round($min * $ratio);
} else {
$ratio = $height/$width;
$width = $min;
$height = round($min * $ratio);
}
echo "Width: $width<br>";
echo "Height: $height<br>";
http://codepad.viper-7.com/RsxAsC
Aparently, you'd like to have the smallest side to fit 200px, but dont upscale. I'd do something like this
if ($width > 200 || $height > 200) {
if ($width > $height) {
$ratio = (200 / $height);
} else {
$ratio = (200 / $width);
}
$resizedWidth = $ratio * $width;
$resizedHeight = $ratio * $height;
}
Not complete code, just a starting point, hope this helps
if your server has the php imagick extension installed you can use the function cropThumbnailImage()
http://php.net/manual/en/imagick.cropthumbnailimage.php
I altered Jonahtan answer to be a bit shorter and where you only calculate a ratio once:
// Set image dimensions for testing
$imgWidth = 100;
$imgHeight = 250;
// Minimal dimension requirements
$minDim = 300;
if ($imgWidth < $minDim || $imgHeight < $minDim) {
$ratio = $imgWidth / $imgHeight;
if( $ratio > 1) { // Width is greater
$imgHeight = $minDim;
$imgWidth = round($minDim * $ratio);
}
else { // Height is greater
$imgWidth = $minDim;
$imgHeight = round($minDim / $ratio);
}
}
echo "Width: $imgWidth<br>";
echo "Height: $imgHeight<br>";
I'm trying to downsize image uploaded by users at the time of upload.
This is no problem but I want to get specific with sizes now.
I'm looking for some advice on an algorithm im struggling to produce that will take any shape image - square or rectangle of any widths/heights and downsize it.
This image needs to be downsized to a target box size (this changes but is stored in a variable)..
So it needs to downsize the image so that both the width and height are larger than the width and height of the target maintaining aspect ratio. but only just..
The target size will be used to crop the image so there is no white space around the edges etc.
I'm looking for just the algorithm behind creating the correct resize based on different dimension images - I can handle the cropping, even resizing in most cases but it fails in a few so i'm reaching out.
I'm not really asking for PHP code more pseudo. Either is fine obviously.
Thanks Kindly.
Current code.. but I've gone through so many iterations this might not work at all anymore.. :P
$image = $image_orig->clone();
$wRatio = $imageprops['width'] / $dimensions[0]; // imageprops = original image dimens.
$hRatio = $imageprops['height'] / $dimensions[1]; // $dimensions[0] = new width
$maxRatio = max($wRatio,$hRatio); // $dimensions[1] = new height
var_dump('thumb');
$ratio = ($imageprops['width'] - $dimensions[0]) > ($imageprops['height'] - $dimensions[1]);
$shape = ($imageprops['width'] > $imageprops['height']);
$error = ($imageprops['width'] / $imageprops['height']);
if( $error < 0.95 || $error > 1.05 ) { // its NOT a square
if($shape){ // longer width
$scale = $imageprops['height'] / $dimensions[0];
var_dump('1');
$height = $imageprops['height'] / $scale;
$image->scaleImage(0, $height);
} else {
$scale = $imageprops['width'] / $dimensions[1];
var_dump('2');
$width = $imageprops['width'] / $scale;
$image->scaleImage($width, 0);
}
} elseif($ratio) { // is square
$scale = $imageprops['height'] / $dimensions[1];
$newWidth = $imageprops['width'] / $scale;
$extra = 0;
$height = $dimensions[1]+$extra;
$image->scaleImage(0, $height);
} else {
$scale = $imageprops['width'] / $dimensions[0];
$newHeight = $imageprops['height'] / $scale;
$extra = 0;
$width = $dimensions[0]+$extra;
$image->scaleImage($width, 0);
}
$image_size = $image->getImageGeometry();
$image->cropImage($dimensions[0], $dimensions[1],($image_size['width']-$dimensions[0])/2,($image_size['height']-$dimensions[1])/2);
Notice: I wrote this answer before the original poster edited his question to include things which clarified points which has since changed what I believed the original question was asking.
So, there are a few concepts and ideas that you can throw around to try and solve what it is you are intending to achieve. (Gravity Cropping, Content Aware Cropping, Content Aware Rescaling etc)
Because you are always decreasing the size of the original image, you are essentially just looking to "chop" out a section of the image. Very much like this:
The issue however, is that you frequently want to make sure that you select the best region of the image so that you don't crop on an irrelevant segment of the image. This is known as content-aware image cropping, and by just searching using "Content Aware Image Crop" you can find a wealth of information.
Anyway, moving on, depending on your exact use case, you might discover that actually you don't want to chop out anything form the image, and instead you want to "liquid scale and crop" the image, so that you do "Content Aware Resizing". The difference with content aware resizing is that the resizing ignores all aspect ratios of the image and instead looks at neighbouring edges and colors in order to resize the image in a fluidic way.
So, luckily enough, Imagick comes with it's very own [liquidRescaleImage][3] function.
You can use it like
$im->liquidRescaleImage(480, 260, 3, 18);
Using Liquid Rescale can rescale out quite nicely. Here is an example below which is anything but perfect, but I purposefully have created an example that isn't perfect so you can actually see that liquidRescale changes the composition of the image, rather than just adjusts the aspect ratio.
Original
Content Aware Liquid Rescale (450x350)
If however, you just want to scale an image, and keep the aspect ratio, you might want to do what sites such as Flickr do, where they make the images longestLength to be equal to a value. (Flickr for example has 6 or so different dimension sizes)
We resize your photos to more web-friendly dimensions. Each image has
a 75x75 and 150x150 square thumbnail and 100-, 240-, 320-, 500-, 640-
800*-, 1024-, 1600*-, and 2048*-pixel versions (that's the length of
the longest side), as well as your original file.
Basic Class that Replicates the Flickr Resize Policies...
<?php
class Scale {
public function getImageScale($x, $y, $longestLength, $allowDistort = false) {
//Set the default NEW values to be the old, in case it doesn't even need scaling
list($nx, $ny) = array($x, $y);
if ($x > $y) {
if ($longestLength > $x && !$allowDistort) {
return array($x, $y);
}
$r = $x / $longestLength;
return array($longestLength, $y / $r);
} else {
if ($longestLength > $x && !$allowDistort) {
return array($x, $y);
}
$r = $y / $longestLength;
return array($x / $r, $longestLength);
}
return array($nx, $ny);
}
}
And then if you were to pass in the Image Dimensions like this:
$originalImageX = 480;
$originalImageY = 260;
$scale = new Scale();
var_dump($scale->getImageScale($originalImageX,$originalImageY,120 ) );
/* Outputs
array(2) {
[0]=>
int(120)
[1]=>
int(65)
} */
Also, there is this Content Aware Cropping class on Git, that I have previously adapted the base class/Structure previously to use in my own project so I know that it works nicely. I'm not sure on the licencing of this class, but you can obviously take a look at it and slice out what works for you as I have done previously.
https://gist.github.com/2468935#file_content_aware_image.php
(And PS. Provide all the information in your questions straight up next time...)
Dunno if this is what you want, from my personal script library xD :
You only need to specify the desired $maxW $maxH and same for thumbnails, and it will downscale the image proportionally.
The script has two parts that are almost the same, one is for the thumbnails and one for the actual image.
list($w, $h) = getimagesize($file);
$maxW = 1024;
$maxH = 786;
$thumbMaxW = 138;
$thumbMaxH = 92;
$newW = $w;
$newH = $h;
if($w > $thumbMaxW OR $h > $thumbMaxH){
if($w>=$h){
$thumbW = $thumbMaxW;
$thumbRatio = $w/$thumbMaxW;
$thumbH = (int)($h/$thumbRatio);
} else {
$thumbH = $thumbMaxH;
$thumbRatio = $h/$thumbMaxH;
$thumbW = (int)($w/$thumbRatio);
}
} else {
$thumbW = $w;
$thumbH = $h;
}
--------------------------------------------
if($w > $maxW OR $h > $maxH){
if($w >= $h){
$newW = $maxW;
$reduction = $w/$newW;
$newH = (int) ($h/$reduction);
}
if($h > $w){
$newH = $maxH;
$reduction = $h/$newH;
$newW = (int)($w/$reduction);
}
} else {
$newW = $w;
$newH = $h;
}
I have 2 variables available about a photo i'm importing, width and height.(I could use imagesx etc, but i'm importing in bulk and it takes way too long)
eg..
$w = 720;
$h = 540;
How can I workout the correct height for the photo if I say the max width is 180px, I know the correct height should be 135px (on a standard landscape photo). But the starting width and height i'm given will change depending on the photo.
Keeping the image in proportion what calculation do I need to do to workout the reduced width and height?
Final result will be in pixels so can not have any decimal places.
(I do not actually need to re-size the photo, just calculate the size.)
Thanks in advance :)
720x = 540 * 180
It is a simple cross-multiplying rule. Then round the numbers to get rid of the decimal portion.
Here's one I use:
function scaleDimensions($orig_width, $orig_height, $max_width, $max_height) {
if ($orig_width < $max_width && $orig_height < $max_height) {
return array($orig_width, $orig_height);
}
$ratiow = $max_width / $orig_width;
$ratioh = $max_height / $orig_height;
$ratio = min($ratiow, $ratioh);
$width = intval($ratio * $orig_width);
$height = intval($ratio * $orig_height);
return array($width, $height);
}
Usage:
list($width, $height) = scaleDimensions($w, $h, 180, 135);
And this is the javascript version of #fire's answer
scaleDimensions : function(orig_width, orig_height, max_width, max_height) {
if (orig_width < max_width && orig_height < max_height) {
return [orig_width, orig_height];
}
var ratiow = max_width / orig_width;
var ratioh = max_height / orig_height;
var ratio = Math.min(ratiow, ratioh);
var width = (ratio * orig_width).toFixed(0);
var height = (ratio * orig_height).toFixed(0);
return [width, height];
}
What would be the best way to resize images, which could be of any dimension, to a fixed size, or at least to fit within a fixed size?
The images come from random urls that are not in my control, and I must ensure the images do not go out of an area roughly 250px x 300px, or 20% by 50% of a layer.
I would think that I would first determine the size, and if it fell outside the range, resize by a factor, but I am unsure how to work out the logic to resize if the image size could be anything.
edit:I do not have local access to the image, and the image url is in a variable with is output with img src=..., I need a way to specify the values of width and height tags.
It's easy to do with ImageMagick. Either you use convert command line tool via exec or you use http://www.francodacosta.com/phmagick/resizing-images .
If you use 'convert' you can even tell ImageMagick to just resize larger images and to not convert smaller ones: http://www.imagemagick.org/Usage/resize/
If you don't have local access, you won't be able to use ImageMagick:
<?php
$maxWidth = 250;
$maxHeight = 500;
$size = getimagesize($url);
if ($size) {
$imageWidth = $size[0];
$imageHeight = $size[1];
$wRatio = $imageWidth / $maxWidth;
$hRatio = $imageHeight / $maxHeight;
$maxRatio = max($wRatio, $hRatio);
if ($maxRatio > 1) {
$outputWidth = $imageWidth / $maxRatio;
$outputHeight = $imageHeight / $maxRatio;
} else {
$outputWidth = $imageWidth;
$outputHeight = $imageHeight;
}
}
?>
Have you looked at the GD library documentation, particularly the imagecopyresized method?
If you have to keep the aspect ratio of the image intact then you should scale it by a factor so that the longer side of the image (height or width) fits inside the limitations for that side.
You could use the following:
$maxWidth = 250;
$maxHeight = 500;
$validSize = true;
$imageinfo = getimagesize($filename);
if ($imageinfo) {
$validSize &= $imageinfo[0] <= $maxWidth;
$validSize &= $imageinfo[1] <= $maxHeight;
}
The &= is a combined operator of the bitwise-and operator & and the assignment operator =. Thus $foo &= *expr* and $foo = $foo & *expr* are equivalent.
I'm not sure of the exact solution to your answer, but I know a project written in PHP which has the solution. Go and take a look at the ImageCache built for the Drupal CMS, which is written in PHP.
It basically let's you define some actions to take on an arbitrary image, and produce almost whatever scaling/cropping you want to the image.
You'd have to learn a few of the Drupal API's to be able to understand this example, but it is extensive and if you understand their algorithms, you'd be able to solve other more complicated problems.
I created the following function to do intelligent resizing of images with respect to ratio and even has a parameter to upscale smaller images (which is critical if your HTML layout screws up when thumbnails are wierd sizes).
function ImageIntelligentResize( $imagePath, $maxWidth, $maxHeight, $alwaysUpscale )
{
// garbage in, garbage out
if ( IsNullOrEmpty($imagePath) || !is_file($imagePath) || IsNullOrEmpty($maxWidth) || IsNullOrEmpty($maxHeight) )
{
return array("width"=>"", "height"=>"");
}
// if our thumbnail size is too big, adjust it via HTML
$size = getimagesize($imagePath);
$origWidth = $size[0];
$origHeight = $size[1];
// Check if the image we're grabbing is larger than the max width or height or if we always want it resized
if ( $alwaysUpscale || $origWidth > $maxWidth || $origHeight > $maxHeight )
{
// it is so let's resize the image intelligently
// check if our image is landscape or portrait
if ( $origWidth > $origHeight )
{
// target image is landscape/wide (ex: 4x3)
$newWidth = $maxWidth;
$ratio = $maxWidth / $origWidth;
$newHeight = floor($origHeight * $ratio);
// make sure the image wasn't heigher than expected
if ($newHeight > $maxHeight)
{
// it is so limit by the height
$newHeight = $maxHeight;
$ratio = $maxHeight / $origHeight;
$newWidth = floor($origWidth * $ratio);
}
}
else
{
// target image is portrait/tall (ex: 3x4)
$newHeight = $maxHeight;
$ratio = $maxHeight / $origHeight;
$newWidth = floor($origWidth * $ratio);
// make sure the image wasn't wider than expected
if ($newWidth > $maxWidth)
{
// it is so limit by the width
$newWidth = $maxWidth;
$ratio = $maxWidth / $origWidth;
$newHeight = floor($origHeight * $ratio);
}
}
}
// it's not, so just use the current height and width
else
{
$newWidth = $origWidth;
$newHeight = $origHeight;
}
return array("width"=>$newWidth, "height"=>$newHeight);
}