如何使用GD检查PNG图像是否具有透明度?

发布于 2024-10-27 16:43:39 字数 41 浏览 7 评论 0原文

如何使用 PHP 的 GD 扩展检查 PNG 图像是否具有透明像素?

How do I check if a PNG image has transparent pixels using PHP's GD extension?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

不美如何 2024-11-03 16:43:39

我知道这已经很旧了,但我刚刚在 PHP 文档的评论中发现了这一点。 (链接

这里是确定 PNG 图像是否包含 Alpha 的函数:

<前><代码>

PNG 图像的颜色类型存储在字节偏移量 25 处。第 25 个字节的可能值为:

  • 0 - 灰度
  • 2 - RGB
  • 3 - RGB 带调色板
  • 4 - 灰度 + Alpha
  • 6 - RGB + Alpha

仅适用于 PNG 图像。

I know this is old, but I just found this on the comments of the PHP docs. (link)

Here is the function which determines whether the PNG image contains alpha or not:

<?php
    function is_alpha_png($fn){
      return (ord(@file_get_contents($fn, NULL, NULL, 25, 1)) == 6);
    }
?>

The color type of PNG image is stored at byte offset 25. Possible values of that 25'th byte is:

  • 0 - greyscale
  • 2 - RGB
  • 3 - RGB with palette
  • 4 - greyscale + alpha
  • 6 - RGB + alpha

Only works for PNG images though.

離人涙 2024-11-03 16:43:39

看起来似乎无法一眼看出透明度。

imagecolorat 手册页上的注释建议处理真彩色图像时得到的整数实际上总共可以移动四次,其中第四个是 Alpha 通道(其他三个是红色、绿色和蓝色)。因此,给定 $x$y 处的任何像素位置,您可以使用以下方法检测 alpha:

$rgba = imagecolorat($im,$x,$y);
$alpha = ($rgba & 0x7F000000) >> 24;
$red = ($rgba & 0xFF0000) >> 16;
$green = ($rgba & 0x00FF00) >> 8;
$blue = ($rgba & 0x0000FF);

127 的 $alpha 显然是完全透明的,而零是完全不透明的。

不幸的是,您可能需要处理图像中的每一个像素才能找到透明的像素,而这只适用于真彩色图像。否则 imagecolorat 返回一个颜色索引,您必须使用 imagecolorsforindex,它实际上返回一个带有 alpha 值的数组。

It doesn't look like you can detect transparency at a glance.

The comments on the imagecolorat manual page suggest that the resulting integer when working with a true-color image can actually be shifted four times total, with the fourth being the alpha channel (the other three being red, green and blue). Therefore, given any pixel location at $x and $y, you can detect alpha using:

$rgba = imagecolorat($im,$x,$y);
$alpha = ($rgba & 0x7F000000) >> 24;
$red = ($rgba & 0xFF0000) >> 16;
$green = ($rgba & 0x00FF00) >> 8;
$blue = ($rgba & 0x0000FF);

An $alpha of 127 is apparently completely transparent, while zero is completely opaque.

Unfortunately you might need to process every single pixel in the image just to find one that is transparent, and then this only works with true-color images. Otherwise imagecolorat returns a color index, which you must then look up using imagecolorsforindex, which actually returns an array with an alpha value.

时光清浅 2024-11-03 16:43:39

我知道这是一个旧线程,但在我看来它需要改进,因为通过检查所有像素来遍历一个巨大的 png 却发现它不透明是浪费时间。因此,经过一番谷歌搜索后,我发现 Jon Fox 的博客 和我在 W3C PNG 规范<的帮助下改进了他的代码/a> 进一步可靠、快速并具有最小的内存印记:

function IsTransparentPng($File){
    //32-bit pngs
    //4 checks for greyscale + alpha and RGB + alpha
    if ((ord(file_get_contents($File, false, null, 25, 1)) & 4)>0){
        return true;
    }
    //8 bit pngs
    $fd=fopen($File, 'r');
    $continue=true;
    $plte=false;
    $trns=false;
    $idat=false;
    while($continue===true){
        $continue=false;
        $line=fread($fd, 1024);
        if ($plte===false){
            $plte=(stripos($line, 'PLTE')!==false);
        }
        if ($trns===false){
            $trns=(stripos($line, 'tRNS')!==false);
        }
        if ($idat===false){
            $idat=(stripos($line, 'IDAT')!==false);
        }
        if ($idat===false and !($plte===true and $trns===true)){
            $continue=true;
        }
    }
    fclose($fd);
    return ($plte===true and $trns===true);
}

I know this is an old thread, but in my opinion it needs improvement since walking through a huge png by checking all pixels only to find out it is not transparent is a waste of time. So after some googleing I found Jon Fox's Blog and I improved his code with the help of the W3C PNG Specification further to be reliable, fast and have a minimum on memory imprint:

function IsTransparentPng($File){
    //32-bit pngs
    //4 checks for greyscale + alpha and RGB + alpha
    if ((ord(file_get_contents($File, false, null, 25, 1)) & 4)>0){
        return true;
    }
    //8 bit pngs
    $fd=fopen($File, 'r');
    $continue=true;
    $plte=false;
    $trns=false;
    $idat=false;
    while($continue===true){
        $continue=false;
        $line=fread($fd, 1024);
        if ($plte===false){
            $plte=(stripos($line, 'PLTE')!==false);
        }
        if ($trns===false){
            $trns=(stripos($line, 'tRNS')!==false);
        }
        if ($idat===false){
            $idat=(stripos($line, 'IDAT')!==false);
        }
        if ($idat===false and !($plte===true and $trns===true)){
            $continue=true;
        }
    }
    fclose($fd);
    return ($plte===true and $trns===true);
}
踏雪无痕 2024-11-03 16:43:39

这是可以做到的!

我已将所有答案和评论合并到一个函数中,该函数应该是快速且可靠的。可靠的:

function hasAlpha($imgdata) {
    $w = imagesx($imgdata);
    $h = imagesy($imgdata);

    if($w>50 || $h>50){ //resize the image to save processing if larger than 50px:
        $thumb = imagecreatetruecolor(10, 10);
        imagealphablending($thumb, FALSE);
        imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 10, 10, $w, $h );
        $imgdata = $thumb;
        $w = imagesx($imgdata);
        $h = imagesy($imgdata);
    }
    //run through pixels until transparent pixel is found:
    for($i = 0; $i<$w; $i++) {
        for($j = 0; $j < $h; $j++) {
            $rgba = imagecolorat($imgdata, $i, $j);
            if(($rgba & 0x7F000000) >> 24) return true;
        }
    }
    return false;
}

