如何测试图像是否为灰度?

How can I test an image to be grayscale?

Aforge.net/Accord.net库中,the following test判断图片是否灰度:

public static bool IsGrayscale (Bitmap image)
{
    bool ret = false;

    // check pixel format
    if (image.PixelFormat == PixelFormat.Format8bppIndexed)
    {
        ret = true;
        // check palette
        ColorPalette cp = image.Palette;
        Color c;
        // init palette
        for ( int i = 0; i < 256; i++ )
        {
            c = cp.Entries[i];
            if ((c.R != i) || (c.G != i) || (c.B != i))
            {
                ret = false;
                break;
            }
        }
    }
    return ret;
}

这不是谬论吗?

definition而言,灰度图像可以是除 1 位 pp 以外的任何颜色深度。例如,以下是 32 位灰度图像:


那么,我的问题是,测试灰度图像的正确方法是什么?

看来我在 this link 中得到了答案。

If the image is a Gray Scale image then

if(R=G=B) //Grayscale 

For more accurate results you can introduce some thresholds values. i.e

if((abs(R-G)< Threshold))// Threshold-> can be greater than zero. eg
0.006 //Grayscale 

By this way you can get pretty good results.

但是,我怀疑,这个过程会非常慢。

所以,欢迎大家有更好的想法来回答

该代码正在检查标准 8 位灰度,其中像素值对应于它们的亮度。这或多或少是灰度的标准,但它确实不匹配优化的调色板或类似的东西。

虽然我不确定您为什么要排除 1bpp。它和其他任何格式一样是一种索引格式,事实上它有一个像 8bpp 一样的调色板,这意味着它甚至不限于纯黑色和白色。这是该鹦鹉的 1bpp 灰度版本,其调色板中有两个灰度值:

检查索引图像的最简单方法确实是遍历调色板并进行 R=G=B 测试,但从技术上讲,即便如此,您也可以争辩说只要任何非灰度图像都是灰度图像调色板上的颜色实际上并未在图像上使用

一个万无一失的方法可能只是让LockBits将图像转换为32bppARGB,然后检查其上的R,G和B。但即使在那里,您也必须做出选择... do 100% transparent pixels that don't match R=G=B make the image "not grayscale"?

无论如何,这是我要使用的方法:

public static Boolean IsGrayscale(Bitmap cur)
{
    // Indexed format, and no non-gray colours in the images palette: immediate pass.
    if ((cur.PixelFormat & PixelFormat.Indexed) == PixelFormat.Indexed
        && cur.Palette.Entries.All(c => c.R == c.G && c.R == c.B))
        return true;
    // Quick indexed check failed; actually check image data.
    // Get bytes out of the image, converted to 32bpp ARGB 
    BitmapData curBitmapData = cur.LockBits(new Rectangle(0, 0, cur.Width, cur.Height),
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    Int32 stride = curBitmapData.Stride;
    Byte[] data = new Byte[stride * cur.Height];
    Marshal.Copy(curBitmapData.Scan0, data, 0, data.Length);
    cur.UnlockBits(curBitmapData);
    // Go over all bytes per block of four.
    Int32 curRowOffs = 0;
    for (Int32 y = 0; y < cur.Height; y++)
    {
        // Set offset to start of current row
        Int32 curOffs = curRowOffs;
        for (Int32 x = 0; x < cur.Width; x++)
        {
            Byte b = data[curOffs];
            Byte g = data[curOffs + 1];
            Byte r = data[curOffs + 2];
            Byte a = data[curOffs + 3];
            // Increase offset to next colour
            curOffs += 4;
            if (a == 0)
                continue;
            if (r != g || r != b)
                return false;
        }
        // Increase row offset
        curRowOffs += stride;
    }
    return true;
}