使用Python/PIL检测图像是彩色、灰度还是黑白

使用Python/PIL检测图像是彩色、灰度还是黑白,python,opencv,python-2.7,python-imaging-library,Python,Opencv,Python 2.7,Python Imaging Library,我从一个jpeg格式的PDF文件中提取页面图像,我需要确定每个图像是否更灰度、颜色或黑白(带有容差因子) 我已经找到了一些使用PIL(和)进行颜色检测的方法,但我不知道如何回答这个简单的(视觉)问题:它更多的是黑白、彩色还是灰度图像 对于这一部分,我更喜欢使用Python和PIL,但如果有人有线索(或解决方案),我也可以使用OpenCV。您可以使用操作符,它可以告诉您它是“灰度”(即2通道)还是“颜色”(即3通道)图像。对于黑白,您需要基于灰度设置更深入的测试,因为定义不同。我已经找到了一种通过

我从一个jpeg格式的PDF文件中提取页面图像,我需要确定每个图像是否更灰度、颜色或黑白(带有容差因子)

我已经找到了一些使用PIL(和)进行颜色检测的方法,但我不知道如何回答这个简单的(视觉)问题:它更多的是黑白、彩色还是灰度图像


对于这一部分,我更喜欢使用Python和PIL,但如果有人有线索(或解决方案),我也可以使用OpenCV。

您可以使用操作符,它可以告诉您它是“灰度”(即2通道)还是“颜色”(即3通道)图像。对于黑白,您需要基于灰度设置更深入的测试,因为定义不同。

我已经找到了一种通过PIL ImageStat模块猜测这一点的方法。Thanx用于图像的单色测定

from PIL import Image, ImageStat

MONOCHROMATIC_MAX_VARIANCE = 0.005
COLOR = 1000
MAYBE_COLOR = 100

def detect_color_image(file):
    v = ImageStat.Stat(Image.open(file)).var
    is_monochromatic = reduce(lambda x, y: x and y < MONOCHROMATIC_MAX_VARIANCE, v, True)
    print file, '-->\t',
    if is_monochromatic:
        print "Monochromatic image",
    else:
        if len(v)==3:
            maxmin = abs(max(v) - min(v))
            if maxmin > COLOR:
                print "Color\t\t\t",
            elif maxmin > MAYBE_COLOR:
                print "Maybe color\t",
            else:
                print "grayscale\t\t",
            print "(",maxmin,")"
        elif len(v)==1:
            print "Black and white"
        else:
            print "Don't know..."
# Iterate over all Pixels in the image (width * height times) and do this for every pixel:
{
    int rg = Math.abs(r - g);
    int rb = Math.abs(r - b);
    int gb = Math.abs(g - b);
    diff += rg + rb + gb;
}

return diff / (height * width) / (255f * 3f);
从PIL导入图像,ImageStat
单色最大方差=0.005
颜色=1000
也许颜色=100
def检测颜色图像(文件):
v=ImageStat.Stat(Image.open(file)).var
is_单色=减少(λx,y:x和y<单色最大方差,v,真)
打印文件'-->\t',
如果是单色的:
打印“单色图像”,
其他:
如果len(v)==3:
最大最小值=abs(最大值(v)-最小值(v))
如果maxmin>颜色:
打印“颜色\t\t\t”,
elif maxmin>可能的颜色:
打印“可能是彩色的”,
其他:
打印“灰度\t\t”,
打印“(”,最大值“)”
elif len(v)==1:
打印“黑白”
其他:
打印“不知道…”
颜色和颜色常数是快速开关,用于查找彩色图像和灰度图像之间的差异,但不安全。作为一个例子,我有几个JPEG图像,它们被视为彩色图像,但实际上是灰度图像,由于扫描过程中出现了一些彩色伪影。这就是为什么我要在另一个层面上注意到其他人的舒尔彩色图像


如果有人有更好的方法,请告诉我。

我尝试了Gepeto的解决方案,但它有很多误报,因为颜色的大方差可能只是偶然的相似。正确的方法是计算每像素的方差。首先缩小图像,这样就不必处理数百万像素

默认情况下,此函数还使用平均颜色偏差调整,我发现这可以改进预测。这样做的一个副作用是,它还可以检测单色但非灰度图像(通常是深褐色的图像,该模型在检测较大的灰度偏差时似乎有点崩溃)。您可以通过在色带上设置阈值来将其从真实灰度中分离出来

我对13000张照片进行了测试,得到了99.1%的准确率和92.5%的召回率。使用非线性偏差调整(例如,颜色值必须介于0和255之间)可能会进一步提高精度。也许用中值平方误差代替均方误差会更好地允许(例如)带有小彩色标记的灰度图像