//SAMPLE USE:
hasAlpha( imagecreatefrompng("myfile.png") );   //returns true if img has transparency

It can be done!

I've combined all answers and comments into one function which should be fast & reliable:

function hasAlpha($imgdata) {
    $w = imagesx($imgdata);
    $h = imagesy($imgdata);

    if($w>50 || $h>50){ //resize the image to save processing if larger than 50px:
        $thumb = imagecreatetruecolor(10, 10);
        imagealphablending($thumb, FALSE);
        imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 10, 10, $w, $h );
        $imgdata = $thumb;
        $w = imagesx($imgdata);
        $h = imagesy($imgdata);
    }
    //run through pixels until transparent pixel is found:
    for($i = 0; $i<$w; $i++) {
        for($j = 0; $j < $h; $j++) {
            $rgba = imagecolorat($imgdata, $i, $j);
            if(($rgba & 0x7F000000) >> 24) return true;
        }
    }
    return false;
}

//SAMPLE USE:
hasAlpha( imagecreatefrompng("myfile.png") );   //returns true if img has transparency
翻了热茶 2024-11-03 16:43:39

非常简单的forward函数,它会检查图像中是否有透明像素,如果有,则返回true。

$im = imagecreatefrompng('./transparent.png');
if(check_transparent($im)) {
    echo 'DA';
}
else {
    echo 'NU';
}

