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 mathXList = new List();

            static public List mathYList = new List();


            ///

            /// 获取缩略图

            ///

            ///

            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;

            }

            ///

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

            ///

            ///

            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;

            }

            ///

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

            ///

            ///

            /// 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 allGray = new List();

                  //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;

            }

            ///

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

            ///

            ///

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

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

            {

                if (lx.Count < 4 || ly.Count < 4) throw new Exception("List 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 allGray = new List();

                    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;

            }

            ///

            /// 对两个AvgHash进行比较

            ///

            ///

            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;

            }

            ///

            /// 对两个AvgHash进行比较

            ///

            ///

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

            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 pointXList, List pointYList)

            {

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

                {

                    mathXList = new List();

                    mathYList = new List();

                    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;

                }

            }

        }


}

你可能感兴趣的:(C#实现哈希均值算法处理图片)