QQ隐藏图原理与C#实现(含源文件)

Posted Dear_Xuan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QQ隐藏图原理与C#实现(含源文件)相关的知识,希望对你有一定的参考价值。

        QQ群聊的背景色为白色,而打开图片后的背景色为黑色,如果能巧妙修改图片各个像素的透明度,就可以达到在不同背景下显示出不同图片的功能。

目录

效果

 原理分析

透明度叠加算法

灰度图算法 

黑白图算法

彩色图算法

图像放缩

界面设计

源文件 


​​​​​​​

效果

 点开前:

 点开后:

 原理分析

        我们已经知道是通过修改透明度来实现这个效果,现在只需要计算出透明度就行了。

        假设有两张图片,一张是在白色背景下可以看到的,我们称之为“白图”,另一种是在黑色背景下才能看到的,我们称之为“黑图”。为了把两张图混合在一起,对任意像素点G(i,j),计算(i+j),根据即奇偶性来选择显示白图还是黑图,相当于把两张图片穿插在一起,整体上看起来不会有任何异常。

        对于透明度具体怎么计算,下面提供了三种思路。

透明度叠加算法

设有两张图A,B,A在B的上面,B的不透明度为255(0表示全透明,255表示不透明),A的不透明度为alpha,则实际看到的像素值为

灰度图算法 

设白图在点(i,j)处像素值为G',求出灰度值G,通常采用的灰度值算法为

 现在已经知道了灰度值G,我们希望它能在白色背景下显示出原图的灰度图,此时前景色为黑色,背景色为白色,列出表达式:

 得到alpha = 255-G,即G的反色。

设黑图在(i,j)处的灰度值为G,此时前景色为白色,背景色为黑色,列出表达式:

 得到alpha = G。

所以对于白图,把它的不透明度设置为255 - G,对于黑图,把它的不透明度设置为G,就能做到在白色背景下能看到白图,在黑色背景下能看到黑图的效果。

算法代码:

private static Bitmap Merge_GRAY(Bitmap p1, Bitmap p2)
{
    bool flag;//判断显示白图还是黑图
    Color color;
    int transparency;//不透明度
    Bitmap bitmap = new Bitmap(p1.Width, p1.Height);
    for (int i = 0; i < p1.Width; i++)
    {
        for (int j = 0; j < p1.Height; j++)
        {
            flag = (i + j) % 2 == 0;
            if (flag)
            {
                color = p1.GetPixel(i, j);
                //R,G,B为静态变量,分别是299,587,114
                transparency = (color.R * R + color.G * G + color.B * B) / 1000;
                bitmap.SetPixel(i, j, Color.FromArgb(255 - transparency, Color.Black));
            }
            else
            {
                color = p2.GetPixel(i, j);
                transparency = (color.R * R + color.G * G + color.B * B) / 1000;
                bitmap.SetPixel(i, j, Color.FromArgb(transparency, Color.White));
            }
        }
    }
    return bitmap;
}

黑白图算法

与灰度图类似,但是要将灰度值用黑白两种颜色来代替,所以需要先遍历整张图,计算平均灰度值,把高于平均值的一律替换成白色,即255,把低于平均值的一律替换成黑色,即0

private static Bitmap Merge_BLACK_AND_WHITE(Bitmap p1, Bitmap p2)
        {
            bool flag;
            Color color;
            int RGB;
            Bitmap bitmap = new Bitmap(p1.Width, p1.Height);
            int[,] gray1 = new int[p1.Width,p1.Height];
            int[,] gray2 = new int[p1.Width, p1.Height];
            int sum1 = 0, sum2 = 0;
            for (int i = 0; i < p1.Width; i++)
            {
                for (int j = 0; j < p1.Height; j++)
                {
                    color = p1.GetPixel(i,j);
                    gray1[i, j] = (color.R * R + color.G * G + color.B * B) / 1000;
                    sum1 += gray1[i, j];
                    color = p2.GetPixel(i, j);
                    gray2[i, j] = (color.R * R + color.G * G + color.B * B) / 1000;
                    sum2 += gray2[i, j];
                }
            }
            sum1 /= p1.Width * p1.Height;
            sum2 /= p1.Width * p1.Height;
            for(int i = 0; i < p1.Width; i++)
            {
                for(int j = 0; j < p1.Height; j++)
                {
                    flag = (i + j) % 2 == 0;
                    if (flag)
                    {
                        RGB = gray1[i, j] > sum1 ? 0 : 255;
                        bitmap.SetPixel(i, j, Color.FromArgb(RGB, Color.Black));
                    }
                    else
                    {
                        RGB = gray2[i, j] > sum2 ? 0 : 255;
                        bitmap.SetPixel(i, j, Color.FromArgb(255 - RGB, Color.White));
                    }
                }
            }
            return bitmap;
        }

