使用PHP的GD库,您可以使用imagerotate
功能旋转图像。这个功能的缺点是它不会剪切边缘,这正是我需要的。
以下是显示我的问题的示例图片:
如您所见,在Photoshop中边缘被剪裁。在PHP中,由于旋转,图像的大小刚刚增加。我真的想得到与Photoshop中相同的结果。知道如何在PHP中执行此操作吗?
(我只能访问GD库。)
答案 0 :(得分:6)
如果你懒得计算旋转图像的新尺寸,只需使用支持开箱即用的这些计算的基于GD的图像库。
一个这样的库是Wideimage。您加载原始图像,获取它的宽度和高度,然后旋转它,然后使用center
,middle
中所谓的智能坐标裁剪它,并使用原始图像宽度和高度:
$image = WideImage::load('big.png');
$width = $image->getWidth();
$height = $image->getHeight();
$image->rotate(120)->crop("center", "middle", $width, $height);
答案 1 :(得分:2)
如果angle
是旋转角度,则旋转图像的宽度和高度width′
和height′
,is given by:
width′ = height * s + width * c height′ = height * c + width * s
其中width
是源图像宽度,height
是源图像高度,并且:
s = abs(sin(angle)) c = abs(cos(angle))
请注意,由于三角恒等式sin( - θ)= -sin(θ)和cos( - θ)= cos (θ),如果在测量angle
时正方向是顺时针方向或逆时针方向,则上述公式无关紧要。
您知道的一件事是源图像的中心点被映射到旋转图像的中心。因此,如果width′ ≥ width
和height′ ≥ height
,则旋转图像中左上角的坐标为:
x = rotated_width / 2 - width / 2 y = rotated_height / 2 - height / 2
因此,如果width′ ≥ width
和height′ ≥ height
,以下PHP代码将根据需要裁剪图像:
$cropped = imagecrop($rotated, array(
'x' => $rotated_width / 2 - $width / 2,
'y' => $rotated_height / 2 - $height / 2,
'width' => $width,
'height' => $height
));
但是,这仅适用于width′ ≥ width
和height′ ≥ height
。例如,如果源图像的尺寸是正方形,则这是成立的,因为:
length′ = length * (abs(sin(angle)) + abs(cos(angle)))
和abs(sin(angle)) + abs(cos(angle)) ≥ 1
。
请参阅"y = abs(sin(theta)) + abs(cos(theta)) minima" on WolframAlpha。
如果width′ ≥ width
和height′ ≥ height
不成立(例如,250×40图像顺时针旋转50°),则生成的图像将完全变黑(因为无效的裁剪矩形会传递给imagecrop ())。
可以使用以下代码修复这些问题:
$cropped = imagecrop($rotated, array(
'x' => max(0, $rotated_width / 2 - $width / 2),
'y' => max(0, $rotated_height / 2 - $height / 2),
'width' => min($width, $rotated_width),
'height'=> min($height, $rotated_height)
));
此代码的结果是下图中的蓝色区域:
(有关SVG版本,请参阅http://fiddle.jshell.net/5jf3wqn4/show/。)
在图中,半透明的红色矩形表示原始的250×40图像。红色矩形表示图像的旋转。虚线矩形表示由imagerotate()创建的图像的边界。
将这些全部放在一起,这里是用于旋转和裁剪图像的PHP代码:
$filename = 'http://placehold.it/250x40';
$degrees = -50;
$source = imagecreatefrompng($filename);
$width = imagesx($source);
$height = imagesy($source);
$rotated = imagerotate($source, $degrees, 0);
imagedestroy($source);
$rotated_width = imagesx($rotated);
$rotated_height = imagesy($rotated);
$cropped = imagecrop($rotated, array(
'x' => max(0, (int)(($rotated_width - $width) / 2)),
'y' => max(0, (int)(($rotated_height - $height) / 2)),
'width' => min($width, $rotated_width),
'height'=> min($height, $rotated_height)
));
imagedestroy($rotated);
imagepng($cropped);
编辑:似乎有a bug in imagecrop(),其中1px黑线被添加到裁剪图像的底部。有关解决方法,请参阅imagecrop() alternative for PHP < 5.5。
EDIT2:我发现imageaffine()可以比imagerotate()产生更好的质量。用户“abc at ed48 dot com”has commented,使用仿射变换,用于逆时针旋转给定角度。
以下是使用imageaffine()而不是imagerotate()的代码:
// Crops the $source image, avoiding the black line bug in imagecrop()
// See:
// - https://bugs.php.net/bug.php?id=67447
// - https://stackoverflow.com/questions/26722811/imagecrop-alternative-for-php-5-5
function fixedcrop($source, array $rect)
{
$cropped = imagecreate($rect['width'], $rect['height']);
imagecopyresized(
$cropped,
$source,
0,
0,
$rect['x'],
$rect['y'],
$rect['width'],
$rect['height'],
$rect['width'],
$rect['height']
);
return $cropped;
}
$filename = 'http://placehold.it/250x40';
$degrees = -50;
$source = imagecreatefrompng($filename);
$width = imagesx($source);
$height = imagesy($source);
$radians = deg2rad($degrees);
$cos = cos($radians);
$sin = sin($radians);
$affine = [ $cos, -$sin, $sin, $cos, 0, 0 ];
$rotated = imageaffine($source, $affine);
imagedestroy($source);
$rotated_width = imagesx($rotated);
$rotated_height = imagesy($rotated);
$cropped = fixedcrop($rotated, array(
'x' => max(0, (int)(($rotated_width - $width) / 2)),
'y' => max(0, (int)(($rotated_height - $height) / 2)),
'width' => min($width, $rotated_width),
'height'=> min($height, $rotated_height)
));
imagedestroy($rotated);
imagepng($cropped);
答案 2 :(得分:1)
目前的答案只是解决问题的一种方法。它没有讨论计算新盒子大小所需的数学。
旋转后,您需要使用imagecrop
裁剪图像。为此,您可以使用以下公式使其居中。
rotated_dimension * (1 - source_dimension / rotated_dimension) * 0.5
这是一个有效的例子。 placehold.it URL可以替换为本地文件路径。
<?php
$filename = 'http://placehold.it/200x200';
$degrees = -45;
header('Content-type: image/png');
$source = imagecreatefrompng($filename);
$sw = imagesx($source);
$sh = imagesy($source);
$rotate = imagerotate($source, $degrees, 0);
$rw = imagesx($rotate);
$rh = imagesy($rotate);
$crop = imagecrop($rotate, array(
'x' => $rw * (1 - $sw / $rw) * 0.5,
'y' => $rh * (1 - $sh / $rh) * 0.5,
'width' => $sw,
'height'=> $sh
));
imagepng($crop);