對圖像邊緣進行隨機均勻采樣的C#算法實現

http://www.cnblogs.com/xiaotie/archive/2010/04/18/1714988.html

圖像邊緣含有圖像形狀的豐富信息,然而,圖像邊緣有時所含的像素點還是太多,很多情況下需要繼續精簡(比如,使用 ShapeContext 進行形狀匹配),於是就出現一個問題:如何從圖像邊緣上提取出N個點,使這N個點最具有代表性呢?一個很直觀的思路是:

(1)這N個點要在圖像邊緣上;

(2)最近鄰的兩點之間要盡量分散開。

如,圖像為:

image

 

需要設計一個采樣算法,使它得到下面的結果:

image

 

==== 實現 ====

1,將圖像加載,轉換為ImageU8類(參見《發布我的高性能純C#圖像處理基本類,順便也挑戰一下極限。:)》),方便下一步處理。

2,獲得全部邊緣像素的位置。

在《重新認識C#: 玩轉指針》的一文基礎上新添加一個擴展方法:

ForEach 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public unsafe delegate void ActionOnPosition(Int32 x, Int32 y, TPixel* p); 
 public unsafe static void ForEach(this UnmanagedImage<TPixel> src, ActionOnPosition handler) 
 { 
     Int32 width = src.Width; 
     Int32 height = src.Height; 
 
     TPixel* p = (TPixel*)src.StartIntPtr; 
     for (Int32 r = 0; r < height; r++) 
     { 
         for (Int32 w = 0; w < width; w++) 
         { 
             handler(w, r, p); 
             p++; 
         } 
     } 
 }

假設灰度值>0的點是邊緣點,通過下面的兩行代碼就可以取得所有的邊緣點:

1 List<Point> points = new List<Point>();
2 img.ForEach((x, y, p) => { if (*> 0) points.Add(new Point(x, y)); }); 

簡潔吧!

3,隨機抽樣

這一步參考了Jitendra Malik的實現,下面是他的matlab代碼:

matlab 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 function [xi,yi,ti]=get_samples_1(x,y,t,nsamp); 
 % [xi,yi,ti]=get_samples_1(x,y,t,nsamp); 
 % 
 % uses Jitendra's sampling method 
 
 N=length(x); 
 k=3; 
 Nstart=min(k*nsamp,N); 
 
 ind0=randperm(N); 
 ind0=ind0(1:Nstart); 
 
 xi=x(ind0); 
 yi=y(ind0); 
 ti=t(ind0); 
 xi=xi(:); 
 yi=yi(:); 
 ti=ti(:); 
 
 d2=dist2([xi yi],[xi yi]); 
 d2=d2+diag(Inf*ones(Nstart,1)); 
 
 s=1; 
 while s 
    % find closest pair 
    [a,b]=min(d2); 
    [c,d]=min(a); 
    I=b(d); 
    J=d; 
    % remove one of the points 
    xi(J)=[]; 
    yi(J)=[]; 
    ti(J)=[]; 
    d2(:,J)=[]; 
    d2(J,:)=[]; 
    if size(d2,1)==nsamp 
       s=0; 
    end 
 end

這段代碼原理是:檢查全部點對的距離,每次去除距離最小的點對中的一個點,直至剩下的點的數量達到要取樣的點的數量N。如果點的總量M>>N,這樣的操作是很費時間的,為了減少計算量,當M>>N時,隨機取3N個點,對這3N個點進行操作即可。需要說明的是,即使M<3N,在具體抽樣之前,也需要對樣本進行隨機打亂,這樣才能使得後面刪除點對中的某一個點這一行為具有隨機性,不然的話,一條直線上的點恐怕會刪的只剩尾部一個點。

下面是我的實現,實現方法和Jitendra Malik的略有不同,Jitendra Malik是使用矩陣來計算的,我使用List來計算:

RandomUniformSample 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public static List<Point> RandomUniformSample(List<Point> srcList, Int32 count) 
 { 
     List<Point> resultList = new List<Point>(count); 
     Int32 numNeedRemoved = srcList.Count - count; 
     if (numNeedRemoved < 1) 
     { 
         resultList.AddRange(srcList); 
         return resultList; 
     } 
 
     // 將序列隨機打亂。RandomPermute是擴展方法。 
     srcList.RandomPermute(); 
 
     // 如果srcList的數量巨大,則隨機抽取部分點。由於上面已經隨機大亂了,使用GetRange方法便可。 
     if (srcList.Count > count * 3) 
     { 
         srcList = srcList.GetRange(0, count * 3); 
         numNeedRemoved = srcList.Count - count; 
     } 
 
     // mask 記錄點的刪除狀況。若位於mask[i]=1,則代表第i個點未被刪除,若為0,則代表已刪除 
     Int32[] mask = new Int32[srcList.Count]; 
     for(int i=0; i < mask.Length; i++) 
     { 
         mask[i] = 1; 
     } 
 
     // 計算全部點對,並計算距離的平方 
     List<PairDistance> list = new List<PairDistance>(srcList.Count*(1 + srcList.Count/2)); 
     for (int i = 0; i < srcList.Count; i++) 
     { 
         for (int j = i + 1; j < srcList.Count; j++) 
         { 
             Point p0 = srcList[i]; 
             Point p1 = srcList[j]; 
             Int32 deltaX = p0.X - p1.X; 
             Int32 deltaY = p0.Y - p1.Y; 
             list.Add(new PairDistance{ Index0 = i, Index1= j, DistanceSquare = deltaX*deltaX + deltaY * deltaY}); 
         } 
     } 
 
     // 進行排序 
     list.Sort(); 
 
     // 遍歷list,直至足夠的點被移除 
     int startIndex = 0; 
     while (numNeedRemoved > 0) 
     { 
         PairDistance pair = list[startIndex]; 
 
         // 如果點對的兩點均未被移除,則將其中一點移除。 
         if (mask[pair.Index0] != 0 && mask[pair.Index1] != 0) 
         { 
             mask[pair.Index1] = 0; 
             numNeedRemoved--; 
         } 
         startIndex++; 
     } 
 
     // 根據mask中的記錄,得到全部采樣點 
     for (int i = 0; i < mask.Length; i++) 
     { 
         if (mask[i] == 1) resultList.Add(srcList[i]); 
     } 
     return resultList; 
 }

其中:

PairDistance 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 private class PairDistance : IComparable<PairDistance> 
 { 
     public Int32 Index0 {get;set;} 
     public Int32 Index1 { get; set; } 
     public Int32 DistanceSquare { get; set; } 
 
     #region IComparable<PairDistance> Members 
 
     public int CompareTo(PairDistance other) 
     { 
         return DistanceSquare.CompareTo(other.DistanceSquare); 
     } 
 
     #endregion 
 }

RandomPermute 


Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public static void RandomPermute<T>(this IList<T> data) 
 { 
     int count = data.Count; 
     for (int i = 0; i < count; i++) 
     { 
         int index0 = Random.Next(0, count - i); 
         int index1 = count - i - 1; 
         T tmp = data[index0]; 
         data[index0] = data[index1]; 
         data[index1] = tmp; 
     } 
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值