29

是否可以在 PHP 中删除图像周围的空白?

注意:澄清我的意思是像photoshops修剪功能。

谢谢。

4

7 回答 7

57

要修剪所有空白,如您所说,围绕图像的有趣部分,首先我们找出“空白”停止的位置,然后我们复制这些边界内的所有内容。

//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);

我的旧示例,假设图像的所有侧面都有相同的“边框”,只是为了澄清评论:)

//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");
于 2009-11-03T19:51:14.043 回答
13

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
);
}
于 2012-08-23T13:11:34.403 回答
9

我知道这已经很老了,但如果你启用了 ImageMagick,你可以使用这个方法

修剪图像

于 2010-08-26T22:20:13.043 回答
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
}

默认情况下imagecropauto会尝试使用透明度进行裁剪,然后使用图像的 4 个角来尝试检测要裁剪的背景;IMG_CROP_AUTO在上面的示例中,我还成功地使用了以下常量:

  • IMG_CROP_BLACK- 适用于黑色背景的图像
  • IMG_CROP_WHITE- 适用于白色背景的图像
  • IMG_CROP_THRESHOLD- 允许您设置裁剪时使用的颜色和阈值
于 2017-11-14T23:58:43.617 回答
2

我意识到这已经很老了,但我对通过 GD 修剪图像的看法略有不同。而不是一次只做一侧 - 做所有四个。在某些方面,它在 cpu 方面更快且更便宜。但是,如果您在找到顶部-底部-左侧-右侧的那一刻停止 FOR 循环 - 这比这更快。

所以首先有:

#
#   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;
                    }
                }
            }

然后是:

#
#   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; }
                }

在这两种情况下,$color 变量都包含图像中的第一个颜色点:

$color = imagecolorat( $gd, 0, 0 );

这是因为在 GIF 图像中 - 第一个点 99% 的时间是透明(或背景)颜色。此外,$offset(对我而言)是一种表示我知道图像只会如此宽和如此高的方式。因此,如果我画的东西最多只有 256 x 256,但我把它放在 1024 x 1024 背景上,我可以去掉一些背景并制作 255 的偏移量,从而使 FOR 循环仅从 255 变为(1024 -255) 或 769。

好的-在有人问之前-为什么我会做这样的事情-因为某些字体(例如 Bastarda)中没有正确的字体信息,并且字母“z”的 256pt 输出会产生一个图像,其中“ z" 超过 256(下降到 512 之类的值),因此为了获得整个图像,您必须开始(或结束)比您认为字体会走的更远的位置。因此,我将差异分开并从两端敲出 255 个像素。这是在实际看到巴斯塔达这样做之后。

一些附加说明:

1. 您可以将 PNG 图像设置为 GIF 图像,但通常您必须指定背景颜色。
2. JPEG 图像不会每次都以完全相同的方式解压缩。因此,即使比较您两次加载的同一张图像,也可能会产生不同的效果,并且可能会给出不同的尺寸。
3. 这些例程最适用于简单的黑白(或两种颜色)图像。多种颜色可以摆脱这些常规。特别是如果您决定使用公差。
4. 要使用容差来确定您是否找到了图像的边缘,您所要做的就是预先计算高容差和低容差(即:如果红色分量的容差为五(5) ,那么您可以将公差计算为 X-5-to-x+5 或 x-2.5-to-x+2.5,具体取决于您希望公差是整个范围还是仅 +/- 范围)。您可以对颜色的 RED、GREEN、BLUE 和 ALPHA 部分或整个颜色本身设置容差。因此,如果需要,您可以计算几种不同的公差,并且根据您的需要,所有这些都是正确的方法。

于 2015-12-29T07:23:51.107 回答
1

查看 PHP 中的ImageMagick库。它具有处理和操作图像(包括裁剪)的良好方法。

您必须弄清楚图像周围的“空白”在哪里。这可能具有挑战性,因为“空白”可能是白色、其他颜色、透明度等......

于 2009-11-03T19:50:30.817 回答
0

假设您有边框颜色为 0x212121 的图像。您想使用 PHP 自动修剪此边框。你可以用这样的代码来做到这一点:

// Load image
$img1='input.png';
$finfo = getimagesize($img1);
$image_old = $finfo['mime']=='image/png'?imagecreatefrompng($img1):imagecreatefromjpeg($img1);
// !! REMOVE BORDER !!
$cropped = imagecropauto($image_old , IMG_CROP_THRESHOLD, 1, 0x212121);
// Save image
imagepng($cropped, 'output.png');
于 2022-01-17T16:50:05.763 回答