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).
I have found some ways to work with color detection with PIL ( here and here ) but I can't figure out how to answer this simple (visual) question : is it much more black and white, color or grayscale image ?
I prefer working with Python and PIL for this part but I could use too OpenCV if someone has a clue (or solution).
I personally prefer the answer of TomB. This is not a new answer, I just want to post the Java version:
The result is the difference as an matrix, this way you could apply some threshold and even detect shapes. If you want the result as a single number, you will just have to calculate the average value. This can be done using
Core.mean()
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.
We use this simple function to determine the color-factor of an image.
As gray values have r-g = 0 and r-b = 0 and g-b = 0 diff will be near 0 for grayscale images and > 0 for colored images.
You can use the cv::Mat::channels() operator and that can tell you whether it is a "grayscale" (i.e., 2 channel) or "color" (i.e., 3-channel) image. For black and white, you will need set deeper tests based on grayscale since the definition varies.
I have found a way to guess this with the PIL ImageStat module. Thanx to this post for the monochromatic determination of an image.
The COLOR and MAYBE_COLOR constant are quick switches to find the differences between color and grayscale images but it is not safe. As an exemple, I have several JPEG images that are view as color but in real are grayscale with some color artefacts due to a scan process. That's why I have another level to note really shure color image from the others.
If someone has a better approch, let me know.