opencv 使用Python检测图像是彩色、灰度还是白色

eoxn13cs  于 2022-11-15  发布在  Python
关注(0)|答案(7)|浏览(314)

我从jpeg格式的PDF文件中提取页面图像,我需要确定每个图像是否更多的灰度,彩色或白色(与公差因素)。
我找到了一些使用PIL(herehere)进行颜色检测的方法,但我不知道如何回答这个简单的(视觉)问题:是白色、彩色还是灰度图像?
我更喜欢用Python和PIL来完成这部分,但是如果有人有线索(或解决方案),我也可以使用OpenCV。

vawmfj5a

vawmfj5a1#

我尝试了Gepeto的解决方案,它有很多假阳性,因为颜色总方差可能只是偶然相似。正确的方法是计算每个像素的方差。首先缩小图像,这样你就不必处理数百万像素。
默认情况下,这个函数还使用了平均颜色偏差调整,我发现这可以提高预测效果。这样做的一个副作用是,它还可以检测单色但非灰度图像(通常是深褐色的东西,在检测与灰度的较大偏差时,模型似乎有点崩溃)。您可以通过对色带平均值进行阈值处理,将这些图像与真正的灰度分离开来。
我在13,000张照片的测试集上运行了这个方法,得到了99.1%的准确率和92.5%的召回率。通过使用非线性偏差调整(例如,颜色值必须在0和255之间),准确率可能会进一步提高。也许看中值平方误差而不是MSE会更好地允许带有小颜色戳的灰度图像。

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
vkc1a9a2

vkc1a9a22#

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

# 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,所以对于灰度图像,diff将接近0,而对于彩色图像,diff将〉0。

fykwrbwg

fykwrbwg3#

我已经找到了一种方法来猜测这与PIL.ImageStat模块。Thanx到this post为单色确定一个图像。

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

COLORMAYBE_COLOR常数是快速开关,用于查找彩色和灰度图像之间的差异,但这并不安全。例如,我有几个JPEG图像,它们在视图中是彩色的,但真实的上是灰度的,由于扫描过程中存在一些颜色伪影。这就是为什么我有另一个层次来注意其他图像中真正确定的彩色图像。
如果有人有更好的方法,让我知道。

uqxowvwt

uqxowvwt4#

我个人更喜欢TomB的答案,这不是新的答案,我只是想发布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;
}

结果是矩阵形式的差值,这样你就可以应用一些阈值,甚至检测形状。如果你想得到单个数字的结果,你只需要计算平均值。这可以使用Core.mean()来完成

0aydgbwb

0aydgbwb5#

import numpy as np
import cv2
import imutils

def image_colorfulness(image):
    (B, G, R) = cv2.split(image.astype("float"))
    rg = np.absolute(R - G)
    yb = np.absolute(0.5 * (R + G) - B)
    (rbMean, rbStd) = (np.mean(rg), np.std(rg))
    (ybMean, ybStd) = (np.mean(yb), np.std(yb))
    stdRoot = np.sqrt((rbStd ** 2) + (ybStd ** 2))
    meanRoot = np.sqrt((rbMean ** 2) + (ybMean ** 2))
    return stdRoot + (0.3 * meanRoot)

image = cv2.imread('green.JPG')
image = imutils.resize(image, width=250)
C  = image_colorfulness(image)
#set a threshold 
print(C)
if C > 10:
    print('its a color image...')
elif 8 < C <= 10:
    print('Not Sure...')
else:
    print('Black and white image...')
cv2.putText(image, "{:.2f}".format(C), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.4, (0, 255, 0), 3)

cv2.imshow('im',image)
cv2.waitKey(0)
tez616oj

tez616oj6#

这个解决方案的灵感来自TomB的帖子。有一个微小的变化。Tom的帖子是基于RGB颜色空间的,而我的是基于LAB颜色空间的。要了解更多关于LAB空间的信息,请浏览这篇帖子和其中提到的链接。

使用实验室空间的优势

LAB有3个通道,就像RGB一样。但是只有2个通道有颜色信息(A和B),而L通道代表亮度值。与RGB不同,我们必须分析所有三个通道,使用LAB我们可以只分析2个通道。当必须分析大量图像时,优势将显而易见。

方法:

这个方法和Tom的帖子没有什么不同。

  • 获取图像的A和B通道
  • 求出它们之间差值的平均值
  • 确定阈值,在该阈值之上所有图像都可以被标记为彩色。
    代码
  • 使用的图像:*

灰度图像:

彩色图像:

einstein_img = cv2.imread('Einstein.jpg')
flower_img = cv2.imread('flower.jpg')

# convert to LAB space
elab = cv2.cvtColor(einstein_img, cv2.COLOR_BGR2LAB)
flab = cv2.cvtColor(flower_img, cv2.COLOR_BGR2LAB)

# split the channels
el, ea, eb = cv2.split(elab)
# obtain difference between A and B channel at every pixel location
de = abs(ea-eb)
# find the mean of this difference
mean_e = np.mean(de)

# same as above for the color image:
fl, fa, fb = cv2.split(flab)
df = abs(fa-fb)
mean_f = np.mean(df)

# for gray image
print(mean_e)

0.0

# for color image
print(mean_f)

83.5455

为什么会这样

这是因为主要包含白色、灰色和黑色的图像在LAB空间的双色通道中不会显示太多变化。它被设计为很好地分割/隔离主色。但也可以很好地用于颜色较少的图像。
彩色flower图像的A和B通道并排放置:

由于在每个像素处两者之间存在差异,因此我们获得非零平均值。
A和B通道的灰度Einstein图像并排放置:

然而,这里我们没有得到平均值。

***注:***虽然0是理想的平均值,但在某些情况下,灰色图像可能会出现非零值。但该值不会像彩色图像那么大。在这种情况下,可以定义一个阈值。

2admgd59

2admgd597#

您可以使用cv::Mat::channels()操作符,它可以告诉您它是“灰度”(即2通道)还是“彩色”(即3通道)图像。对于白色图像,您需要根据灰度设置更深层次的测试,因为清晰度不同。

相关问题