from PIL import Image, ImageStat
def detect_color_image(file, thumb_size=40, MSE_cutoff=22, adjust_color_bias=True):
    pil_img = Image.open(file)
    bands = pil_img.getbands()
    if bands == ('R','G','B') or bands== ('R','G','B','A'):
        thumb = pil_img.resize((thumb_size,thumb_size))
        SSE, bias = 0, [0,0,0]
        if adjust_color_bias:
            bias = ImageStat.Stat(thumb).mean[:3]
            bias = [b - sum(bias)/3 for b in bias ]
        for pixel in thumb.getdata():
            mu = sum(pixel)/3
            SSE += sum((pixel[i] - mu - bias[i])*(pixel[i] - mu - bias[i]) for i in [0,1,2])
        MSE = float(SSE)/(thumb_size*thumb_size)
        if MSE <= MSE_cutoff:
            print "grayscale\t",
        else:
            print "Color\t\t\t",
        print "( MSE=",MSE,")"
    elif len(bands)==1:
        print "Black and white", bands
    else:
        print "Don't know...", bands
从PIL导入图像,ImageStat
def检测颜色图像(文件,拇指大小=40,MSE截止=22,调整颜色偏差=True):
pil_img=Image.open(文件)
bands=pil_img.getbands()
如果频带=('R'、'G'、'B')或频带=('R'、'G'、'B'、'A'):
thumb=pil\u img.resize((thumb\u大小,thumb\u大小))
SSE,偏差=0,[0,0,0]
如果调整颜色偏差:
偏差=ImageStat.Stat(拇指)。平均值[:3]
偏差=[b-总和(偏差)/3表示偏差中的b]
对于thumb.getdata()中的像素:
mu=总和(像素)/3
SSE+=sum((像素[i]-mu-bias[i])*(像素[i]-mu-bias[i]),用于[0,1,2]中的i)
MSE=浮动(SSE)/(拇指大小*拇指大小)

如果MSE我们使用这个简单的函数来确定图像的颜色因子

from PIL import Image, ImageStat

MONOCHROMATIC_MAX_VARIANCE = 0.005
COLOR = 1000
MAYBE_COLOR = 100

def detect_color_image(file):
    v = ImageStat.Stat(Image.open(file)).var
    is_monochromatic = reduce(lambda x, y: x and y < MONOCHROMATIC_MAX_VARIANCE, v, True)
    print file, '-->\t',
    if is_monochromatic:
        print "Monochromatic image",
    else:
        if len(v)==3:
            maxmin = abs(max(v) - min(v))
            if maxmin > COLOR:
                print "Color\t\t\t",
            elif maxmin > MAYBE_COLOR:
                print "Maybe color\t",
            else:
                print "grayscale\t\t",
            print "(",maxmin,")"
        elif len(v)==1:
            print "Black and white"
        else:
            print "Don't know..."
# Iterate over all Pixels in the image (width * height times) and do this for every pixel:
{
    int rg = Math.abs(r - g);
    int rb = Math.abs(r - b);
    int gb = Math.abs(g - b);
    diff += rg + rb + gb;
}

return diff / (height * width) / (255f * 3f);
因为灰色值具有r-g=0、r-b=0和g-b=0
灰度图像的差异接近0,彩色图像的差异大于0。

我个人更喜欢古墓的答案。这不是一个新的答案,我只想发布Java版本:

private Mat calculateChannelDifference(Mat mat) {   

    // Create channel list:
    List<Mat> channels = new ArrayList<>();

    for (int i = 0; i < 3; i++) {
        channels.add(new Mat());
    }

    // Split the channels of the input matrix:
    Core.split(mat, channels);

    Mat temp = new Mat();

    Mat result = Mat.zeros(mat.size(), CvType.CV_8UC1);

    for (int i = 0; i < channels.size(); i++) {

        // Calculate difference between 2 successive channels:
        Core.absdiff(channels.get(i), channels.get((i + 1) % channels.size()), temp);

        // Add the difference to the result:
        Core.add(temp, result, result);
    }

    return result;
}
private Mat calculateChannelDifference(Mat-Mat){
//创建频道列表:
列表通道=新的ArrayList();
对于(int i=0;i<3;i++){
添加(新的Mat());
}
//拆分输入矩阵的通道:
堆芯、分裂(垫、通道);
材料温度=新材料();
材料结果=材料零点(材料尺寸(),CvType.CV_8UC1);
对于(int i=0;i

其结果是作为矩阵的差异,这样您可以应用一些阈值,甚至检测形状。如果您想将结果作为单个数字,只需计算平均值。这可以使用
Core.mean()

Thanx scap3y进行回答。你说的是图像结构,但是视觉上看起来是灰度或黑白的RGB JPEG图像(3个通道)如何(因此可以转换为8位JPEG或TIF-Group4)?我本来打算提出一个解决方案,但我想你已经找到了一种基于PIL的方法,可以为你服务。干杯。嗨,谢谢你。我正在努力弄清楚这是怎么回事。“MSE”、“SSE”是什么意思?你能链接到一些文档吗?SSE/MSE是平方误差/均方误差之和。回顾这一点,如果将其转换为HSV或类似的颜色空间,在概念上可能会更容易。然后,SSE将仅在色调/饱和度通道中计算为与零的差值或与偏差的差值(如果有)。这里的问题是,没有对每个像素进行绝对计算。负面差异