2010-06-24 19 views
6

更新查找繪區域:我試圖去拉一點點混亂這個職位,更簡潔的總結一下。如果需要,請參閱原始編輯。算法在畫布

我目前正在試圖找到一個系列單彩色斑點的一個位圖的畫布。

例如我試圖跟蹤位圖的一個例子如下所示: alt text http://www.refuctored.com/polygons.bmp

成功後跟蹤3個斑點的輪廓的圖像上,我將有舉行綁在一個斑點的顏色類點列表表示blob的輪廓(不是blob內的所有像素)。

我遇到的問題是在相鄰的像素具有比前一像素之外,沒有其他的周圍像素的實例的邏輯。

e.g頂端示例將跟蹤罰款,但因爲像素具有不到哪去,因爲以前的像素已經使用第二會失敗。

alt text http://www.refuctored.com/error.jpg

我跟蹤左到右,上到下的,在直角利於對角線角度。我必須能夠根據我提取的數據重新繪製區域的精確副本,因此列表中的像素必須按照正確的順序才能使副本工作。

到目前爲止,我已經嘗試充滿了失敗,拉我的頭髮試圖每個解決問題的時間重寫算法有點不同的幾天。迄今爲止,我一直沒有成功。有沒有其他人有類似的問題,像我的誰有一個很好的算法來找到邊緣?

回答

2

避免這些死衚衕的一個簡單的技巧是在追蹤它之前,使用最近鄰居縮放算法將要跟蹤的圖像的大小加倍。就像那樣,你永遠不會得到單條。

另一種方法是使用行進平方算法 - 但似乎仍然有一個或兩個的情況下失敗:http://www.sakri.net/blog/2009/05/28/detecting-edge-pixels-with-marching-squares-algorithm/

+0

將尺寸加倍 - 這是一個好主意。我很驚訝,沒有跨過我的腦海。我會檢查出來的! – 2010-06-28 18:44:54

0

而不是使用遞歸,使用堆棧。

僞代碼:

Add initial pixel to polygon 
Add initial pixel to stack 
while(stack is not empty) { 
    pop pixel off the stack 
    foreach (neighbor n of popped pixel) { 
     if (n is close enough in color to initial pixel) { 
      Add n to polygon 
      Add n to stack 
     } 
    } 
} 

這將佔用更少的內存比使用遞歸相同的解決方案。

1

你看過斑點檢測算法嗎?例如,如果您可以將OpenCV集成到您的應用程序中,則可以使用http://opencv.willowgarage.com/wiki/cvBlobsLib。結合閾值爲圖像中的每種顏色(或顏色範圍)創建二進制圖像,您可以輕鬆找到相同顏色的斑點。對圖像中的每種顏色重複一次,並且您有按顏色排序的斑點列表。

如果你不能直接使用OpenCV,可能是該庫引用的論文(F.Chang等人的「一種使用輪廓跟蹤技術的線性時間分量標記算法」)將提供一種查找斑點的好方法。

0

只需將您的'圖像'發送到BuildPixelArray函數,然後調用FindRegions。 之後,'顏色'變量將保存每個列表成員中的顏色列表和像素座標。

我已經從我的一個項目中複製了源代碼,可能會有一些未定義的變量或語法錯誤。

public class ImageProcessing{ 
    private int[,] pixelArray; 
    private int imageWidth; 
    private int imageHeight; 
    List<MyColor> colors; 

    public void BuildPixelArray(ref Image myImage) 
    { 
     imageHeight = myImage.Height; 
     imageWidth = myImage.Width; 
     pixelArray = new int[imageWidth, imageHeight]; 
     Rectangle rect = new Rectangle(0, 0, myImage.Width, myImage.Height); 
     Bitmap temp = new Bitmap(myImage); 
     BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
     int remain = bmpData.Stride - bmpData.Width * 3; 
     unsafe 
     { 
      byte* ptr = (byte*)bmpData.Scan0; 
      for (int j = 15; j < bmpData.Height; j++) 
      { 
       for (int i = 0; i < bmpData.Width; i++) 
       { 
        pixelArray[i, j] = ptr[0] + ptr[1] * 256 + ptr[2] * 256 * 256; 
        ptr += 3; 
       } 
       ptr += remain; 
      } 
     } 
     temp.UnlockBits(bmpData); 
    } 

    public void FindRegions() 
    { 
     colors = new List<MyColor>(); 

     for (int i = 0; i < imageWidth; i++) 
     { 
      for (int j = 0; j < imageHeight; j++) 
      { 
       int tmpColorValue = pixelArray[i, j]; 
       MyColor tmp = new MyColor(tmpColorValue); 
       if (colors.Contains(tmp)) 
       { 
        MyColor tmpColor = (from p in colors 
             where p.colorValue == tmpColorValue 
             select p).First(); 

        tmpColor.pointList.Add(new MyPoint(i, j)); 
       } 
       else 
       { 
        tmp.pointList.Add(new MyPoint(i, j)); 
        colors.Add(tmp); 
       } 
      } 
     } 
    } 
} 

public class MyColor : IEquatable<MyColor> 
{ 
    public int colorValue { get; set; } 
    public List<MyPoint> pointList = new List<MyPoint>(); 
    public MyColor(int _colorValue) 
    { 
     colorValue = _colorValue; 
    } 
    public bool Equals(MyColor other) 
    { 
     if (this.colorValue == other.colorValue) 
     { 
      return true; 
     } 
     return false; 
    } 
} 
public class MyPoint 
{ 
    public int xCoord { get; set; } 
    public int yCoord { get; set; } 

    public MyPoint(int _xCoord, int _yCoord) 
    { 
     xCoord = _xCoord; 
     yCoord = _yCoord; 
    } 
} 
+0

這很酷 - 但它看起來像給了我一個區域內的每一點。我需要做的是找到一個地區的輪廓。 – 2010-06-25 17:10:12

+0

Ops誤解了! 那麼我已經寫了你用C++要求的代碼,但我現在找不到它。 但是,如果所有鄰居都是相同的顏色,您仍然可以檢查pointList上的點。如果所有的鄰居都是相同的顏色,你可以刪除它,不是很有效,但只是一個伎倆。 – 2010-06-25 17:19:30

+0

順便說一句,你可以嘗試索貝爾邊緣過濾器找出區域的輪廓。 – 2010-06-26 06:19:38

0

如果你得到一個堆棧溢出,我猜你不排除已經檢查過的像素。訪問廣場的第一個檢查應該是你之前是否來過這裏。

而且,我是工作在一個相關的問題不太久以前,我想出了一個使用更少的內存,採用不同的方法:

隊列:

AddPointToQueue(x, y); 
repeat 
    x, y = HeadItem; 
    AddMaybe(x - 1, y); x + 1, y; x, y - 1; x, y + 1; 
until QueueIsEmpty; 

AddMaybe(x, y): 
if Visited[x, y] return; 
Visited[x, y] = true; 
AddPointToQueue(x, y); 

的這個點的方法是,你最終與你的隊列基本上保持一個包裹在映射區域的線。這比堆棧可以更好地限制內存使用。

如果相關,也可以進行簡單修改以產生到任何正方形的移動距離。

0

嘗試使用AForge.net。我會去按顏色過濾,閾值,然後你可以做一些形態來減少黑色/白色區域,使物體之間失去聯繫。然後你可以去找Blob。