http://www.cnblogs.com/xiaotie/archive/2010/04/18/1714988.html
圖像邊緣含有圖像形狀的豐富信息,然而,圖像邊緣有時所含的像素點還是太多,很多情況下需要繼續精簡(比如,使用 ShapeContext 進行形狀匹配),於是就出現一個問題:如何從圖像邊緣上提取出N個點,使這N個點最具有代表性呢?一個很直觀的思路是:
(1)這N個點要在圖像邊緣上;
(2)最近鄰的兩點之間要盡量分散開。
如,圖像為:
需要設計一個采樣算法,使它得到下面的結果:
==== 實現 ====
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 (*p > 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;
}
}