C#实现哈希均值算法处理图片

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Text;

using System.Linq; //list集合的扩展

namespace W

{

        // private static Bitmap ZoomImage(Bitmap bitmap, int destHeight, int destWidth)

        //        {

        //            try

        //            {

        //                System.Drawing.Image sourImage = bitmap;

        //                int width = 0, height = 0;

        //                //按比例缩放           

        //                int sourWidth = sourImage.Width;

        //                int sourHeight = sourImage.Height;

        //                if (sourHeight > destHeight || sourWidth > destWidth)

        //                {

        //                    if ((sourWidth * destHeight) > (sourHeight * destWidth))

        //                    {

        //                        width = destWidth;

        //                        height = (destWidth * sourHeight) / sourWidth;

        //                    }

        //                    else

        //                    {

        //                        height = destHeight;

        //                        width = (sourWidth * destHeight) / sourHeight;

        //                    }

        //                }

        //                else

        //                {

        //                    width = sourWidth;

        //                    height = sourHeight;

        //                }

        //                Bitmap destBitmap = new Bitmap(destWidth, destHeight);

        //                Graphics g = Graphics.FromImage(destBitmap);

        //                g.Clear(Color.Transparent);

        //                //设置画布的描绘质量         

        //                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

        //                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

        //                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

        //                g.DrawImage(sourImage, new Rectangle((destWidth - width) / 2, (destHeight - height) / 2, width, height), 0, 0, sourImage.Width, sourImage.Height, GraphicsUnit.Pixel);

        //                g.Dispose();

        //                //设置压缩质量     

        //                System.Drawing.Imaging.EncoderParameters encoderParams = new System.Drawing.Imaging.EncoderParameters();

        //                long[] quality = new long[1];

        //                quality[0] = 100;

        //                System.Drawing.Imaging.EncoderParameter encoderParam = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);

        //                encoderParams.Param[0] = encoderParam;

        //                sourImage.Dispose();

        //                return destBitmap;

        //            }

        //            catch

        //            {

        //                return bitmap;

        //            }

        //        }

        ////转换为灰度图:

        //        public static Bitmap ToGray(Bitmap bmp, out int avgGray)

        //        {

        //            int sum = 0;

        //            for (int i = 0; i < bmp.Width; i++)

        //            {

        //                for (int j = 0; j < bmp.Height; j++)

        //                {

        //                    //获取该点的像素的RGB的颜色

        //                    Color color = bmp.GetPixel(i, j);

        //                    //利用公式计算灰度值

        //                    int gray = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);

        //                    Color newColor = Color.FromArgb(gray, gray, gray);

        //                    sum += gray;

        //                    bmp.SetPixel(i, j, newColor);

        //                }

        //            }

        //            avgGray = sum / (bmp.Width * bmp.Height);

        //            return bmp;

        //        }

        ////灰度指纹:(64位其实可以换成更高位数,比如用16x16,和8x8相比就要用4个64位进行表示)

        //            Bitmap bmp1 = ZoomImage((Bitmap)img1, 8, 8);

        //            Bitmap bmp2 = ZoomImage((Bitmap)img2, 8, 8);

        //            int avgGray1, avgGray2;

        //            bmp1 = ToGray(bmp1, out avgGray1);

        //            bmp2 = ToGray(bmp2, out avgGray2);

        //            UInt64 Hashdis1 = 0;

        //            UInt64 Hashdis2 = 0;

        //            UInt64 mask = 1;

        //            for (int x = 0; x < 8; x++)

        //            {

        //                for (int y = 0; y < 8; y++)

        //                {

        //                    int m = y * 8 + x;

        //                    Color c1 = bmp1.GetPixel(x, y);

        //                    if (c1.R >= avgGray1)

        //                    {

        //                        Hashdis1 |= mask << m;

        //                    }

        //                    Color c2 = bmp1.GetPixel(x, y);

        //                    if (c2.R >= avgGray2)

        //                    {

        //                        Hashdis2 |= mask << m;

        //                    }

        //                }

        //            }

        ////得到汉明距离:

        //            UInt64 hash= Hashdis1 ^ Hashdis2;

        //            int Hammingdis = 0;

        //            for (int i = 0; i < 64; i++)

        //            {

        //                if ((hash & (mask << i)) != 0)

        //                    Hammingdis++;

        //            }

        //            return Hammingdis;

        ////两个指纹进行异或运算,求不为0的位数(相异为1)



      #region ahash算法

