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.
精彩评论