提取单图元轮廓
一. 應(yīng)用場(chǎng)景!
使用過(guò)Genesis的朋友都知道,它可以提取你點(diǎn)擊單圖元中心<提取圖元輪廓計(jì)算中心點(diǎn)>!
二.
由于工作需要,去年在師傅的知道下寫了一個(gè)單圖元輪廓提取算法!
三. 原理
提取輪廓即需要找出單圖元最外(內(nèi))層連續(xù)不間斷的點(diǎn)!
故我們需要一個(gè)起始點(diǎn),即從你點(diǎn)擊的單圖元出發(fā),向左(←↑→↓, 4個(gè)方向皆可,只需改變算子的起始點(diǎn))遍歷至第一個(gè)與點(diǎn)擊點(diǎn)不用色調(diào)的點(diǎn),此點(diǎn)極為最外層輪廓的起始點(diǎn),然后以你所選方向?yàn)樗阕悠鹗柬?xiàng),進(jìn)行順時(shí)針(逆時(shí)針)遍歷,直到遍歷點(diǎn)與起始點(diǎn)重合,說(shuō)明輪廓提取完畢!
四.
本例中我采用向左順時(shí)針遍歷,剩余的7種就交給聰明的你了!
1)準(zhǔn)備算子結(jié)構(gòu)
| 1 | 2 | 3 |
| 0 | * | 4 |
| 7 | 6 | 5 |
typedef struct DOTS_ARR // 采用屏幕坐標(biāo),Y軸向下!
{
INT32 OffsetX; // 相對(duì)中心點(diǎn)X向的偏移量
INT32 OffsetY; // 相對(duì)中心點(diǎn)Y向的偏移量
INT32 NextDotIndex; // 此點(diǎn)為目標(biāo)點(diǎn),下次遍歷位置索引
}DOTS_ARR;
2)未優(yōu)化算子
DOTS_ARR DotsArr[] =?
{
-1, ?0, 5,
-1, -1, 6,
0, -1, 7, // 上圖中當(dāng)前計(jì)算點(diǎn)的正上方2號(hào)點(diǎn),若此點(diǎn)為最新提取的輪廓點(diǎn),則下一點(diǎn)應(yīng)為此點(diǎn)相對(duì)位置7開始遍歷,原因如下:
1, -1, 0,
1, ?0, 1,
1, ?1, 2,
0, ?1, 3,
-1, ?1, 4,
};
為了保證不遺漏,我們必須采取順時(shí)針(逆時(shí)針)遍歷當(dāng)前點(diǎn)周圍所有點(diǎn)(以上次遍歷成功2號(hào)點(diǎn)為例)
| 1 | 2 | 3 |
| 0(1) | *(2) | 4(3) |
| 7(0) | 6(*) | 5(4) |
()括號(hào)中為以上以中心點(diǎn)為相對(duì)位置的坐標(biāo)
看到上圖,聰明的朋友一定會(huì)說(shuō)7不是最好的位置,1才是,應(yīng)為0,7在上次的遍歷中已經(jīng)被排除了(you are right)!
故最優(yōu)算子如下
DOTS_ARR DotsArr[] =?
{
-1, ?0, 5,
-1, -1, 7,
0, -1, 1,
1, -1, 1,
1, ?0, 3,
1, ?1, 3,
0, ?1, 5,
-1, ?1, 5,
};
五. Demo
關(guān)鍵的算子已經(jīng)序數(shù)完了,那么我用一個(gè)簡(jiǎn)單的Demo來(lái)實(shí)踐一下!
1)先上一個(gè)提取后的效果圖
2)簡(jiǎn)單的列幾處關(guān)鍵代碼:
1.創(chuàng)建一個(gè)簡(jiǎn)單窗口類單獨(dú)用來(lái)繪制
?m_MyView.CreateEx(NULL, AfxRegisterWndClass(CS_HREDRAW,NULL,(HBRUSH)::GetStockObject(WHITE_BRUSH),NULL), L"CPaintView Window", WS_CHILD, m_MyViewRect, this, 0);
if (NULL == m_MyView)
{
TRACE(_T("Error:(%s.%d) CreateEx Failed!\r\n"), __FUNCTION__, __LINE__);
}?
2.在窗口顯示之前,加載我們的位圖
if (bShow)// Show
{
m_hMemDC = CreateCompatibleDC(NULL);
HBITMAP hBitmap = LoadBitmapW(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_START));
BITMAP bmp;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
m_BitmapSize.cx = bmp.bmWidth;
m_BitmapSize.cy = bmp.bmHeight;
m_hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, hBitmap);
}
else// Close
{
HBITMAP hBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hOldBitmap);
DeleteObject(m_hOldBitmap);
DeleteDC(m_hMemDC);
}
注意:一定要?jiǎng)h除GDI對(duì)象,做到萬(wàn)浪叢中過(guò),滴水不沾身,否則會(huì)造成GDI泄漏(進(jìn)程中GDI對(duì)象達(dá)到9999個(gè),此進(jìn)程會(huì)奔潰)
3.在圖元上打擊做按鈕時(shí),提取輪廓!
#pragma region Get Contour Centerm_VectorContourPoint.clear();CDC *lpCDC = GetDC();HDC hClientDC = lpCDC->GetSafeHdc();RECT ContourRect = {};POINT ptStart = {};POINT ptErgod = {point.x, point.y};COLORREF iColorValue = GetPixel(hClientDC, point.x, point.y);while (iColorValue == GetPixel(hClientDC, ptErgod.x , ptErgod.y)){ptErgod.x--;}if (-1 == ptErgod.x){return;}ptErgod.x++;memcpy(&ptStart, &ptErgod, sizeof(POINT));ContourRect.left = ptStart.x;ContourRect.right = ptStart.x;ContourRect.top = ptStart.y;ContourRect.bottom = ptStart.y;INT32 Count = 0;INT32 FlagIndex = 0;do {for (INT32 i = 0; i < DotsArrCount; i++){if (iColorValue == GetPixel(hClientDC, ptErgod.x + DotsArr[FlagIndex].OffsetX, ptErgod.y + DotsArr[FlagIndex].OffsetY)){Count++;ptErgod.x += DotsArr[FlagIndex].OffsetX;ptErgod.y += DotsArr[FlagIndex].OffsetY;FlagIndex = DotsArr[FlagIndex].NextDotIndex;m_VectorContourPoint.push_back(ptErgod);break;}FlagIndex++;if (FlagIndex == DotsArrCount){FlagIndex = 0;}}ContourRect.left = (ptErgod.x < ContourRect.left) ? ptErgod.x : ContourRect.left;ContourRect.right = (ptErgod.x > ContourRect.right) ? ptErgod.x : ContourRect.right;ContourRect.top = (ptErgod.y < ContourRect.top) ? ptErgod.y : ContourRect.top;ContourRect.bottom = (ptErgod.y > ContourRect.bottom) ? ptErgod.y : ContourRect.bottom;} while (ptStart.x != ptErgod.x || ptStart.y != ptErgod.y);TRACE("Count is %d\r\n", Count);m_ptCenter.x = (ContourRect.left + ContourRect.right) >> 1;m_ptCenter.y = (ContourRect.top + ContourRect.bottom) >> 1;ReleaseDC(lpCDC);Invalidate(TRUE); #pragma endregion
4)簡(jiǎn)單動(dòng)態(tài)顯示我們提取成果
BitBlt(dc.GetSafeHdc(), 0, 0, m_BitmapSize.cx, m_BitmapSize.cy, m_hMemDC, 0, 0, SRCCOPY);for (vector<POINT>::iterator itPoint = m_VectorContourPoint.begin(); itPoint != m_VectorContourPoint.end(); itPoint++){if (itPoint == m_VectorContourPoint.begin()){MoveToEx(dc.GetSafeHdc(), (*itPoint).x + m_BitmapSize.cx, (*itPoint).y, NULL);}else{Sleep(1);LineTo(dc.GetSafeHdc(), itPoint->x + m_BitmapSize.cx, itPoint->y);}}
5)完成,運(yùn)行過(guò)程中截圖,運(yùn)行完成即為之前貼出的預(yù)覽圖!
六.總結(jié)
師傅告訴我:圖像算法的處理要長(zhǎng)期的積累,自己去悟,多思考,當(dāng)遇到一個(gè)新的算法需求,結(jié)合自己的經(jīng)驗(yàn)積累,就能快速得到處理方法!
有興趣的朋友可以不使用Demo中Getpixel(),而是直接處理位圖數(shù)據(jù)塊,這樣輪廓提取的效率會(huì)更高!
這也是我去年學(xué)到第一個(gè)算法處理的,在這一年中,自己也積累了一些簡(jiǎn)單的圖像處理算法:如灰度淡化(抽點(diǎn)),多圖元骨架提取等!
最后希望圖像處理大神不嗇指出demo中的不足以及需要優(yōu)化之處,謝謝!
七)Demo源碼下載地址
http://download.csdn.net/detail/u012158162/9590647
總結(jié)
- 上一篇: 如何在IE地址栏显示自己的小图标
- 下一篇: 中小企业邮件系统选型攻略