zoukankan      html  css  js  c++  java
  • 查找类似图片(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;

          }  

            

  • 相关阅读:
    数据库
    java语法
    《Lucene实战(第2版)》 配书代码在IDEA下的编译方法
    lucene学习
    社交关系调研(费)
    微博开发平台java SDK demo学习之examples(demo)
    微博开发平台java SDK demo学习之friendships
    F. Classical? (数论 + 思维 + 推理 + 容斥)
    石子合并问题,经典区间DP
    luoguP2048 [NOI2010]超级钢琴
  • 原文地址:https://www.cnblogs.com/barrysgy/p/2267867.html
Copyright © 2011-2022 走看看