1. 程式人生 > >基於開源算法實現圖片比對進行圖片全圖和局部 比對

基於開源算法實現圖片比對進行圖片全圖和局部 比對

== transform col reads img 希望 object 兩個 最新

需要最新源碼,或技術提問,請加QQ群:538327407,由於源碼在不斷完善,會在之後同步到AI開源項目中

一、需求

需要針對藝術品 局部和全圖進行相識度比對,從而識別圖片的真偽。

二、技術思路

通過AI 算法,查找兩張圖片的相似點,特征點的比對比例分析,如果達到90% 以上,默認相同或者相識。

三、技術儲備

FREAK算法

簡介
FREAK算法是2012年CVPR上《FREAK: Fast Retina Keypoint》文章中,提出來的一種特征提取算法,也是一種二進制的特征描述算子。

它與BRISK算法非常相似,個人覺得就是在BRISK算法上的改進,關於BRISK算法詳見上一篇博文:BRISK特征提取算法。FREAK依然具有尺度不變性、旋轉不變性、對噪聲的魯棒性等。

FREAK算法


采樣模式
在BRISK算法中,采樣模式是均勻采樣模式(在同一圓上等間隔的進行采樣);FREAK算法中,采樣模式發生了改變,它采取了更為接近於人眼視網膜接收圖像信息的采樣模型。圖中展示了人眼視網膜拓撲結構,Fovea區域主要是對高進度的圖像信息進行處理,Para主要是對低精度的圖像信息進行處理。采樣點為:6、6、6、6、6、6、6、1,那個1是特征點。

技術分享圖片

表示采樣點對Pa中前一個采樣點的像素值,同理,表示後一個采樣點的像素值。

當然得到特征點的二進制描述符後,也就算完成了特征提取。但是FREAK還提出,將得到的Nbit二進制描述子進行篩選,希望得到更好的,更具有辨識度的描述子,也就是說要從中去粗取精。(也就是降維)

1、建立矩陣D,D的每一行是一個FREAK二進制描述符,即每一行有N個元素;在上圖的采樣模式中公有43個采樣點,可以產生N=43*(43-1)/2=903個采樣點對,也就是說矩陣D有903行列(修改於:2015-11-15);

2、對矩陣D的每一列計算其均值,由於D中元素都是0/1分布的,均值在0.5附近說明該列具有高的方差;

3、每一列都有一個均值,以均值最接近0.5的排在第一位,均值離0.5越遠的排在越靠後,對列進行排序;

4、選取前512列作為最終的二進制描述符。(也可以是256、128、64、32等)

小結:最原始的二進制長度為903,當然這包含了許多冗余或粗糙的信息,所以根據一定的方法取N個二進制長度,方法是建立矩陣D。假如提取到228個特征點,那麽矩陣D應該是228行*903列,然後經過計算均值,將每個列重新排序,選取前N列,這個矩陣D就是228*N的矩陣了。當然這個N我在文中寫得是512,你也可以是256、128、64、32這些都是可以的。 最終D的每一行仍然是一個特征點的描述符,只是更短小精悍而已,即降低了維度。(添加於:2016-01-11)

由於FREAK描述符自身的圓形對稱采樣結構使其具有旋轉不變性,采樣的位置好半徑隨著尺度的變化使其具有尺度不變性,對每個采樣點進行高斯模糊,也具有一定的抗噪性能,像素點的強度對比生成二進制描述子使其具有光照不變性。因此由上述產生的二進制描述子可以用來進行特征匹配。在匹配之前,再補充一下特征點的方向信息。

特征方向
由於特征點周圍有43個采樣點,可產生43*(43-1)/2=903個采樣點對,FREAK算法選取其中45個長的、對稱的采樣點對來提取特征點的方向,采樣點對如下:

技術分享圖片

用O表示局部梯度信息,M表示采樣點對個數,G表示采樣點對集合,Po表示采樣點對的位置,則:

技術分享圖片

同BRISK算法,可得到特征點的方向。

特征匹配
在特征描述中,我們得到了512bit的二進制描述符,該描述符的列是高方差——>低方差的排列,而高方差表征了模糊信息,低方差表征了細節信息,與人眼視網膜相似,人眼先處理的是模糊信息,再處理細節信息。因此,選取前128bit即16bytes進行匹配(異或),若兩個待匹配的特征點前16bytes距離小於設定的閾值,則再用剩余的位信息進行匹配。這種方法可以剔除掉90%的不相關匹配點。註意:這裏的16bytes的選取是建立在並行處理技術(SIMD)上的,並行處理技術處理16bytes與處理1bytes的時間相同;也就是說,16bytes並不是固定的,如果你的並行處理技術能處理32bytes與處理1bytes的時間相同的話,那麽你也可以選取前32bytes。

技術實現

