繁体   English   中英

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

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

我必须在 python 中编写一个测试用例来检查 jpg 图像是彩色还是灰度。 任何人都可以让我知道是否有任何方法可以不安装像 opencv 这样的额外库吗?

可以按如下方式进行:

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'

您可以检查每个像素以查看它是否为灰度(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

为了更快的处理,最好避免在每个像素上循环,使用 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

使用 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

快速结果的性能增强:由于许多图像具有黑色或白色边框,您希望通过从 im 中采样一些随机 i,j 点并测试它们来更快地终止? 或者使用模运算来遍历图像行。 首先我们采样(-无替换)说 100 个随机 i,j 点; 在不太确定的情况下,我们会对其进行线性扫描。

使用自定义迭代器 iterpixels(im)。 我没有安装 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())

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

我遇到了类似的情况,我尝试了以下方法:

  1. 使用IMREAD_UNCHANGED读取并检查 image.shape
  2. 拆分 B、G、R 通道并检查它们是否相等

这两种方法在我的数据集中只有 53% 的准确率。 我不得不放宽检查不同通道中像素的条件,并创建一个比率以将其分类为灰色或彩色。 通过这种方法,我能够在我的数据集上获得 87.3% 的准确率。

这是对我有用的逻辑:

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")

为什么我们不使用 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为我们提供了列表视图中所有像素的总和 = [R, G, B] 例如 [568283302.0, 565746890.0, 559724236.0]。 对于灰度图像,列表的所有元素都是相等的。

在灰度图像的情况下,某个像素中的所有通道都是相等的(如果您只有一个通道,那么您没有问题)。 所以基本上如果你列出所有像素的 3 个通道值,你会假设每个像素的 3 个通道都相等。

Image.getcolors()返回一个未排序的(计数,像素)值列表。

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

如果len(color_count)超过 256,则此方法返回 None - 意味着您的像素列表中有超过 256 种颜色选项,因此它是彩色图像(在灰度的情况下,您只能有 256 种颜色(0,0, 0) 到 (255,255,255))。 所以在那之后你只需要:

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

请注意,这仅在使用getcolors()的默认参数值getcolors()

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

老问题,但我需要一个不同的解决方案。 有时 3 通道图像(例如 RGB)可能几乎是灰度,而 3 通道中的每个像素都不相同。 这会检查每个像素,但如果需要,您也可以对图像进行子采样。 我在这里使用了斜率,但您可以对回归中的大部分参数进行检查。 由于内部矩阵乘法解,线性回归通常非常快。

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')

这是使用 PIL.image 而不是 cv2 的 Alexey Antonenko 答案的一个版本。 如果你有浮动图像,我认为使用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

正如您可能是对的,OpenCV 可能对这项任务来说太过分了,但为此使用 Python 图像库 (PIL) 应该没问题。 以下应该对您有用:

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

编辑正如 Mark 和 JRicardo000 所指出的,您可以迭代每个像素。 您还可以在此处使用 im.split() 函数。

暂无
暂无

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

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