function check_transparent($im) {

    $width = imagesx($im); // Get the width of the image
    $height = imagesy($im); // Get the height of the image

    // We run the image pixel by pixel and as soon as we find a transparent pixel we stop and return true.
    for($i = 0; $i < $width; $i++) {
        for($j = 0; $j < $height; $j++) {
            $rgba = imagecolorat($im, $i, $j);
            if(($rgba & 0x7F000000) >> 24) {
                return true;
            }
        }
    }

    // If we dont find any pixel the function will return false.
    return false;
}

Pretty strait forward function, it will check if there is any transparent pixel in the image, if it is, it will return true.

$im = imagecreatefrompng('./transparent.png');
if(check_transparent($im)) {
    echo 'DA';
}
else {
    echo 'NU';
}

function check_transparent($im) {

    $width = imagesx($im); // Get the width of the image
    $height = imagesy($im); // Get the height of the image

    // We run the image pixel by pixel and as soon as we find a transparent pixel we stop and return true.
    for($i = 0; $i < $width; $i++) {
        for($j = 0; $j < $height; $j++) {
            $rgba = imagecolorat($im, $i, $j);
            if(($rgba & 0x7F000000) >> 24) {
                return true;
            }
        }
    }

    // If we dont find any pixel the function will return false.
    return false;
}
秋凉 2024-11-03 16:43:39

这就是我检测 8-32 位透明度的方法。它只适用于 PNG。

function detect_transparency($file){

    if(!@getimagesize($file)) return false;

    if(ord(file_get_contents($file, false, null, 25, 1)) & 4) return true;

    $content = file_get_contents($file);
    if(stripos($content,'PLTE') !== false && stripos($content, 'tRNS') !== false) return true;

    return false;
}

This is how I detect 8-32 bit transparency. It only work with PNG's.

function detect_transparency($file){

    if(!@getimagesize($file)) return false;

    if(ord(file_get_contents($file, false, null, 25, 1)) & 4) return true;

    $content = file_get_contents($file);
    if(stripos($content,'PLTE') !== false && stripos($content, 'tRNS') !== false) return true;

    return false;
}
南冥有猫 2024-11-03 16:43:39

cronoklee的功能很好,但是我在使用的时候发现了一个bug。它不适用于 8 位调色板的图像。这是固定的变体:

public function hasAlpha($imgdata)
{
    $w = imagesx($imgdata);
    $h = imagesy($imgdata);

    if($w>100 || $h>100){ //resize the image to save processing
        $thumb = imagecreatetruecolor(100, 100);
        imagealphablending($thumb, FALSE);
        imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 100, 100, $w, $h );
        $imgdata = $thumb;
        $w = imagesx($imgdata);
        $h = imagesy($imgdata);
    }
    //run through pixels until transparent pixel is found:
    for($i = 0; $i<$w; $i++) {
        for($j = 0; $j < $h; $j++) {
            $ci = imagecolorat($imgdata, $i, $j);
            $rgba = imagecolorsforindex($imgdata, $ci);
            if($rgba['alpha']) { return true; }
        }
    }
    return false;
}

cronoklee's function is very good, but when I was using it I found a bug. It does not work for images with 8 bit pallet. Here is the fixed variant:

public function hasAlpha($imgdata)
{
    $w = imagesx($imgdata);
    $h = imagesy($imgdata);

    if($w>100 || $h>100){ //resize the image to save processing
        $thumb = imagecreatetruecolor(100, 100);
        imagealphablending($thumb, FALSE);
        imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 100, 100, $w, $h );
        $imgdata = $thumb;
        $w = imagesx($imgdata);
        $h = imagesy($imgdata);
    }
    //run through pixels until transparent pixel is found:
    for($i = 0; $i<$w; $i++) {
        for($j = 0; $j < $h; $j++) {
            $ci = imagecolorat($imgdata, $i, $j);
            $rgba = imagecolorsforindex($imgdata, $ci);
            if($rgba['alpha']) { return true; }
        }
    }
    return false;
}
凉城已无爱 2024-11-03 16:43:39

改进了cronoklee的功能。删除了每个像素不必要的位移位,减少了漏报计数,在功能描述中添加了解释。

