悉野小楼

导航

查找类似图片(Find Similar Image)

/// <summary>
        
/// 返回一个16位hash码(先将图片转成灰度, 再分块得到每个分块的灰度值(0-255), 再开方, 得0-15值. 正好用16进制数表示
        
/// </summary>
        
/// <param name="strPicPath">图片路径</param>
        
/// <returns>16位hash码</returns>
        public string GetPictureHashCode(string strPicPath)
        {
            try
            {
                //如果传的是字节数组可以用MemoryStream来读取
                int iHBlockNum = 4//图片水平切块数
                int iVBlockNum = 4//图片竖直切块数
                long[] arrayBright = new long[iHBlockNum * iVBlockNum]; //用来存放每个块里面所有灰度图像亮度值之和
                int[] arrayPixelNumber = new int[iHBlockNum * iVBlockNum]; //用来存放每个块的像素个数
                
// Create a new bitmap.
                Bitmap bitmap = new Bitmap(strPicPath);
                Rectangle rect = new Rectangle(00, bitmap.Width, bitmap.Height);

                // Lock the bitmap's bits. 
                
//转成24rgb颜色 24色就是由r g b, 三个颜色, 每个颜色各用一字节(8位)表示亮度
                BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                //图片一行象素所占用的字节  本应是3*bitmap.Width的, 但有内存补齐, 一般是4的位数, 实际是大于3*bitmap.Width
                int iStride = bmpData.Stride;

                // Get the address of the first line.
                IntPtr ptr = bmpData.Scan0;

                // Declare an array to hold the bytes of the bitmap.
                int iBytes = iStride * bitmap.Height;
                byte[] rgbValues = new byte[iBytes];

                // Copy RGB values into the Array
                Marshal.Copy(ptr, rgbValues, 0, iBytes);
                // Unlock the bits.
                bitmap.UnlockBits(bmpData);

                //水平方向每个块的长度 (这里全用整数来除, 得到的值也是整数, bmpData.Width / iOffsetX 的值不会超过 iHBlockNum)
                int iOffsetX = bmpData.Width / iHBlockNum;
                //竖直方向每个块的长度
                int iOffsetY = bmpData.Height / iVBlockNum;
                for (int y = 0; y < bmpData.Height; ++y)
                {
                    for (int x = 0; x < bmpData.Width; ++x)
                    {
                        //图像(x, y)坐标坐标中第1个像素中rgbValues中的索引位置(这儿x,y是从0开始记的)
                        
//rgbValues每行是扫描宽个字节, 不是bitmap.Width * 3
                        int iThird = iStride * y + 3 * x;
                        //计算灰度值
                        byte avg = (byte)((rgbValues[iThird] + rgbValues[iThird + 1] + rgbValues[iThird + 2]) / 3);
                        //灰度图
                        
//rgbValues[iThird] = avg;
                        
//rgbValues[iThird + 1] = avg;
                        
//rgbValues[iThird + 2] = avg;

                        
//计算点在哪个区里面 y / iOffsetY 是取除后的整数值0.9算0
                        
//水平最后一个区块比普通块大一点, 竖直方向最后一块高度也同理
                        
//如: 长为111的图, 水分四块0-26,27-53,54-80,81-110(最后一块水平最大可能是普通块的近两倍)
                        int iBlockX = x / iOffsetX;
                        iBlockX = iBlockX == iHBlockNum ? iHBlockNum - 1 : iBlockX; //超出水平块放到最后一块

                        int iBlockY = y / iOffsetY;
                        iBlockY = iBlockY == iVBlockNum ? iVBlockNum - 1 : iBlockY;//超出竖直块放到最后一块
                        int iBlockNum = iBlockY * iHBlockNum + iBlockX; //像素所在区块
                        arrayBright[iBlockNum] += avg; //三像素平均值
                        arrayPixelNumber[iBlockNum]++; //第iBlockNum块像素个数加1
                    }
                }
                //生成字符串
                string strHash = "";
                for (int i = 0; i < arrayBright.Length; ++i)
                {
                    //如果不开方就是一个字节,0-255, 两位16进制数, 
                    
//调用Convert.ToString(long, 16)会生成ff, ef, a(小数时高位省略了) 等
                    byte bDigit = (byte)Math.Sqrt(arrayBright[i] / arrayPixelNumber[i]);
                    strHash += Convert.ToString(bDigit, 16);
                }
                return strHash;
            }
            catch 
            {
                return null;
            }
        }
