[Happy DSA] 圖像的contour trace算法C++實現

問題提出:

給定一個二維圖像,基於某個threshold,來提取contours。

在圖形圖像學中,這個問題有比較好的解決方案,google "coutour trace",可以得到以下2個比較好的參考文獻:

1. http://en.wikipedia.org/wiki/Moore_neighborhood

2. http://www.imageprocessingplace.com/downloads_V3/root_downloads/tutorials/contour_tracing_Abeer_George_Ghuneim/index.html


算法描述:

The following is a formal description of the Moore-Neighbor tracing algorithm:

Input: A square tessellation, T, containing a connected component P of black cells.

Output: A sequence B (b1, b2 ,..., bkof boundary pixels i.e. the contour.

Define M(a) to be the Moore neighborhood of pixel a
Let denote the current boundary pixel. 
Let denote the current pixel under consideration i.e. c is in M(p).

Begin

  • Set B to be empty.
  • From bottom to top and left to right scan the cells of until a black pixel, s, of is found.
  • Insert s in B.
  • Set the current boundary point to s i.e. p=s
  • Backtrack i.e. move to the pixel from which s was entered.
  • Set c to be the next clockwise pixel in M(p).
  • While c not equal to s do
       If is black
    • insert c in B
    • set p=c
    • backtrack (move the current pixel c to the pixel from which p was entered)
       else
    • advance the current pixel c to the next clockwise pixel in M(p)
    end While
End 


需要注意的是,

1. 按照先列後行,每列從下到上,每行從左到右的順序(當然也可以按照其它順序,只要整個過程一致就可以了),搜索第一個BLACK PIXEL;

2. 有2種情況可以作爲trace one contour的條件:

a) 搜索到第一個BLACK PIXEL時,如果它位於某列的第一行,將它的backtrace point定義該列的-1行;

b) 否則進入這個BLACK PIXEL的backtrace point,一定要是WHITE PIXEL。


以下代碼段是基於Moore Neighborhood算法的C++實現:

void TraceOneContour(Image<char>* bp, const std::pair<int,int>& startpos,
		const std::pair<int,int>& btpos, std::vector<int>& contour)
{
	int xSize = bp->x_size, ySize = bp->y_size;

	char BLACK = 1;
	enum { LKN = 8 };
	static int cwmat[LKN][2] = {		// clock-wise matrix
		{-1, +1},		// left-upper
		{0, +1},		// upper
		{+1,+1},
		{+1, 0},
		{+1, -1},
		{0, -1},
		{-1, -1},
		{-1, 0}		// left
	};
	contour.clear();
	int px = startpos.first, py = startpos.second;	// current center point
	int bx = btpos.first, by = btpos.second ;		// bt point
	contour.push_back(py*xSize + px);

	int cx = bx, cy = by;
	bool bstop = false;
	int nVisitStart = 1;
	static int nStopTimes = 5;

	do {
		// get next clockwise pixel for (cx, cy) around (px, py)
		int cur = -1;
		for (int i = 0; i < LKN; ++i)
		{
			if (cwmat[i][0] == (cx - px) &&
				cwmat[i][1] == (cy - py))
			{
				cur = i;
				break;
			}
		}
		if (cur < 0) break;

		int last = cur;
		cur = (cur+1) % LKN;
		while (cur != last)
		{	// loop 8-connected cells around p
			int prev = (cur - 1 + LKN)%LKN;
			cx = px + cwmat[cur][0], cy = py + cwmat[cur][1];
			bx = px + cwmat[prev][0], by = py + cwmat[prev][1];

			if ((cx == startpos.first && cy == startpos.second)
				&& (bx == btpos.first && by == btpos.second))
			{	// Jacob's stopping criterion
				bstop = true;
				break;
			}
			if (cx >= 0 && cx < xSize && cy >=0 && cy < ySize
				&& bp->pixRef(cx, cy) == BLACK)
			{
				if (cx == startpos.first && cy == startpos.second)
				{  	// In some class, only Jacob's stopping cannot stop algorithm
					// so add visit time stopping criterion
					if (++nVisitStart >= nStopTimes)
					{
						bstop = true;
						break;
					}
				}
				contour.push_back(cy * xSize + cx);
				px = cx, py = cy;
				cx = bx, cy = by;
				break;
			}
			else
			{
				cur = (cur+1) % LKN;
			}
		}
		// handle isolated pixel point
		if (cur == last) break;
		if (bstop) break;
	}  while (1);
}

template <typename Type, typename SpecFunc>
void TraceImageContours(const Image<Type>* in_img, const SpecFunc& specFunc,
	std::vector<std::vector<int> >& vPolyOut)
{
	assert(in_img && in_img->x_size > 0 && in_img->y_size > 0);

	vPolyOut.clear();
	int xSize = in_img->x_size, ySize = in_img->y_size;
	int npix = xSize * ySize;

	char WHITE = 0, BLACK = 1, GREEN = 2;
	Image<char> bp(xSize, ySize);
	std::transform(in_img->pix, in_img->pix + npix, bp.pix, specFunc);

	std::vector<int> onecontour;
	std::pair<int,int> bt(0, -1);
	// firstly column from bottom to top, secondly row from left to right
	for (int i = 0; i < xSize; i++)
	{
		for (int j = 0; j < ySize; j++)
		{
			if (bp.pixRef(i, j) == WHITE)
			{
				bt = std::make_pair(i, j);
			}
			else if (bp.pixRef(i, j) == BLACK)
			{
				if (j == 0) bt = std::make_pair(i, -1);
				if ((j > 0 && bp.pixRef(i, j-1) == WHITE)
					|| j == 0)
				{ // start to trace one contour
					TraceOneContour(&bp, std::make_pair(i, j), bt, onecontour);
					 // mark contour point green(visited)
					for (BOOST_AUTO(itc, onecontour.begin());
						itc != onecontour.end(); ++itc)
					{
						bp.pix[*itc] = GREEN;
					}
					if (!onecontour.empty()) vPolyOut.push_back(onecontour);
				}
			}
		}
	}
}

關於Image<T>,可以參考下面的C++類:

template <typename T>
struct Image {    
    int x_size;
    int y_size;
    T* pix;

public:
    Image(int w, int h) : x_size(w), y_size(h) {
        pix = new T[w*h];
    }
    Image(int w, int h, T* p) : x_size(w), y_size(h), pix(p) {
    }
    ~Image() {
        delete [] pix;
    }
     
    T* detach() {
      T* result = pix;
      pix = 0;
      return result;
    }
    
    T& pixRef(int x, int y) {
      return pix[y * x_size + x];
    }
    const T& pixRef(int x, int y) const {
      return pix[y * x_size + x];
    }
};


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章