将图像转换为灰度(感知图像哈希)

时间:2018-03-22 06:06:35

标签: c# image hash grayscale

我正在研究感知图像哈希。首先,我减小图像大小以消除高频。然后,我将图像缩小到(8 * 8),使得总像素为64。我使用以下代码行。

private void button1_Click(object sender, EventArgs e)
{
    Image img = pictureBox1.Image;
    img = resizeImage(img, new Size(8,8));
    pictureBox2.Image = img;            
}

public static Image resizeImage(Image imgToResize, Size size)
{
    return (Image)(new Bitmap(imgToResize, size));
}

现在我想减少颜色,以便将微小的(8x8)图片转换为灰度,并希望将哈希值从64像素(64红色,64绿色和64蓝色)更改为64种颜色。在这里,我被困住了。

1 个答案:

答案 0 :(得分:0)

如果你想要'感知'哈希,并且只需要处理64个值,那么使用更高级的算法可能会很有趣。其背后的理论大致解释为here

Gray =(0.2126×Red 2.2 + 0.7152×Green 2.2 + 0.0722×Blue 2.2 1 / 2.2

作为代码,这将成为

public static Byte GetGreyValue(Byte red, Byte green, Byte blue)
{
    Double redFactor = 0.2126d * Math.Pow(red, 2.2d);
    Double grnFactor = 0.7152d * Math.Pow(green, 2.2d);
    Double bluFactor = 0.0722d * Math.Pow(blue, 2.2d);
    Double grey = Math.Pow(redFactor + grnFactor + bluFactor, 1d / 2.2);
    return (Byte)Math.Max(0, Math.Min(255, Math.Round(grey, MidpointRounding.AwayFromZero)));
}

现在,您可以查看已调整大小的图像中的所有字节并将其转换为灰色。使用LockBits,这不是太难。你只需复制字节,每四个迭代一次(每个ARGB字节四边形),取出RGB组件,将它们放入'make grey'函数,将结果放入你从中获取的RGB点,然后编写完成后,字节数组会回到你的图像中。

public static Bitmap GetGreyImage(Image img, Int32 width, Int32 height)
{
    // get image data
    Bitmap b = new Bitmap(img, width, height);
    BitmapData sourceData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
    Int32 stride = sourceData.Stride;
    Byte[] data = new Byte[stride * b.Height];
    // Copy bytes from image into data array
    Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
    // iterate over array and convert to gray
    for (Int32 y = 0; y < height; y++)
    {
        Int32 offset = y * stride;
        for (Int32 x = 0; x < width; x++)
        {
            // "ARGB" is little-endian Int32, so the actual byte order is B,G,R,A
            Byte colB = data[offset + 0]; // B
            Byte colG = data[offset + 1]; // G
            Byte colR = data[offset + 2]; // R
            Int32 colA = data[offset + 3]; // A
            // Optional improvement: set pixels to black if color
            // is considered too transparent to matter.
            Byte grayValue = colA < 128 ? 0 : GetGreyValue(colR, colG, colB);
            data[offset + 0] = grayValue; // B
            data[offset + 1] = grayValue; // G
            data[offset + 2] = grayValue; // R
            data[offset + 3] = 0xFF; // A
            offset += 4;
        }
    }
    // Copy bytes from data array back into image
    Marshal.Copy(data, 0, sourceData.Scan0, data.Length);
    b.UnlockBits(sourceData);
    return b;
}

如果“从64红色,64绿色和64蓝色变为64种颜色”,你的意思是你想要一个8位(调色板)图像,其中每个字节是一个像素,那么你将不得不简单将你在那里生成的值保存在一个新的字节数组中而不是将它们写回来,然后用Format8bppIndexed格式创建一个新的8x8位图,在第二个BitmapData对象中打开它,然后在那里写入。

请注意,8位图像需要调色板,因此您需要查看标准生成的图像8位调色板,并将其更改为从(0,0,0)到(255,255,255)的淡入淡出)for循环。