简体   繁体   English

如何仅使用 Python stdlib 检查 jpeg 图像是彩色还是灰度

[英]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.我必须在 python 中编写一个测试用例来检查 jpg 图像是彩色还是灰度。 Can anyone please let me know if there is any way to do it with out installing extra libraries like opencv?任何人都可以让我知道是否有任何方法可以不安装像 opencv 这样的额外库吗?

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)您可以检查每个像素以查看它是否为灰度(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):为了更快的处理,最好避免在每个像素上循环,使用 ImageChops,(还要确保图像是真正的灰度,我们需要比较每个像素上的颜色,不能只使用总和):

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:使用 numpy 功能和 opencv 有更多 pythonic 方式:

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?快速结果的性能增强:由于许多图像具有黑色或白色边框,您希望通过从 im 中采样一些随机 i,j 点并测试它们来更快地终止? Or use modulo arithmetic to traverse the image rows.或者使用模运算来遍历图像行。 First we sample(-without-replacement) say 100 random i,j-points;首先我们采样(-无替换)说 100 个随机 i,j 点; in the unlikely event that isn't conclusive, then we scan it linearly.在不太确定的情况下,我们会对其进行线性扫描。

Using a custom iterator iterpixels(im).使用自定义迭代器 iterpixels(im)。 I don't have PIL installed so I can't test this, here's the outline:我没有安装 PIL,所以我无法测试这个,这是大纲:

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.) (我的原话也是如此,首先你检查 JPEG 标头,偏移量 6:分量数(1 = 灰度,3 = RGB)。如果它是 1 = 灰度,你已经知道答案,而无需检查单个像素。)

I faced a similar situation, where I tried the following approaches:我遇到了类似的情况,我尝试了以下方法:

  1. Reading using IMREAD_UNCHANGED and checking for image.shape使用IMREAD_UNCHANGED读取并检查 image.shape
  2. Splitting B,G,R channels and checking if they are equal拆分 B、G、R 通道并检查它们是否相等

Both of these approaches got me only like 53% accuracy in my dataset.这两种方法在我的数据集中只有 53% 的准确率。 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.通过这种方法,我能够在我的数据集上获得 87.3% 的准确率。

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?为什么我们不使用 ImageStat 模块?

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]. stat.sum为我们提供了列表视图中所有像素的总和 = [R, G, B] 例如 [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.所以基本上如果你列出所有像素的 3 个通道值,你会假设每个像素的 3 个通道都相等。

Image.getcolors() returns an unsorted list of (count, pixel) values. Image.getcolors()返回一个未排序的(计数,像素)值列表。

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)).如果len(color_count)超过 256,则此方法返回 None - 意味着您的像素列表中有超过 256 种颜色选项,因此它是彩色图像(在灰度的情况下,您只能有 256 种颜色(0,0, 0) 到 (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() .请注意,这仅在使用getcolors()的默认参数值getcolors()

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

Old question but I needed a different solution.老问题,但我需要一个不同的解决方案。 Sometimes 3 channel images (eg RGB) might be almost grayscale without every pixel being identical in 3 channels.有时 3 通道图像(例如 RGB)可能几乎是灰度,而 3 通道中的每个像素都不相同。 This checks every pixel but you can also subsample the image if needed.这会检查每个像素,但如果需要,您也可以对图像进行子采样。 I used slope here but you can use checks on most of these parmaters from the regression.我在这里使用了斜率,但您可以对回归中的大部分参数进行检查。 Linear regressions are usually very fast due to internal matrix multiply solution.由于内部矩阵乘法解,线性回归通常非常快。

import glob
import scipy
import cv2
THRESH = 0.01
BASEDIR = 'folder/*.jpg'

files = glob.glob(BASEDIR)
for file in files:
    img = cv2.imread(file)
    slope1, intercept1, r1, p1, se1 = scipy.stats.linregress(img[:,:,0].flatten(),img[:,:,1].flatten())
    slope2, intercept2, r2, p2, se2 = scipy.stats.linregress(img[:,:,0].flatten(),img[:,:,2].flatten())
    if abs(slope1 - 1) > THRESH or abs(slope2 - 1) > THRESH:
        print(f'{file} is colour')
    else:
        print(f'{file} is close to grey scale')

Here is a version of Alexey Antonenko answer using PIL.image instead of cv2.这是使用 PIL.image 而不是 cv2 的 Alexey Antonenko 答案的一个版本。 In case you have float images I think it is safer to use the np.allclose function.如果你有浮动图像,我认为使用np.allclose function 更安全。

from PIL import Image
import numpy as np
def isgray(imgpath):
    img_pil = Image.open(imgpath)
    img = np.asarray(img_pil)
    if len(img.shape) < 3: return True
    if img.shape[2]  == 1: return True
    r,g,b = img[:,:,0], img[:,:,1], img[:,:,2]
    if np.allclose(r,g) and np.allclose(r,b): return True
    return False

As you are probably correct, OpenCV may be an overkill for this task but it should be okay to use Python Image Library (PIL) for this.正如您可能是对的,OpenCV 可能对这项任务来说太过分了,但为此使用 Python 图像库 (PIL) 应该没问题。 The following should work for you:以下应该对您有用:

import Image
im = Image.open("lena.jpg")

EDIT As pointed out by Mark and JRicardo000, you may iterate over each pixel.编辑正如 Mark 和 JRicardo000 所指出的,您可以迭代每个像素。 You could also make use of the im.split() function here.您还可以在此处使用 im.split() 函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM