一. 应用场景!
使用过Genesis的朋友都知道,它可以提取你点击单图元中心<提取图元轮廓计算中心点>!
二.
由于工作需要,去年在师傅的知道下写了一个单图元轮廓提取算法!
三. 原理
提取轮廓即需要找出单图元最外(内)层连续不间断的点!
故我们需要一个起始点,即从你点击的单图元出发,向左(←↑→↓, 4个方向皆可,只需改变算子的起始点)遍历至第一个与点击点不用色调的点,此点极为最外层轮廓的起始点,然后以你所选方向为算子起始项,进行顺时针(逆时针)遍历,直到遍历点与起始点重合,说明轮廓提取完毕!
四.
本例中我采用向左顺时针遍历,剩余的7种就交给聪明的你了!
1)准备算子结构
1 | 2 | 3 |
0 | * | 4 |
7 | 6 | 5 |
typedef struct DOTS_ARR // 采用屏幕坐标,Y轴向下!
{
INT32 OffsetX; // 相对中心点X向的偏移量
INT32 OffsetY; // 相对中心点Y向的偏移量
INT32 NextDotIndex; // 此点为目标点,下次遍历位置索引
}DOTS_ARR;
2)未优化算子
DOTS_ARR DotsArr[] =
{
-1, 0, 5,
-1, -1, 6,
0, -1, 7, // 上图中当前计算点的正上方2号点,若此点为最新提取的轮廓点,则下一点应为此点相对位置7开始遍历,原因如下:
1, -1, 0,
1, 0, 1,
1, 1, 2,
0, 1, 3,
-1, 1, 4,
};
为了保证不遗漏,我们必须采取顺时针(逆时针)遍历当前点周围所有点(以上次遍历成功2号点为例)
1 | 2 | 3 |
0(1) | *(2) | 4(3) |
7(0) | 6(*) | 5(4) |
()括号中为以上以中心点为相对位置的坐标
看到上图,聪明的朋友一定会说7不是最好的位置,1才是,应为0,7在上次的遍历中已经被排除了(you are right)!
故最优算子如下
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
关键的算子已经序数完了,那么我用一个简单的Demo来实践一下!
1)先上一个提取后的效果图
2)简单的列几处关键代码:
1.创建一个简单窗口类单独用来绘制
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);
}
注意:一定要删除GDI对象,做到万浪丛中过,滴水不沾身,否则会造成GDI泄漏(进程中GDI对象达到9999个,此进程会奔溃)
3.在图元上打击做按钮时,提取轮廓!
#pragma region Get Contour Center
m_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)简单动态显示我们提取成果
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)完成,运行过程中截图,运行完成即为之前贴出的预览图!
六.总结
师傅告诉我:图像算法的处理要长期的积累,自己去悟,多思考,当遇到一个新的算法需求,结合自己的经验积累,就能快速得到处理方法!
有兴趣的朋友可以不使用Demo中Getpixel(),而是直接处理位图数据块,这样轮廓提取的效率会更高!
这也是我去年学到第一个算法处理的,在这一年中,自己也积累了一些简单的图像处理算法:如灰度淡化(抽点),多图元骨架提取等!
最后希望图像处理大神不啬指出demo中的不足以及需要优化之处,谢谢!
七)Demo源码下载地址
http://download.csdn.net/detail/u012158162/9590647