//================另一个函数, 原理一样, 不是我写的, 像素属于哪一块时判断方法不一样. 这个函数运行时间只要上面函数运行时间的一半, 注释少了些
public  string GetPictureHashCodeOld(string strPicPath)
        {
            string ret = null;
            try
            {
                Bitmap bmp = new Bitmap(strPicPath);
                int areanum = 16;
                int moietynum = 16;
                int h_int = bmp.Height;
                int w_int = bmp.Width;
                int[,] pic = new int[bmp.Width, bmp.Height];
                int max = 0;
                int min = 255;
                BitmapData bitmapData = bmp.LockBits(new Rectangle(00, w_int, h_int), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                int stride = bitmapData.Stride;
                int nOffset = stride - (int)bitmapData.Width * 3;
                IntPtr ptr = bitmapData.Scan0;
                int bytes = bitmapData.Stride * bmp.Size.Height;
                byte[] rgbValues = new byte[bytes];
                System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
                bmp.UnlockBits(bitmapData);
                int p = 0;
                //------------------------
                int red, green, blue;
                for (int y = 0; y < bitmapData.Height; ++y)
                {
                    for (int x = 0; x < bitmapData.Width; ++x)
                    {
                        blue = rgbValues[p];//这里要注意,数据排列按照BGR排列
                        p++;
                        green = rgbValues[p];
                        p++;
                        red = rgbValues[p];
                        p++;
                        byte pointbyte = (byte)((red + green + blue) / 3);//转化成灰度
                        if (max < pointbyte)
                            max = pointbyte;
                        if (min > pointbyte)
                            min = pointbyte;
                        pic[x, y] = pointbyte;
                    }
                    p += nOffset;
                }
                int n = max - min + 1;
                int m = n / moietynum;
                int lost = n % moietynum;
                if (lost != 0)
                {
                    m++;
                }
                for (int i = 0; i < w_int; i++)
                {
                    for (int j = 0; j < h_int; j++)
                    {

                        pic[i, j] = (pic[i, j] - min) / m;

                    }
                }
                int area = (int)System.Math.Sqrt(areanum);
                int w = w_int / area;
                int h = h_int / area;

                int sum = 0;
                int i_s = 0, i_e = w;
                int j_s = 0, j_e = h;
                int ln = 0;
                int rn = 0;
                string avgstr = "";
                while (rn < area)
                {
                    while (ln < area)
                    {
                        sum = 0;
                        for (int i = i_s; i < i_e; i++)
                        {
                            for (int j = j_s; j < j_e; j++)
                            {
                                sum += pic[i, j];
                            }
                        }
                        i_s += w;
                        i_e += w;
                        int area_avg = sum / (w * h);
                        avgstr += Convert.ToString(area_avg, 16);
                        ln++;
                    }
                    j_s += h;
                    j_e += h;
                    i_s = 0;
                    i_e = w;
                    ln = 0;
                    rn++;
                }
                ret = avgstr;
                return ret;
            }
            catch
            {
                ret = null;
                return ret;
            }
        }
两个函数生成的16位字符串码不一样, 比较时用同一个函数. 比较字符是否相差太大
 /// <summary>
        
/// 根据所给的限定值比较两图是否相同
        
/// </summary>
        
/// <param name="strMark1">图一16位字符编码</param>
        
/// <param name="strMark2">图二16位字符编码</param>
        
/// <param name="iCharDiff">单个字符最大偏差值</param>
        
/// <param name="iTotalDiff">最多几个字符偏差</param>
        
/// <param name="iTotalLimit">字符偏差的允许的总和</param>
        
/// <returns>相同: true; 不同:false</returns>
        public static bool IsSimilarPicture(string strMark1, string strMark2, int iCharDiff, int iTotalDiffNumber, int iTotalLimit)
        {
            Dictionary<intint> dictAppearTimes = new Dictionary<intint>();//记录每个数字出现的次数
            if (strMark1.Length != 16 || strMark2.Length != 16)
                return false;
            int[] b1 = new int[16];
            int[] b2 = new int[16];

            int iTotal = 0;//偏差总和
            
//转成10进制整数比较
            for (int j = 0; j < 16; ++j)
            {
                //初始数组
                b1[j] = Convert.ToInt32(strMark1[j].ToString(), 16);
                b2[j] = Convert.ToInt32(strMark2[j].ToString(), 16);
                //统计各个数字出现的次数
                
//b1数组中第j个数字出现的次数
                if (dictAppearTimes.Keys.Contains(b1[j]))
                    dictAppearTimes[b1[j]]++;
                else
                    dictAppearTimes.Add(b1[j], 1);//头次出现
            }
            int iMaxCount = dictAppearTimes.Values.Max();
            //灰图控制
            
//将黑色位超过10
            if (dictAppearTimes.Keys.Contains(0) && dictAppearTimes[0] > 10)
            {
                iCharDiff = 1;
                iTotalDiffNumber = 1;
                iTotalLimit = 1;
            }
            //如果出现颜色过少
            else if (dictAppearTimes.Keys.Count < 4)
            {
                iCharDiff = 1;
                iTotalDiffNumber = 1;
                iTotalLimit = 1;
            }
            //单种颜色超过8位
            else if (iMaxCount > 8)
            {
                iCharDiff = 1;
                iTotalDiffNumber = 2;
                iTotalLimit = 1;
            }

            int iDiffNumber = 0//发生不等的字符个数
            for (int i = 0; i < 16; ++i)
            {
                //比较单个字符是否超过一定值 绝对值超过单个位最大值
                int iCompare = Math.Abs(b1[i] - b2[i]);
                if (iCompare > iCharDiff)
                    return false;
                //两值不等
                if (b1[i] != b2[i])
                {
                    //比较总值是否超过了最大值
                    iTotal += iCompare;
                    if (iTotal > iTotalLimit)
                        return false;
                    //字符不同的个数
                    ++iDiffNumber;
                    if (iDiffNumber > iTotalDiffNumber)//超过最大值了
                        return false;
                }
            } 
            return true;

      }  

        

posted on 2011-11-29 17:05  悉野  阅读(1873)  评论(2编辑  收藏  举报