开发者

PHP: How to detect if a jpeg is completely black (no image)?

I built a community website where the users upload their photo and they can crop the thumbnail, ala Facebook to be clear.

But for some reason some of them generate a blan开发者_高级运维k (actually black jpeg thumbnail).

I tried different solutions but it sounds like this is happening with big images or maybe in computers where JS is not enabled? I don't know...

Bottom line, since the number of users with this is very tiny, I was thinking of creating a patch: detect when the user generates a blank jpeg.Then I would be able to warn them.

Do you know how to do it?


Resize it to a 1x1 PX image. Then check if the one pixel is black. Not perfect, but if there is a significant amount of non black in the original, that 1px will not be black. The benefit is that it will be fast.


I think there is no faster way than walking though each pixel using imagecolorat().

As to why this happens, it could be because of CMYK JPG files that GD can't digest. Not sure - you would have to try out. If that is the reason, you could detect CMYK using getimagesize() and its channels info.


I like this random algoritm,

function check_if_black($src){

    $img = imagecreatefromjpeg($src);
    list($width_orig, $height_orig)=getimagesize($src);
    for($i=0;$i<20;$i++){
        $rand_width=rand ( 0 , $width_orig );
        $rand_height=rand ( 0 , $height_orig );
        $rgb = imagecolorat($img, $rand_width, $rand_height);
        if($rgb!=0){
            return "not black";
        }
    }
    return "black";
}

Even if 80% of the image is black it has 99% to succeed. you can also increase the loop for better percentage.


Assuming its a valid black image you could:

long way - scan pixel by pixel for the colour black

short way (if its the same black image always) - compare file byte by byte (or md5 hash) to a sample black image


Here is my code:

<?php

namespace App\Traits;

trait BlacknessCheck {

    public static function fileIsBlack($filename, $throw_error = false) {

        if (exif_imagetype($filename) === IMAGETYPE_JPEG) {

            return self::imageIsBlack(imagecreatefromjpeg($filename));

        } elseif ($isimage === IMAGETYPE_PNG) {

            return self::imageIsBlack(imagecreatefrompng($filename));

        } else {

            if ($throw_error) {
                throw new \Exception("Provided file is not an image.");
            } else {
                return false;
            }

        }

    }

    public static function imageIsBlack($image) {

        $scaled_image = self::scaleImage($image);

        $added_colors =
            imagecolorat($scaled_image, 0, 0) +
            imagecolorat($scaled_image, 0, 1) +
            imagecolorat($scaled_image, 0, 2) +
            imagecolorat($scaled_image, 1, 0) +
            imagecolorat($scaled_image, 1, 1) +
            imagecolorat($scaled_image, 1, 2) +
            imagecolorat($scaled_image, 2, 0) +
            imagecolorat($scaled_image, 2, 1) +
            imagecolorat($scaled_image, 2, 2);

        imagedestroy($scaled_image);
        imagedestroy($image);

        return ($added_colors === 0);

    }

    public static function scaleImage($image) {
        $newimage = imagecreatetruecolor(3, 3);
        imagecopyresampled(
            $newimage, $image,
            0, 0, 0, 0,
            3, 3, imagesx($image), imagesy($image)
        );
        return $newimage;
    }

}

I'm using it to remove black thumbs.

private function removeBlackness() {

    $files = $this->glob_recursive(public_path('img/collection/*.jpg'));
    foreach ($files as $filename) {

        if (self::fileIsBlack($filename)) {
            unlink($filename);
        }

    }

}


If it's all black, the file size will be low, as it's compressed. I know JPG uses perceptual rather than RLE, but it will still be smaller. Why not disallow JPGs under 20k for example? Make a few examples, compare the sizes, set your thresholds.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