提取单图元轮廓

一. 应用场景!

使用过Genesis的朋友都知道,它可以提取你点击单图元中心<提取图元轮廓计算中心点>!


二.

由于工作需要,去年在师傅的知道下写了一个单图元轮廓提取算法!


三. 原理

提取轮廓即需要找出单图元最外(内)层连续不间断的点!

故我们需要一个起始点,即从你点击的单图元出发,向左(←↑→↓, 4个方向皆可,只需改变算子的起始点)遍历至第一个与点击点不用色调的点,此点极为最外层轮廓的起始点,然后以你所选方向为算子起始项,进行顺时针(逆时针)遍历,直到遍历点与起始点重合,说明轮廓提取完毕!


四.

本例中我采用向左顺时针遍历,剩余的7种就交给聪明的你了!


1)准备算子结构

123
0*4
765


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号点为例)

123
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);
i
f (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




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值