        /*

                均值哈希的基本思路

        1、缩小尺寸:

        去除图片的高频和细节的最快方法是缩小图片,将图片缩小到8x8的尺寸,总共64个像素。不要保持纵横比,只需将其变成8乘8的正方形。这样就可以比较任意大小的图片,摒弃不同尺寸、比例带来的图片差异。

        2、简化色彩:

        将8乘8的小图片转换成灰度图像。

        3、计算平均值:

        计算所有64个像素的灰度平均值。

        4、比较像素的灰度:

        将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

        5、计算hash值:

        将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

        如果图片放大或缩小,或改变纵横比,结果值也不会改变。增加或减少亮度或对比度,或改变颜色,对hash值都不会太大的影响。最大的优点:计算速度快!

        那么完成了以上步骤,一张图片就相当于有了自己的"指纹"了,然后就是计算不同位的个数,也就是汉明距离(例如1010001与1011101的汉明举例就是2,也就是不同的个数)。

        如果汉明距离小于5,则表示有些不同,但比较相近,如果汉明距离大于10则表明完全不同的图片。

        以上就是均值哈希的基本实现思路,总体来说是比较简单的。

        ————————————————

        版权声明:本文为CSDN博主「床长」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

        原文链接:https://blog.csdn.net/jiangjunshow/article/details/100657183

        */

        static public class ImageHashHelper

        {

            static public List<int> mathXList = new List<int>();

            static public List<int> mathYList = new List<int>();


            /// <summary>

            /// 获取缩略图

            /// </summary>

            /// <returns></returns>

            private static Bitmap GetThumbImage(Image image, int w, int h)

            {

                Bitmap bitmap = new Bitmap(w, h);

                Graphics g = Graphics.FromImage(bitmap);

                g.DrawImage(image,

                    new Rectangle(0, 0, bitmap.Width, bitmap.Height),

                    new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);

                return bitmap;

            }

            /// <summary>

            /// 将图片转换为灰度图像

            /// </summary>

            /// <returns></returns>

            private static Bitmap ToGray(Bitmap bmp)

            {

                try

                {

                    Log.write("ImageHashHelper", "GetAvgHash", "bmp.Width;" + bmp.Width + ";bmp.Height:" + bmp.Height);

                    for (int x = 0; x < bmp.Width; x++)

                    {

                        for (int y = 0; y < bmp.Height; y++)

                        {

                            //获取该点的像素的RGB的颜色

                            Color color = bmp.GetPixel(x, y);

                            //利用公式计算灰度值

                            int gray = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);//计算方式1

                            //int gray1 = (int)((color.R + color.G + color.B) / 3.0M);//计算方式2

                            Color newColor = Color.FromArgb(gray, gray, gray);

                            bmp.SetPixel(x, y, newColor);

                        }

                    }

                }

                catch (Exception ex)

                {

                    Log.write("ImageHashHelper", "ToGray", "" + ex.ToString());

                }

                return bmp;

            }

            /// <summary>

            /// 获取图片的均值哈希

            /// </summary>

            /// <returns></returns>

            /// GetAvgHash函数中获取64个像素的灰度值时直接通过了R来获取,因为RGB都是一样的,所以哪一个都可以。

            public static int bitmapl = 32;

            public static int bitmapw = 32;

            static int bitmaps = bitmapl * bitmapw;

            public static int[] GetAvgHash(Bitmap bitmap)

            {

                int[] code = new int[bitmaps];

                try

                {

                    //Log.write("ImageHashHelper", "GetAvgHash", "bitmaps;" + bitmaps + ";bitmapl:" + bitmapl + ";bitmapw:" + bitmapw);

                    Bitmap newBitmap = ToGray(GetThumbImage(bitmap, bitmapl, bitmapw));

                    //计算所有64个像素的灰度平均值。

                    List<int> allGray = new List<int>();

                  //Log.write("ImageHashHelper", "GetAvgHash", "bitmap.Width;" + newBitmap.Width + ";bitmap.Height:" + newBitmap.Height);

                    for (int row = 0; row < newBitmap.Width; row++)

                    {

                        for (int col = 0; col < newBitmap.Height; col++)

                        {

                            allGray.Add(newBitmap.GetPixel(row, col).R);

                        }

                    }

                    double avg = allGray.Average(a => a);//拿到平均值

                    //比较像素的灰度

                    for (int i = 0; i < allGray.Count; i++)

                    {

                        code[i] = allGray[i] >= avg ? 1 : 0;//将比较结果进行组合

                    }

                }

                catch (Exception ex)

                {

                    Log.write("ImageHashHelper", "GetAvgHash", "" + ex.ToString());

                }

                //返回结果

                return code;

            }

            /// <summary>

            /// 获取图片的均值哈希

            /// </summary>

            /// <returns></returns>

            /// GetAvgHash函数中获取64个像素的灰度值时直接通过了R来获取,因为RGB都是一样的,所以哪一个都可以。

            public static int[] GetAvgHash(Bitmap bitmap,List<int> lx,List<int> ly)