彩色图算法

若一张白图想要在白色背景下显示,设不透明度为alpha,列出表达式

 显然alpha为255

当这张图在黑色背景下时,需要它完全隐藏。

 显然alpha=0

为什么会出现两个完全相反的答案,而之前不会?原来之前的灰度图中,使用灰色像素来显示白图,在白色背景下通过不透明度让灰色像素显示,而在黑色背景下,灰色像素有颜色优势,无论不透明度是多少都不影响它在黑色背景下隐藏。但是彩色像素就不一样了,如果不透明度太大,会导致它在黑色背景下无法隐藏,最终出现两个图显示在一起的效果。

现在的问题在于:如果要图片更清晰,则需要增大不透明度,如果要白图黑图互不干扰,则需要减少不透明度。显然减少黑白图的相互干扰比清晰显示更重要。白图在黑色背景下,灰度值越高(颜色越白),则不透明度应该越低。

所以我们得出结论,不透明度应随着灰度值的增大而减小,且具有相同区间[0,255],显然正比例函数就具有上述特性

设不透明度alpha,灰度值G = (0.299R+0.587G+0.114B) / 3;

代入(0,255),(255,0),得到 k = -1, m = 255。

当然不一定是一次函数,也可以是二次函数,但是实现起来较为复杂,所以不考虑。

现在我们得到alpha = 255 - G,这是白图的计算方法。

对于黑图,它想要在黑色背景下显示,因此灰度值越大(颜色越白),不透明度越高,即不透明度与灰度值也成正比,我们也用上面那式子来代入计算,

得到 alpha = G,这是黑图的计算方法。

代码:

private static Bitmap Merge_COLORFUL(Bitmap p1,Bitmap p2)
{
    bool flag;
    Color color;
    int transparency;
    Bitmap bitmap = new Bitmap(p1.Width, p1.Height);
    for (int i = 0; i < p1.Width; i++)
    {
        for (int j = 0; j < p1.Height; j++)
        {
            flag = (i + j) % 2 == 0;
            if (flag)
            {
                color = p1.GetPixel(i, j);
                transparency = (color.R * R + color.G * G +  color.B * B) / 1000;
                bitmap.SetPixel(i, j, Color.FromArgb(255 - transparency, p1.GetPixel(i, j)));
            }
            else
            {
                color = p2.GetPixel(i, j);
                transparency = (color.R * R + color.G * G + color.B * B) / 1000;
                bitmap.SetPixel(i, j, Color.FromArgb(transparency, p2.GetPixel(i,j)));
            }
        }
    }
    return bitmap;
}

图像放缩

想要让两种图片混合,显然它们必须具有相同的宽度和高度,使用C#自带的放缩方法即可完成

private static Bitmap Scale(Bitmap b,int width,int height)
{
    Bitmap bitmap = new Bitmap(width, height);
    Graphics g = Graphics.FromImage(bitmap);
    g.InterpolationMode = interpolationMode;
    g.DrawImage(b, new Rectangle(0, 0, width, height), new Rectangle(0, 0, b.Width, b.Height), GraphicsUnit.Pixel);
    g.Dispose();
    return bitmap;
}

界面设计

主界面显示黑白两种颜色,分别用来模拟白色和黑色背景,

下图左右两边显示出不同的图片,实际上是同一张,由于背景色的缘故看起来不同,这就是QQ上的最终效果。

 点击“原图”后显示原来的图片,点击“导出”后把生成的图片保存到本地。

 设置里提供了高度自定义功能,可以修改灰度算法的参数,图片比例,放缩算法和图片类型。

源文件 

 源文件:https://dearx.lanzoui.com/iYPmas7wofg

 EXE文件:https://dearx.lanzoui.com/iPar7s7wpre

以上是关于QQ隐藏图原理与C#实现(含源文件)的主要内容,如果未能解决你的问题,请参考以下文章

C#如何实现上传头像,最好能够代码加文字阐述一下实现原理

Moravec(莫拉维克)影像特征点提取(含原理与C代码)

当 listview 行项目中包含隐藏视图时,片段不尊重匹配父高度

多个视图与多个片段

一个简单的QQ隐藏图生成算法

C# winform 窗体左右一部分显示 一部分隐藏