【问题标题】:Crop whitespace from image in PHP在 PHP 中从图像中裁剪空白
【发布时间】:2010-12-12 18:49:51
【问题描述】:

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

注意:澄清一下,我的意思是类似 Photoshop 的修剪功能。

谢谢。

【问题讨论】:

    标签: php image gd crop


    【解决方案1】:

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

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

    我的旧示例假定图像的所有边都有相同的“边框”,只是为了澄清 cmets :)

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

    【讨论】:

    • 很好的例子...正如您所指出的(只是为了澄清),这假定图像周围有一个固定大小的白色边框。
    • 您好,边框不是固定大小的。我正在考虑类似 Photoshop 的修剪功能。
    • 此代码不假设边框的固定大小(例如,所有边框都是 14px),但它假设边框在图像的所有边上的大小相同。不过,您可以将此作为起点。请记住,检查所有侧面的所有像素会变慢 - 不要每次显示图像时都这样做,当用户第一次上传时这样做:)
    • @gnud:我明白你的意思,你的意思是单独计算每一侧的白色边框。上面的代码是从页面的顶部还是左侧计算边框?
    • 这就是我要找的。​​span>
    【解决方案2】:

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

    【讨论】:

    • 谢谢。我在尝试访问范围之外的像素时遇到了一些问题。就像图像是 1500 像素宽一样,它会尝试调用 imagecolorat($img, 1500, 5); $b_rt & $b_btm 应该分别比宽度和高度小 1。
    • 上述脚本效果很好,但会从某些图像中去除颜色。想知道为什么会这样。
    • @SunilChandurkar 该函数采用可选的颜色参数“$hex”。如果指定,这就是将被裁剪的内容。您可以尝试使用 0xFFFFFF 来裁剪纯白色。如果您不指定颜色,它将使用图像左上角像素的颜色裁剪图像。如果图像被分成两种颜色,上半部分为纯红色,下半部分为纯蓝色,则红色部分将被裁剪掉。一种不太激进的方法需要检查所有四个角,并且只有在它们相同时才进行裁剪。更激进的方法是多次裁剪,每个角颜色一次。
    【解决方案3】:

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

    Trim Image

    【讨论】:

    • Imagick::trimImage 现在有一个 fuzz 参数:“默认情况下,目标必须完全匹配特定的像素颜色。但是,在许多情况下,两种颜色可能相差很小。图像的 fuzz 成员定义将两种颜色视为相同的可接受程度。此参数表示量子范围的变化。”
    • 我想从图像中去除白色边框,因为它在非白色背景(红色、黑色等)上看起来很难看。 Php 的 Imagick 库只有 Imagick-> trimImage() 函数,它只去除与背景相同的边框 - 对我来说没用。
    • 尝试使用 Imagick::shave()。只要边界都是平等的,那应该会有所帮助。
    【解决方案4】:

    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 - 允许您设置裁剪时使用的颜色和阈值

    【讨论】:

      【解决方案5】:

      我意识到这已经很老了,但我对通过 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 部分或整个颜色本身设置容差。因此,您可以根据需要计算几种不同的容差,并且根据您的需要,所有这些都是正确的方法。

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

        • 我想从图像中去除白色边框,因为它在非白色背景(红色、黑色等)上看起来很难看。 Php 的 Imagick 库只有 Imagick-> trimImage() 函数,它只去除与背景相同的边框 - 对我来说没用。
        猜你喜欢
        • 2018-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多