代碼

  public IEnumerable<TFeature>[] Transform(Bitmap[] input)
        {
            return Transform(input, new IList<TFeature>[input.Length]);
        }

        public IEnumerable<TFeature>[] Transform(Bitmap[] input, IEnumerable<TFeature>[] result)
        {
            for (int i = 0; i < input.Length; i++)
                result[i] = Transform(input[i]);
            return result;
        }

 private void btnFreak_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(label1.Text) || string.IsNullOrWhiteSpace(label2.Text))
            {

                MessageBox.Show("請先上傳比對圖片!");

                return;
            }

          
            Concatenate concatenate1 = new Concatenate(img1);
            pictureBox.Image = concatenate1.Apply(img2);

         
            FastRetinaKeypointDetector freak = new FastRetinaKeypointDetector();
            //獲取兩張圖片的采樣點
            keyPoints1 = freak.Transform(img1);
            keyPoints2 = freak.Transform(img2);

       
            Bitmap img1mark = new PointsMarker(keyPoints1.Select(x => (IFeaturePoint)x).ToList()).Apply(img1);
            Bitmap img2mark = new PointsMarker(keyPoints2.Select(x => (IFeaturePoint)x).ToList()).Apply(img2);

           
            Concatenate concatenate2 = new Concatenate(img1mark);
            pictureBox.Image = concatenate2.Apply(img2mark);
        }
    private void btnCorrelation_Click(object sender, EventArgs e)
        {
            if (keyPoints1 == null)
            {
                MessageBox.Show("Please, click FREAK button first! :-)");
                return;
            }



            Thread t1 = new Thread(new ThreadStart(() =>
            {
                this.Invoke(new Action(() =>
                {
                    lb_result.Text = "檢測中......";
                }));

                var matcher = new KNearestNeighborMatching<byte[]>(5, new Hamming());
                IntPoint[][] matches = matcher.Match(keyPoints1, keyPoints2);//返回的結果值一般都是相同的,數組中
                string result1 = (((decimal)matches[0].Count() / (decimal)keyPoints1.Count()) * 100).ToString("#0.00");
                string result2 = (((decimal)matches[1].Count() / (decimal)keyPoints2.Count()) * 100).ToString("#0.00");
                string tempString = string.Format("原圖采樣點:{0},對比圖采樣點:{1},匹配個數點:{2},{3},匹配比例:{4}%,{5}%",
                    keyPoints1.Count(), keyPoints2.Count(), matches[0].Count(), matches[1].Count(), result1, result2);

                this.Invoke(new Action(() =>
                {
                    lb_result.Text = tempString;
                    lb_result.BackColor = Color.Azure;
                
                    correlationPoints1 = matches[0];
                    correlationPoints2 = matches[1];

                
                    Concatenate concat = new Concatenate(img1);
                    Bitmap img3 = concat.Apply(img2);

                   
                    PairsMarker pairs = new PairsMarker(
                        correlationPoints1, // Add image1‘s width to the X points to show the markings correctly
                        correlationPoints2.Apply(p => new IntPoint(p.X + img1.Width, p.Y)));

                    pictureBox.Image = pairs.Apply(img3);

                }));

            }));

            t1.Start();

        }

  private IntPoint[][] match(IFeaturePoint<T>[] points1, IFeaturePoint<T>[] points2)
        {
            if (points1.Length == 0 || points2.Length == 0)
                throw new ArgumentException("Insufficient number of points to produce a matching.");


            bool swap = false;



            if (points2.Length > points1.Length)
            {
                var aux = points1;
                points1 = points2;
                points2 = aux;
                swap = true;
            }


        
            T[] features1 = new T[points1.Length];
            for (int i = 0; i < features1.Length; i++)
                features1[i] = points1[i].Descriptor;

            T[] features2 = new T[points2.Length];
            for (int i = 0; i < features2.Length; i++)
                features2[i] = points2[i].Descriptor;

            var knn = CreateNeighbors(features1);

            double[] scores = new double[features2.Length];
            int[] labels = new int[features2.Length];
            knn.Score(features2, ref labels, result: scores);

            int[] bestMatch = new int[points1.Length];
            double[] bestScore = new double[points1.Length];
            for (int i = 0; i < bestScore.Length; i++)
                bestScore[i] = Double.PositiveInfinity;

  
            for (int j = 0; j < labels.Length; j++)
            {
                int i = labels[j];

                if (scores[j] > Threshold)
                {
                    if (scores[j] < bestScore[i])
                    {
                        bestScore[i] = scores[j];
                        bestMatch[i] = j;
                    }
                }
            }


            var p1 = new List<IntPoint>(bestScore.Length);
            var p2 = new List<IntPoint>(bestScore.Length);

           
            for (int i = 0; i < bestScore.Length; i++)
            {
                IFeaturePoint<T> pi = points1[i];

                if (bestScore[i] != Double.PositiveInfinity)
                {
                    int j = bestMatch[i];
                    IFeaturePoint<T> pj = points2[j];
                    p1.Add(new IntPoint((int)pi.X, (int)pi.Y));
                    p2.Add(new IntPoint((int)pj.X, (int)pj.Y));
                }
            }

            IntPoint[] m1 = p1.ToArray();
            IntPoint[] m2 = p2.ToArray();

          

            if (swap)
                return new IntPoint[][] { m2, m1 };
            return new IntPoint[][] { m1, m2 };
        }

效果比對

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

中國名畫

技術分享圖片

技術分享圖片

結論

技術分享圖片

基於開源算法實現圖片比對進行圖片全圖和局部 比對