須要最新源碼,或技術提問,請加QQ羣:538327407git
個人各類github 開源項目和代碼:https://github.com/linbin524github
1、需求 算法
須要針對藝術品 局部和全圖進行相識度比對,從而識別圖片的真僞。數組
2、技術思路性能
經過AI 算法,查找兩張圖片的類似點,特徵點的比對比例分析,若是達到90% 以上,默認相同或者相識。this
3、技術儲備spa
FREAK算法3d
簡介
FREAK算法是2012年CVPR上《FREAK: Fast Retina Keypoint》文章中,提出來的一種特徵提取算法,也是一種二進制的特徵描述算子。rest
它與BRISK算法很是類似,我的以爲就是在BRISK算法上的改進,關於BRISK算法詳見上一篇博文:BRISK特徵提取算法。FREAK依然具備尺度不變性、旋轉不變性、對噪聲的魯棒性等。code
FREAK算法
採樣模式
在BRISK算法中,採樣模式是均勻採樣模式(在同一圓上等間隔的進行採樣);FREAK算法中,採樣模式發生了改變,它採起了更爲接近於人眼視網膜接收圖像信息的採樣模型。圖中展現了人眼視網膜拓撲結構,Fovea區域主要是對高進度的圖像信息進行處理,Para主要是對低精度的圖像信息進行處理。採樣點爲:六、六、六、六、六、六、六、1,那個1是特徵點。
表示採樣點對Pa中前一個採樣點的像素值,同理,表示後一個採樣點的像素值。
固然獲得特徵點的二進制描述符後,也就算完成了特徵提取。可是FREAK還提出,將獲得的Nbit二進制描述子進行篩選,但願獲得更好的,更具備辨識度的描述子,也就是說要從中去粗取精。(也就是降維)
一、創建矩陣D,D的每一行是一個FREAK二進制描述符,即每一行有N個元素;在上圖的採樣模式中公有43個採樣點,能夠產生N=43*(43-1)/2=903個採樣點對,也就是說矩陣D有903行列(修改於:2015-11-15);
二、對矩陣D的每一列計算其均值,因爲D中元素都是0/1分佈的,均值在0.5附近說明該列具備高的方差;
三、每一列都有一個均值,以均值最接近0.5的排在第一位,均值離0.5越遠的排在越靠後,對列進行排序;
四、選取前512列做爲最終的二進制描述符。(也能夠是25六、12八、6四、32等)
小結:最原始的二進制長度爲903,固然這包含了許多冗餘或粗糙的信息,因此根據必定的方法取N個二進制長度,方法是創建矩陣D。假如提取到228個特徵點,那麼矩陣D應該是228行*903列,而後通過計算均值,將每一個列從新排序,選取前N列,這個矩陣D就是228*N的矩陣了。固然這個N我在文中寫得是512,你也能夠是25六、12八、6四、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 }; }
效果比對
中國名畫
結論