使用WideImage调整图像大小而不失真并保持宽高比,然后裁剪多余部分

13

我在一个网站上正在开发一个区域,该区域将显示从外部来源获取的用户个人资料图片(因此无法控制其原始大小)。

我想要做的是将一张图片(在本例中为1000px x 800px)调整为200px x 150px。显然,这会导致长宽比差异。

我想要做的是在不失真的情况下调整原始图片的大小,这样就会产生一个200px x 160px的图像。接下来,我想要对边缘进行裁剪,以生成正确的图像大小。因此,在本例中,从图像的顶部和底部各裁剪5px,最终生成一个200px x 150px的图像。

我目前拥有WideImage库,并希望使用它。我在SO上看到了几个类似的问题,但没有一个能完全满足我想要实现的功能。


你有什么问题吗?请不要在标题中写标签。这不是一个留言板;我们有自己的语义帖子标记系统。 - Lightness Races in Orbit
纵横比背后的数学不应该很复杂,因为WideImageresize()方法已经完成了这项工作。但我不确定$fit应该设置为inside还是outside。此外,我无法从文档中理解crop('center', 'center', 200, 150)是否会将左上角设置为center-100, center-75center, center - binaryLV
我不确定API参考文档中如何实现这个功能,无论是直接通过API本身实现还是需要事先计算(而我不知道如何计算,因为以前从未这样做过)。 - lethalMango
5个回答

27

你可以尝试以下方法:

$img->resize(200, 150, 'outside')->crop('center', 'middle', 200, 150);
一些用户发布了他们自己的计算版本...这里也有我的版本:
$sourceWidth = 1000;
$sourceHeight = 250;

$targetWidth = 200;
$targetHeight = 150;

$sourceRatio = $sourceWidth / $sourceHeight;
$targetRatio = $targetWidth / $targetHeight;

if ( $sourceRatio < $targetRatio ) {
    $scale = $sourceWidth / $targetWidth;
} else {
    $scale = $sourceHeight / $targetHeight;
}

$resizeWidth = (int)($sourceWidth / $scale);
$resizeHeight = (int)($sourceHeight / $scale);

$cropLeft = (int)(($resizeWidth - $targetWidth) / 2);
$cropTop = (int)(($resizeHeight - $targetHeight) / 2);

var_dump($resizeWidth, $resizeHeight, $cropLeft, $cropTop);

1
谢谢,我会尝试一下并告诉你我的进展。 - lethalMango
1
@Salman A,你是对的,应该使用outside而不是inside。在crop()中,你关于将$left$top参数的值设为center也是正确的。 - binaryLV

9
我尝试了您提供的所有解决方案,每次都得到奇怪的数字。 因此,这是我的计算方式。
基本比例公式:a/b=x/y-> ay = bx然后解决未知值。 因此,让我们将其编码。
此函数假定您已将图像打开到变量中。 如果您传递路径,则必须在函数中打开图像,进行数学计算,关闭它,返回值,然后在调整大小时再次打开图像,这是低效的...
function resize_values($image){
    #for the purpose of this example, we'll set this here
    #to make this function more powerful, i'd pass these 
    #to the function on the fly
    $maxWidth = 200; 
    $maxHeight = 200;

    #get the size of the image you're resizing.
    $origHeight = imagesy($image);
    $origWidth = imagesx($image);

    #check for longest side, we'll be seeing that to the max value above
    if($origHeight > $origWidth){ #if height is more than width
         $newWidth = ($maxHeight * $origWidth) / $origHeight;

         $retval = array(width => $newWidth, height => $maxHeight);
    }else{
         $newHeight= ($maxWidth * $origHeight) / $origWidth;

         $retval = array(width => $maxWidth, height => $newHeight);
    }
return $retval;
}

上述函数显然返回一个数组。您可以将此公式嵌入到您正在编写的任何脚本中。这已被证明是完成工作的正确公式。因此...使用与否由您决定。待会见!


在这些 if 之前再添加一个 if 来检查它们是否相等,这样就不用计算了,这个思路不错吧? - kodfire

2
//Following code for keeping aspect ratio

$srcWidth=1000;
$srcHeight=800;

$targetWidth=200;
$targetHeight=150;

$diff=0;
//get difference

