duanlu1279 2012-11-20 16:49
浏览 126


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


$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];

        $height = $imageprops['height'] / $scale;
        $image->scaleImage(0, $height);
    } else {
        $scale = $imageprops['width'] / $dimensions[1];

        $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);
  • 写回答

2条回答 默认 最新

  • doupaoshu8334 2012-11-20 17:23

    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:

    enter image description here

    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.



    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...

    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) {
    } */

    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.


    (And PS. Provide all the information in your questions straight up next time...)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?



  • ¥15 关于IMageEnView 图标定位问题
  • ¥20 求解答(matlab)
  • ¥30 ffmpeg库使用过程中遇到的问题
  • ¥15 pyqt5 中python如何通过Qtwebchannel主动发消息给web前端
  • ¥15 关于HTML中title获取xml内容的问题
  • ¥15 fanuc机器人PRIO083数字信号未复原错误,如何解决?
  • ¥20 如何为现有电路板增加远程控制功能
  • ¥15 UE5打包失败,求解决
  • ¥15 请问STM32G431的CANOPEN协议函数怎么写
  • ¥15 graphpad prism 三因素重复测定报错