在 PHP 中从图像中裁剪空白
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1669683/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Crop whitespace from image in PHP
提问by usertest
Is it possible to remove the whitespace surrounding an image in PHP?
是否可以在 PHP 中删除图像周围的空格?
NOTE: to clarify I mean something like photoshops trim feature.
注意:澄清我的意思是像Photoshop修剪功能。
Thanks.
谢谢。
回答by gnud
To trim all whitespace, as you call it, surrounding the interesting part of the image, first we find out where the "whitespace" stops, and then we copy everything inside of those borders.
要修剪所有空白,如您所说,围绕图像的有趣部分,首先我们找出“空白”停止的位置,然后我们复制这些边界内的所有内容。
//load the image
$img = imagecreatefromjpeg("http://ecx.images-amazon.com/images/I/413XvF0yukL._SL500_AA280_.jpg");
//find the size of the borders
$b_top = 0;
$b_btm = 0;
$b_lft = 0;
$b_rt = 0;
//top
for(; $b_top < imagesy($img); ++$b_top) {
for($x = 0; $x < imagesx($img); ++$x) {
if(imagecolorat($img, $x, $b_top) != 0xFFFFFF) {
break 2; //out of the 'top' loop
}
}
}
//bottom
for(; $b_btm < imagesy($img); ++$b_btm) {
for($x = 0; $x < imagesx($img); ++$x) {
if(imagecolorat($img, $x, imagesy($img) - $b_btm-1) != 0xFFFFFF) {
break 2; //out of the 'bottom' loop
}
}
}
//left
for(; $b_lft < imagesx($img); ++$b_lft) {
for($y = 0; $y < imagesy($img); ++$y) {
if(imagecolorat($img, $b_lft, $y) != 0xFFFFFF) {
break 2; //out of the 'left' loop
}
}
}
//right
for(; $b_rt < imagesx($img); ++$b_rt) {
for($y = 0; $y < imagesy($img); ++$y) {
if(imagecolorat($img, imagesx($img) - $b_rt-1, $y) != 0xFFFFFF) {
break 2; //out of the 'right' loop
}
}
}
//copy the contents, excluding the border
$newimg = imagecreatetruecolor(
imagesx($img)-($b_lft+$b_rt), imagesy($img)-($b_top+$b_btm));
imagecopy($newimg, $img, 0, 0, $b_lft, $b_top, imagesx($newimg), imagesy($newimg));
//finally, output the image
header("Content-Type: image/jpeg");
imagejpeg($newimg);
My old example, that assumes an identical "border" on all sides of the image, just to clarify the comments :)
我的旧示例,假设图像的所有侧面都有相同的“边框”,只是为了澄清评论:)
//load the image
$img = imagecreatefromjpeg("img.jpg");
//find the size of the border.
$border = 0;
while(imagecolorat($img, $border, $border) == 0xFFFFFF) {
$border++;
}
//copy the contents, excluding the border
//This code assumes that the border is the same size on all sides of the image.
$newimg = imagecreatetruecolor(imagesx($img)-($border*2), imagesy($img)-($border*2));
imagecopy($newimg, $img, 0, 0, $border, $border, imagesx($newimg), imagesy($newimg));
//finally, if you want, overwrite the original image
imagejpeg($newimg, "img.jpg");
回答by skibulk
Gnud's script redundantly calls imagesx and imagesy. It also iterates every pixel on every side, even when the corners overlap. This improved version eliminates redundant function calls and checks every pixel only once, granting a significant increase in speed. The function returns a status ($result['#']) equal to 2 if every pixel is the trimmed.
Gnud 的脚本多余地调用了imagesx 和imagesy。它还会迭代每一边的每个像素,即使在角重叠时也是如此。这个改进版本消除了冗余的函数调用,并且每个像素只检查一次,从而显着提高了速度。如果每个像素都被修剪,则该函数返回等于 2 的状态 ($result['#'])。
example();
function example(){
$img = imagecreatefromjpeg("http://ecx.images-amazon.com/images/I/413XvF0yukL._SL500_AA280_.jpg");
// find the trimmed image border
$box = imageTrimBox($img);
// copy cropped portion
$img2 = imagecreate($box['w'], $box['h']);
imagecopy($img2, $img, 0, 0, $box['l'], $box['t'], $box['w'], $box['h']);
// output cropped image to the browser
header('Content-Type: image/png');
imagepng($img2);
imagedestroy($img);
imagedestroy($img2);
}
function imageTrimBox($img, $hex=null){
if (!ctype_xdigit($hex)) $hex = imagecolorat($img, 0,0);
$b_top = $b_lft = 0;
$b_rt = $w1 = $w2 = imagesx($img);
$b_btm = $h1 = $h2 = imagesy($img);
do {
//top
for(; $b_top < $h1; ++$b_top) {
for($x = 0; $x < $w1; ++$x) {
if(imagecolorat($img, $x, $b_top) != $hex) {
break 2;
}
}
}
// stop if all pixels are trimmed
if ($b_top == $b_btm) {
$b_top = 0;
$code = 2;
break 1;
}
// bottom
for(; $b_btm >= 0; --$b_btm) {
for($x = 0; $x < $w1; ++$x) {
if(imagecolorat($img, $x, $b_btm-1) != $hex) {
break 2;
}
}
}
// left
for(; $b_lft < $w1; ++$b_lft) {
for($y = $b_top; $y <= $b_btm; ++$y) {
if(imagecolorat($img, $b_lft, $y) != $hex) {
break 2;
}
}
}
// right
for(; $b_rt >= 0; --$b_rt) {
for($y = $b_top; $y <= $b_btm; ++$y) {
if(imagecolorat($img, $b_rt-1, $y) != $hex) {
break 2;
}
}
}
$w2 = $b_rt - $b_lft;
$h2 = $b_btm - $b_top;
$code = ($w2 < $w1 || $h2 < $h1) ? 1 : 0;
} while (0);
// result codes:
// 0 = Trim Zero Pixels
// 1 = Trim Some Pixels
// 2 = Trim All Pixels
return array(
'#' => $code, // result code
'l' => $b_lft, // left
't' => $b_top, // top
'r' => $b_rt, // right
'b' => $b_btm, // bottom
'w' => $w2, // new width
'h' => $h2, // new height
'w1' => $w1, // original width
'h1' => $h1, // original height
);
}
回答by Bill H
I know this is pretty old but if you have ImageMagick enabled you can use this method
我知道这已经很老了,但是如果您启用了 ImageMagick,则可以使用此方法
回答by SteJ
PHP's gd library has the imagecropautofunction (PHP version 5.5+):
PHP 的 gd 库有如下imagecropauto功能(PHP 5.5+ 版本):
<?php
$img=imagecreatefrompng("tux.png"); // Load and instantiate the image
if($img) {
$cropped=imagecropauto($img,IMG_CROP_DEFAULT); // Auto-crop the image
imagedestroy($img); // Clean up as $img is no longer needed
header("Content-type: image/png"); // Set the appropriate header so the browser
// knows how to present it
imagepng($cropped); // Return the newly cropped image
}
By default imagecropautowill try to crop using transparency, and then fall back on using the 4 corners of the image to attempt to detect the background to crop; I have also had success with the following constants in place of IMG_CROP_AUTOin the example above:
默认情况下imagecropauto将尝试使用透明度进行裁剪,然后使用图像的 4 个角尝试检测要裁剪的背景;我还成功地使用了以下常量代替了IMG_CROP_AUTO上面的示例:
IMG_CROP_BLACK- Useful for images with a black backgroundIMG_CROP_WHITE- Useful for images with a white backgroundIMG_CROP_THRESHOLD- Allows you to set a colour and threshold to use when cropping
IMG_CROP_BLACK- 适用于黑色背景的图像IMG_CROP_WHITE- 适用于白色背景的图像IMG_CROP_THRESHOLD- 允许您设置裁剪时使用的颜色和阈值
回答by Mark Manning
I realize this is quite old but I have a slightly different take on trimming an image via GD. Instead of doing just one side at a time - do all four. It is faster and less expensive cpu-wise in some ways. However, if you stop the FOR loops the moment you find the top-bottom-left-right sides - that is faster than this.
我意识到这已经很老了,但我对通过 GD 修剪图像的看法略有不同。而不是一次只做一侧 - 做所有四个。在某些方面,它在 CPU 方面更快、更便宜。但是,如果您在找到上下左右两侧的那一刻停止 FOR 循环 - 这比这快。
So first there is:
所以首先是:
#
# Do all four sides at once
#
echo "Finding the top-left-bottom-right edges of the image...please wait.\n";
$top = 99999;
$bot = -99999;
$left = 99999;
$right = -99999;
for( $x=$offset; $x<($w-$offset); $x++ ){
for( $y=$offset; $y<($h-$offset); $y++ ){
$rgb = imagecolorat( $gd, $x, $y );
if( $color != $rgb ){
$left = ($x < $left) ? $x : $left;
$right = ($x > $right) ? $x : $right;
$top = ($y < $top) ? $y : $top;
$bot = ($y > $bot) ? $y : $bot;
}
}
}
and then there is:
然后是:
#
# Top
#
echo "Finding the top of the image\n";
$top = null;
for( $y=$offset; $y<($h-$offset); $y++ ){
for( $x=$offset; $x<($w-$offset); $x++ ){
$rgb = imagecolorat( $gd, $x, $y );
if( $color != $rgb ){ $top = $y; break; }
}
if( !is_null($top) ){ break; }
}
#
# Bottom
#
echo "Finding the bottom of the image\n";
$bot = null;
for( $y=($h-$offset); $y>$offset; $y-- ){
for( $x=$offset; $x<($w-$offset); $x++ ){
$rgb = imagecolorat( $gd, $x, $y );
if( $color != $rgb ){ $bot = $y; break; }
}
if( !is_null($bot) ){ break; }
}
#
# Left
#
echo "Finding the left of the image\n";
$left = null;
for( $x=$offset; $x<($w-$offset); $x++ ){
for( $y=$offset; $y<($h-$offset); $y++ ){
$rgb = imagecolorat( $gd, $x, $y );
if( $color != $rgb ){ $left = $x; break; }
}
if( !is_null($left) ){ break; }
}
#
# right
#
echo "Finding the right of the image\n";
$right = null;
for( $x=($w-$offset); $x>$offset; $x-- ){
for( $y=$offset; $y<($h-$offset); $y++ ){
$rgb = imagecolorat( $gd, $x, $y );
if( $color != $rgb ){ $right = $x; break; }
}
if( !is_null($right) ){ break; }
}
In both cases, the $color variable contains the first color dot in the image:
在这两种情况下, $color 变量都包含图像中的第一个颜色点:
$color = imagecolorat( $gd, 0, 0 );
This is because in GIF images - the first dot is 99% of the time the transparent (or background) color. Also, the $offset is (for me) a way to say I know the image is only going to be so wide and so high. So if I draw something that is only a maximum of 256 by 256 but I put it on a 1024 x 1024 background I can whack off some of that background and make an offset of 255 thus making the FOR loops only go from 255 to (1024-255) or 769.
这是因为在 GIF 图像中 - 第一个点在 99% 的时间是透明(或背景)颜色。此外,$offset 是(对我而言)一种方式,表示我知道图像只会变得如此宽和如此高。所以如果我画的东西最多只有 256 x 256,但我把它放在 1024 x 1024 的背景上,我可以去掉一些背景并制作 255 的偏移量,从而使 FOR 循环只从 255 到 (1024 -255) 或 769。
Ok - before someone asks - WHY I would do such a thing - Because some fonts (like Bastarda) don't have the correct font information in them and a 256pt output of the letter "z" produces an image where the bottom of the "z" goes past 256 (down to something like 512) so in order to get the entire image you have to start (or end) farther down than what you'd think the font would go. So I split the difference and whack off 255 pixels from either end. This was after actually seeing that Bastarda does this.
好的 - 在有人问之前 - 为什么我会做这样的事情 - 因为某些字体(如 Bastarda)中没有正确的字体信息,并且字母“z”的 256pt 输出会生成一个图像,其中“ z" 超过 256(下降到 512 之类的),因此为了获得整个图像,您必须开始(或结束)比您认为字体会走的更远的位置。所以我分开了差异并从两端敲掉了 255 个像素。这是在实际看到巴斯塔达这样做之后。
Some additional notes:
一些补充说明:
1. PNG images you CAN set up to be like GIF images but normally you will have to specify what the background color is going to be.
2. JPEG images do NOT uncompress the exact same way each time. So even comparing the same image you loaded twice might not work the same and may give different sizes.
3. These routines work best on simple black and white (or two color) images. Multiple colors can throw these routines off. Especially if you decide to use tolerances.
4. To use tolerances to determine if you have found the edge of an image, all you have to do is to pre-compute both the high and low tolerance (ie: if you have a tolerance of five(5) on the red component, then you can compute the tolerance as EITHER X-5-to-x+5 OR x-2.5-to-x+2.5 depending upon if you want the tolerance to be the WHOLE range or just the +/- range). You can have a tolerance for the RED, GREEN, BLUE, and ALPHA parts of the color or the entire color itself. So there are several different tolerances you can compute if you want and all of them are the correct way to do it depending upon your needs.
1. 您可以将 PNG 图像设置为类似于 GIF 图像,但通常您必须指定背景颜色。
2. JPEG 图像不会每次都以完全相同的方式解压缩。因此,即使比较您加载两次的同一张图片,效果也可能不同,并且可能会给出不同的尺寸。
3. 这些例程最适用于简单的黑白(或两种颜色)图像。多种颜色可以摆脱这些常规。特别是如果您决定使用公差。
4. 要使用容差来确定您是否找到了图像的边缘,您所要做的就是预先计算高容差和低容差(即:如果您对红色分量的容差为五(5) ,然后您可以将公差计算为 X-5-to-x+5 或 x-2.5-to-x+2.5,具体取决于您希望公差是整个范围还是 +/- 范围)。您可以对颜色的 RED、GREEN、BLUE 和 ALPHA 部分或整个颜色本身设置容差。因此,您可以根据需要计算几种不同的容差,并且所有这些容差都是根据您的需要进行计算的正确方法。
回答by jheddings
Check out the ImageMagicklibrary in PHP. It has good methods of working with and manipulating images (including crop).
查看PHP 中的ImageMagick库。它具有处理和操作图像(包括crop)的好方法。
You'll have to figure out where the "whitespace" is around the image. It could be challenging, since "whitespace" could be the color white, some other color, transparency, etc...
您必须弄清楚图像周围的“空白”在哪里。这可能具有挑战性,因为“空白”可能是白色、其他颜色、透明度等......