            {

                if (lx.Count < 4 || ly.Count < 4) throw new Exception("List<int> lx,ly 必须为四边形的四个点的有效坐标");

                int[] code = new int[bitmaps];

                try

                {

                    Log.write("ImageHashHelper", "GetAvgHash", "bitmaps;" + bitmaps + ";bitmapl:" + bitmapl + ";bitmapw:" + bitmapw);

                    Bitmap newBitmap = ToGray(GetThumbImage(bitmap, bitmapl, bitmapw));

                    //计算所有64个像素的灰度平均值。

                    List<int> allGray = new List<int>();

                    Log.write("ImageHashHelper", "GetAvgHash", "bitmap.Width;" + newBitmap.Width + ";bitmap.Height:" + newBitmap.Height);

                    int codeMack = 0;

                    for (int row = 0; row < newBitmap.Width; row++)

                    {

                        for (int col = 0; col < newBitmap.Height; col++)

                        {


                            if (isInTriangle2(lx[0], ly[0], lx[1], ly[1], lx[2], ly[2], row, col) && isInTriangle2(lx[0], ly[0], lx[3], ly[3], lx[2], ly[2], row, col))

                            {

                                //标记无效


                                code[codeMack++] = -1;

                                allGray.Add(newBitmap.GetPixel(row, col).R);

                                continue;

                            }


                            allGray.Add(newBitmap.GetPixel(row, col).R);

                        }

                    }

                    double avg = allGray.Average(a => a);//拿到平均值

                    //比较像素的灰度

                    for (int i = 0; i < allGray.Count; i++)

                    {

                        if( code[codeMack] == -1)continue;

                        code[i] = allGray[i] >= avg ? 1 : 0;//将比较结果进行组合

                    }

                }

                catch (Exception ex)

                {

                    Log.write("ImageHashHelper", "GetAvgHash", "" + ex.ToString());

                }

                //返回结果

                return code;

            }

            /// <summary>

            /// 对两个AvgHash进行比较

            /// </summary>

            /// <returns></returns>

            public static int Compare(int[] code1, int[] code2)

            {

                int v = 0;

                for (int i = 0; i < bitmaps; i++)

                {

                    if (code1[i] == code2[i])

                    {

                        v++;

                    }

                }

                return v;

            }

            /// <summary>

            /// 对两个AvgHash进行比较

            /// </summary>

            /// <returns></returns>

            /// 移除了去取均值的功能

            public static int getAvg(int[] code)

            {

                int sum = 0;

                for (int i = 0; i < bitmaps; i++)

                {

                    if (code[i] == -1) continue;

                    sum += code[i];

                }

                return sum;

            }

            public static int getAvg4User(Bitmap bitmap)

            {

                return getAvg(GetAvgHash(bitmap));

            }

            public static int getAvg4UserInArea(Bitmap bitmap)

            {

                return getAvg(GetAvgHash(bitmap, mathXList, mathXList));

            }

            #endregion

            #region 判断点是否在四边形内  (判断是否为凸四边形)

            static double Xproduct(double ax, double ay, double bx, double by, double cx, double cy)

            {

                return (bx - ax) * (cy - ay) - (cx - ax) * (by - ay);

            }

            public static bool isInTriangle2(double ax, double ay, double bx, double by, double cx, double cy, double dx, double dy)

            {

                return (Xproduct(ax, ay, bx, by, dx, dy) >= 0 && Xproduct(bx, by, cx, cy, dx, dy) >= 0 && Xproduct(cx, cy, ax, ay, dx, dy) >= 0) ||

                      (Xproduct(ax, ay, bx, by, dx, dy) <= 0 && Xproduct(bx, by, cx, cy, dx, dy) <= 0 && Xproduct(cx, cy, ax, ay, dx, dy) <= 0);

            }

            #endregion

            public static void getMathPoint(List<int> pointXList, List<int> pointYList)

            {

                if (pointYList.Count >= 4 && pointXList.Count >= 4)

                {

                    mathXList = new List<int>();

                    mathYList = new List<int>();

                    int maxY = pointYList[0];

                    int maxX = pointXList[0];

                    int minX = pointXList[0];

                    int minY = pointYList[0];

                    foreach (int i in pointXList)

                    {

                        if (i > maxX)

                        {

                            maxX = i;

                        }

                        if (i < minX)

                        {

                            minX = i;

                        }

                    }

                    foreach (int j in pointYList)

                    {

                        if (j > maxY)

                        {

                            maxY = j;

                        }

                        if (j < minY)

                        {

                            minY = j;

                        }

                    }

                    for (int i = 0; i < pointXList.Count; i++)

                    {

                        pointXList[i] -= minX;

                    }

                    for (int i = 0; i < pointYList.Count; i++)

                    {

                        pointYList[i] -= minY;

                    }

                    for (int i = 0; i < pointXList.Count; i++)

                    {

                        if (pointXList[i] == 0)

                        {

                            mathXList.Add(0);

                        }

                        else

                        {

                            mathXList.Add(ImageHashHelper.bitmapw * ImageHashHelper.bitmapw / pointXList[i]);

                        }

                    }

                    for (int i = 0; i < pointYList.Count; i++)

                    {

                        if (pointYList[i] == 0)

                        {

                            mathYList.Add(0);

                        }

                        else

                        {

                            mathYList.Add(ImageHashHelper.bitmapl * ImageHashHelper.bitmapl / pointYList[i]);

                        }

                    }

                }

                else

                {

                    return;

                }

            }

        }


}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359