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?
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.
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.
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 .
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'
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
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
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
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.)
I faced a similar situation, where I tried the following approaches:
IMREAD_UNCHANGED
and checking for image.shapeBoth 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")
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With