Detect if image is color, grayscale or black and white with Python/PIL

后端 未结 5 715
青春惊慌失措
青春惊慌失措 2020-12-24 09:06

I extract pages images from a PDF file in jpeg format and I need to determine if each image is much more grayscale, color ou black and white (with a tolerance factor).

5条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-24 09:52

    I tried Gepeto's solution and it has a lot of false positives since the color grand variances can be similar just by chance. The correct way to do this is to calculate the variance per pixel. Shrink down the image first so you don't have to process millions of pixels.

    By default this function also uses a mean color bias adjustment, which I find improves the prediction. A side effect of this is that it will also detect monochrome but non-grayscale images (typically sepia-toned stuff, the model seems to break down a little in detecting larger deviations from grayscale). You can separate these out from true grayscale by thresholding on the color band means.

    I ran this on a test set of 13,000 photographic images and got classification with 99.1% precision and 92.5% recall. Accuracy could probably be further improved by using a nonlinear bias adjustment (color values must be between 0 and 255 for example). Maybe looking at median squared error instead of MSE would better allow e.g. grayscale images with small color stamps.

    from PIL import Image, ImageStat
    def detect_color_image(file, thumb_size=40, MSE_cutoff=22, adjust_color_bias=True):
        pil_img = Image.open(file)
        bands = pil_img.getbands()
        if bands == ('R','G','B') or bands== ('R','G','B','A'):
            thumb = pil_img.resize((thumb_size,thumb_size))
            SSE, bias = 0, [0,0,0]
            if adjust_color_bias:
                bias = ImageStat.Stat(thumb).mean[:3]
                bias = [b - sum(bias)/3 for b in bias ]
            for pixel in thumb.getdata():
                mu = sum(pixel)/3
                SSE += sum((pixel[i] - mu - bias[i])*(pixel[i] - mu - bias[i]) for i in [0,1,2])
            MSE = float(SSE)/(thumb_size*thumb_size)
            if MSE <= MSE_cutoff:
                print "grayscale\t",
            else:
                print "Color\t\t\t",
            print "( MSE=",MSE,")"
        elif len(bands)==1:
            print "Black and white", bands
        else:
            print "Don't know...", bands
    

提交回复
热议问题