/**
 * Estimates, if image has pixels with transparency. It shrinks image to 64 times smaller
 * size, if necessary, and searches for the first pixel with non-zero alpha byte.
 * If image has 1% opacity, it will be detected. If any block of 8x8 pixels has at least
 * one semi-opaque pixel, the block will trigger positive result. There are still cases,
 * where image with hardly noticeable transparency will be reported as non-transparent,
 * but it's almost always safe to fill such image with monotonic background.
 *
 * Icons with size <= 64x64 (or having square <= 4096 pixels) are fully scanned with
 * absolutely reliable result.
 *
 * @param  resource $image
 * @return bool
 */
function hasTransparency ($image): bool {
  if (!is_resource($image)) {
    throw new \InvalidArgumentException("Image resource expected. Got: " . gettype($image));
  }

  $shrinkFactor      = 64.0;
  $minSquareToShrink = 64.0 * 64.0;

  $width  = imagesx($image);
  $height = imagesy($image);
  $square = $width * $height;

  if ($square <= $minSquareToShrink) {
    [$thumb, $thumbWidth, $thumbHeight] = [$image, $width, $height];
  } else {
    $thumbSquare = $square / $shrinkFactor;
    $thumbWidth  = (int) round($width / sqrt($shrinkFactor));
    $thumbWidth < 1 and $thumbWidth = 1;
    $thumbHeight = (int) round($thumbSquare / $thumbWidth);
    $thumb       = imagecreatetruecolor($thumbWidth, $thumbHeight);
    imagealphablending($thumb, false);
    imagecopyresized($thumb, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $width, $height);
  }

  for ($i = 0; $i < $thumbWidth; $i++) { 
    for ($j = 0; $j < $thumbHeight; $j++) {
      if (imagecolorat($thumb, $i, $j) & 0x7F000000) {
        return true;
      }
    }
  }

  return false;
}

用法:

hasTransparency( imagecreatefrompng("myfile.png") );   //returns true if img has transparency

Improved cronoklee's function. Removed unnecessary bit shifting for each pixel, reduced false negatives count, added explanation in function description.

/**
 * Estimates, if image has pixels with transparency. It shrinks image to 64 times smaller
 * size, if necessary, and searches for the first pixel with non-zero alpha byte.
 * If image has 1% opacity, it will be detected. If any block of 8x8 pixels has at least
 * one semi-opaque pixel, the block will trigger positive result. There are still cases,
 * where image with hardly noticeable transparency will be reported as non-transparent,
 * but it's almost always safe to fill such image with monotonic background.
 *
 * Icons with size <= 64x64 (or having square <= 4096 pixels) are fully scanned with
 * absolutely reliable result.
 *
 * @param  resource $image
 * @return bool
 */
function hasTransparency ($image): bool {
  if (!is_resource($image)) {
    throw new \InvalidArgumentException("Image resource expected. Got: " . gettype($image));
  }

  $shrinkFactor      = 64.0;
  $minSquareToShrink = 64.0 * 64.0;

  $width  = imagesx($image);
  $height = imagesy($image);
  $square = $width * $height;

  if ($square <= $minSquareToShrink) {
    [$thumb, $thumbWidth, $thumbHeight] = [$image, $width, $height];
  } else {
    $thumbSquare = $square / $shrinkFactor;
    $thumbWidth  = (int) round($width / sqrt($shrinkFactor));
    $thumbWidth < 1 and $thumbWidth = 1;
    $thumbHeight = (int) round($thumbSquare / $thumbWidth);
    $thumb       = imagecreatetruecolor($thumbWidth, $thumbHeight);
    imagealphablending($thumb, false);
    imagecopyresized($thumb, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $width, $height);
  }

  for ($i = 0; $i < $thumbWidth; $i++) { 
    for ($j = 0; $j < $thumbHeight; $j++) {
      if (imagecolorat($thumb, $i, $j) & 0x7F000000) {
        return true;
      }
    }
  }

  return false;
}

Usage:

hasTransparency( imagecreatefrompng("myfile.png") );   //returns true if img has transparency
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文