Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check whether a jpeg image is color or gray scale using only Python stdlib

I have to write a test case in python to check whether a jpg image is in color or grayscale. Can anyone please let me know if there is any way to do it with out installing extra libraries like opencv?

like image 885
kadina Avatar asked May 14 '14 17:05

kadina


People also ask

How do you know if an image is grayscale or color?

You can check every pixel to see if it is grayscale (R == G == B) Just a performance-enhance for fast results: since many images have black or white border, you'd expect faster termination by sampling random i,j-points from im and test them? Or use modulo arithmetic to traverse the image.

How do I tell if a image is color in Python?

To detect colors in images, the first thing you need to do is define the upper and lower limits for your pixel values. Once you have defined your upper and lower limits, you then make a call to the cv2. inRange method which returns a mask, specifying which pixels fall into your specified upper and lower range.

How do you read an image as gray scale in Python?

Convert an Image to Grayscale in Python Using the Conversion Formula and the Matplotlib Library. We can also convert an image to grayscale using the standard RGB to grayscale conversion formula that is imgGray = 0.2989 * R + 0.5870 * G + 0.1140 * B .


8 Answers

Can be done as follow:

from scipy.misc import imread, imsave, imresize
image = imread(f_name)
if(len(image.shape)<3):
      print 'gray'
elif len(image.shape)==3:
      print 'Color(RGB)'
else:
      print 'others'
like image 154
superMind Avatar answered Oct 03 '22 08:10

superMind


You can check every pixel to see if it is grayscale (R == G == B)

import Image

def is_grey_scale(img_path):
    img = Image.open(img_path).convert('RGB')
    w, h = img.size
    for i in range(w):
        for j in range(h):
            r, g, b = img.getpixel((i,j))
            if r != g != b: 
                return False
    return True
like image 20
joaoricardo000 Avatar answered Oct 03 '22 07:10

joaoricardo000


For faster processing, it is better to avoid loops on every pixel, using ImageChops, (but also to be sure that the image is truly grayscale, we need to compare colors on every pixel and cannot just use the sum):

from PIL import Image,ImageChops

def is_greyscale(im):
    """
    Check if image is monochrome (1 channel or 3 identical channels)
    """
    if im.mode not in ("L", "RGB"):
        raise ValueError("Unsuported image mode")

    if im.mode == "RGB":
        rgb = im.split()
        if ImageChops.difference(rgb[0],rgb[1]).getextrema()[1]!=0: 
            return False
        if ImageChops.difference(rgb[0],rgb[2]).getextrema()[1]!=0: 
            return False
    return True
like image 35
Karl K Avatar answered Oct 03 '22 09:10

Karl K


There is more pythonic way using numpy functionality and opencv:

import cv2
def isgray(imgpath):
    img = cv2.imread(imgpath)
    if len(img.shape) < 3: return True
    if img.shape[2]  == 1: return True
    b,g,r = img[:,:,0], img[:,:,1], img[:,:,2]
    if (b==g).all() and (b==r).all(): return True
    return False
like image 33
Alexey Antonenko Avatar answered Oct 03 '22 09:10

Alexey Antonenko


A performance-enhance for fast results: since many images have black or white border, you'd expect faster termination by sampling a few random i,j-points from im and test them? Or use modulo arithmetic to traverse the image rows. First we sample(-without-replacement) say 100 random i,j-points; in the unlikely event that isn't conclusive, then we scan it linearly.

Using a custom iterator iterpixels(im). I don't have PIL installed so I can't test this, here's the outline:

import Image

def isColor(r,g,b): # use tuple-unpacking to unpack pixel -> r,g,b
    return (r != g != b)

class Image_(Image):
    def __init__(pathname):
        self.im = Image.open(pathname)
        self.w, self.h = self.im.size
    def iterpixels(nrand=100, randseed=None):
        if randseed:
            random.seed(randseed) # For deterministic behavior in test
        # First, generate a few random pixels from entire image
        for randpix in random.choice(im, n_rand)
            yield randpix
        # Now traverse entire image (yes we will unwantedly revisit the nrand points once)
        #for pixel in im.getpixel(...): # you could traverse rows linearly, or modulo (say) (im.height * 2./3) -1
        #    yield pixel

    def is_grey_scale(img_path="lena.jpg"):
        im = Image_.(img_path)
        return (any(isColor(*pixel)) for pixel in im.iterpixels())

(Also my original remark stands, first you check the JPEG header, offset 6: number of components (1 = grayscale, 3 = RGB). If it's 1=grayscale, you know the answer already without needing to inspect individual pixels.)

like image 23
smci Avatar answered Oct 03 '22 09:10

smci


I faced a similar situation, where I tried the following approaches:

  1. Reading using IMREAD_UNCHANGEDand checking for image.shape
  2. Splitting B,G,R channels and checking if they are equal

Both of these approaches got me only like 53% accuracy in my dataset. I had to relax the condition for checking pixels in different channels and create a ratio to classify it as grey or color. With this approach, I was able to get 87.3% accuracy on my dataset.

Here is the logic which worked for me:

import cv2
import numpy as np

###test image
img=cv2.imread('test.jpg')

### splitting b,g,r channels
b,g,r=cv2.split(img)

### getting differences between (b,g), (r,g), (b,r) channel pixels
r_g=np.count_nonzero(abs(r-g))
r_b=np.count_nonzero(abs(r-b))
g_b=np.count_nonzero(abs(g-b))

### sum of differences
diff_sum=float(r_g+r_b+g_b)

### finding ratio of diff_sum with respect to size of image
ratio=diff_sum/img.size

if ratio>0.005:
    print("image is color")
else:
    print("image is greyscale")
like image 20
Sreekiran A R Avatar answered Oct 03 '22 09:10

Sreekiran A R


Why wouldn't we use ImageStat module?

from PIL import Image, ImageStat

def is_grayscale(path="image.jpg")

    im = Image.open(path).convert("RGB")
    stat = ImageStat.Stat(im)

    if sum(stat.sum)/3 == stat.sum[0]:
        return True
    else:
        return False

stat.sum gives us a sum of all pixels in list view = [R, G, B] for example [568283302.0, 565746890.0, 559724236.0]. For grayscale image all elements of list are equal.

like image 35
GriMel Avatar answered Oct 03 '22 07:10

GriMel


In case of a grayscale image, all channels in a certain pixel are equal (if you only have one channel then you don't have a problem) . So basicly if you list all the pixels with their 3 channels values, you suppose to see that each pixel has all 3 channels equal.

Image.getcolors() returns an unsorted list of (count, pixel) values.

im = Image.open('path_to_image.whatever')
color_count = im.getcolors()

If len(color_count) exceeds 256, this method returns None - meaning that you had more than 256 color-options in your pixel list, hence it is a colored image (in case of grayscale you can only have 256 colors (0,0,0) to (255,255,255)). So after that you only need :

if color_count: 
    # your image is grayscale
else:
    # your images is colored

Notice this will work only when using the default parameter value of getcolors().

Documentation: https://pillow.readthedocs.io/en/3.0.x/reference/Image.html#PIL.Image.Image.getcolors

like image 36
M.F Avatar answered Oct 03 '22 08:10

M.F