if( $srcWidth > $srcHeight ) // 1000 > 800
{
    $diff = $srcWidth - $srcHeight; // 1000 - 800 = 200
    $diff = ($diff / $srcWidth) * 100; // ( 200 / 1000 ) * 100 = 20
}
else
{
    $diff = $srcHeight - $srcWidth;
    $diff = ($diff / $srcHeight)*100;
}

if( $targetWidth > $targetHeight)
{
    $targetHeight = (( $targetHeight * $diff ) / 100) - $targetWidth; // (( 200 * 20 ) /  100) - 200 = 160
}
else
{
    $targetWidth = (( $targetWidth * $diff ) / 100) - $targetHeight;
}

2

答案#1(已编辑)

在查看这个这个之后,你可能需要像这样做:

$img->resize(200, 150, 'outside')->crop("center", "center", 200, 150);

在调整大小时,它会将图像调整为完全适合盒子(盒子= 200x150),或者其中一个维度适合而另一个超出盒子。在裁剪时,图像溢出盒子的部分将被修剪。指定中心智能坐标意味着将删除顶部+底部或左侧+右侧的部分。

答案#2

如果您在计算要裁剪的内容时遇到问题,请尝试以下方法:

<?php
$target_wide = 200;
$target_tall = 150;

$test_case = array(
    array(1000, 800),
    array(800, 1000),
    array(1000, 750), // perfect fit
    array(750, 1000)
);

foreach($test_case as $test) {
    list(
        $source_wide,
        $source_tall
    ) = $test;
    $source_aspect_ratio = $source_wide / $source_tall;
    $target_aspect_ratio = $target_wide / $target_tall;
    if ($source_aspect_ratio > $target_aspect_ratio)
    {
        $output_tall = $target_tall;
        $output_wide = (int) ($target_tall * $source_aspect_ratio);
    }
    else
    {
        $output_wide = $target_wide;
        $output_tall = (int) ($target_wide / $source_aspect_ratio);
    }
    $output_crop_hori = (int) (($output_wide - $target_wide) / 2);
    $output_crop_vert = (int) (($output_tall - $target_tall) / 2);
    var_dump($source_wide, $source_tall, $output_wide, $output_tall, $output_crop_hori, $output_crop_vert);
    echo PHP_EOL;
}

输出:

int(1000)
int(800)
int(200)
int(160)
int(0)
int(5)

int(800)
int(1000)
int(200)
int(250)
int(0)
int(50)

int(1000)
int(750)
int(200)
int(150)
int(0)
int(0)

int(750)
int(1000)
int(200)
int(266)
int(0)
int(58)

关于“答案#1”(实际上是“答案#2”:)crop()的前两个参数是$left$right。我认为将center作为$left$right会指定裁剪图像的角落位于图像中心,即如果他有200x160图像,则crop('center','center',200,150)crop(100,80,200,150)相同,而不是crop(0,5,200,150)。但是,这应该经过测试。 P.S.我已经在一个小时前写了关于这种方法的内容... - binaryLV
@binary:是的,答案是第二个 :) 前两个参数是left和top,而不是right。因此(0, 5, 200, 150)('center', 'center', 200, 150)是等价的;指定诸如'center'之类的常量可以免去自己进行计算的麻烦。 - Salman A
刚刚测试了一下,$left$right 应该确实只是 center - binaryLV

1

这是如何计算作物大小的方法:

$src_width = 1000;
$src_height = 500;
$src_ratio = $src_width/$src_height;

$width = 200;
$height = 150;
$ratio = $width/$height;

$crop_height = 0;
$crop_width = 0;

if ($src_height > $src_width)
{
    $new_height = $width/$src_ratio;
    $crop_height = $new_height-$height;
}
else
{
    $new_width = $height*$src_ratio;
    $crop_width = $new_width-$width;
}

print 'Crop height: '.$crop_height.PHP_EOL
      .'Crop width: '.$crop_width;

我认为还是有问题...尝试将$src_height设置为1500。我得到的是200 x 133.333而不是200 x 300。 - binaryLV
@binaryLV,这是对裁剪的计算,而不是新宽度和高度的计算。当$src_height = 1500;时,结果为:裁剪高度:-16.6(6) - OZ_
我在谈论 $new_width$new_height。而且如何才能使 crop height 为负数?裁剪角落的位置应该在图像内部。 - binaryLV
@binaryLV,这只是用来计算作物大小的临时变量。但你是对的,在一个地方应该将 * 替换为 / - 这是复制粘贴错误 :) 谢谢。 - OZ_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接