徒手自写一个简单的图像识别算法

引言

在工作当中,遇到了一个问题,需要在安卓模拟器上找到模拟器里面安装的app里面的一个控件元素位置:
     

比如,如上图,我们需要找到app里面的支付方式的矩形框位置,但是app在不同的分辨率和DPI下控件位置和大小是不同的,而且并没有一个规律。

这个时候可能一筹莫展,但我们发现,支付方式的矩形框是纯白色的,为了获取这个矩形框的位置,我们可以通过扫描这张图像纯白像素的方式来获取并返回矩形框(RECT)位置。完成最终效果如下:

(右上图是得到的效果图,并将原图纯白像素进行显示,非纯白色不显示,外框绘制蓝色矩形框)

 一般在遇到图像分析的时候,我们通常会用到opencv等工具库来辅助我们来完成,opencv确实能完美的解决将我们需要找到图像给识别出来,但同时我们发现opencv这样的库体积太大,我们只需要知道纯白框的位置信息,用opencv这样库简直就是杀鸡用牛刀的感觉。于是我们想到通过自写算法的方式来获取我们需要获取的控件矩形位置和大小。

思路分析

 如何能将一张图片中不规则的图形用框将其包含呢:

 比如上面的这张图,如何得到上面不规则图形的外矩形框呢,经过思索,其实发现解决这个问题并不是那么容易的:

1.面临着各种噪声的干扰,比如上图,除了四个大的矩形外框,外框里面依然包含了不同形状的纯白像素的内框,但我们只需要获取外框矩形即可,因为外框矩形是包含内框矩形的。

 2.外框看起来好像是一个规则的矩形,其实并不是,因为像素之间是有过渡像素的,也就是我们不能简单的认为外框是一个正规的矩形,而是应该认为是一个不规则的形状来看。

3.不同纯白矩形框之间可能存在着相交:

解决方案

1.先把这个纯白区域假设为一个不规则的椭圆形,现在就是思考,如何用一个最小的矩形框把这个不规则的椭圆给框住,这个外框就是我们需要知道的矩形框(RECT)。

2.取椭圆区域中的任意一个坐标点,向上下左右延伸直线直到到达椭圆边界,然后这两条直线形成的矩形就可以把这两条直线所经过的纯白像素点给全部包含住:

   

 把这些所有的矩形进行合并(UnionRect),最终得到的合并矩形就是包含了椭圆所有纯白像素的矩形框RECT(类似于高数中积分原理):
  

 编码过程:

1.用一个二维数组来标记这张图片的纯白像素,如果是纯白则标记为1。
2.用一个二维数组来保存这些纯白像素对应的外矩形。

    std::vector<std::vector<BYTE>> vtDotMark;  // 纯白像素
	std::vector<std::vector<CRect>> vtRectMark;  // 纯白像素向上下左右扩展的矩形
	vtDotMark.resize(iHeight);   // 列初始化
	vtRectMark.resize(iHeight);  // 列初始化
	for (int i = 0; i < iHeight; i++)
	{
		vtDotMark[i].resize(iWidth, 0); // 行初始化
		vtRectMark[i].resize(iWidth, { 0,0,0,0 });  // 行初始化
	}

	int nBPS = pBitmap->GetBPS();
	DMColor cl(0xFF, 0xFF, 0xFF, 0xFF); // 纯白像素

	for (int nY = 0; nY < iHeight; nY++)
	{
		for (int nX = 0; nX < iWidth; nX++)
		{
			LPBYTE lpPixSrc = (LPBYTE)pPixelBits + nY*nBPS + nX * 4;
			if (memcmp(&cl, lpPixSrc, 3) == 0)  // 不比较alpha通道
			{
				vtDotMark[nY][nX] = 0x01;
			}
		}
	}

 3.然后把这张图片进行左到右、上到下进行扫描处理。在从左到右进行扫描的过程中,同时把这一条横线的left、right进行保存,因为这个白色区域内这条横线内所有的点的最左边和最右边都是一个值:

    // 先把需要标记颜色的像素进行每个像素矩形统计
	// 上到下扫
	for (int nY = 0; nY < iHeight; nY++)
	{
		// 左到右扫
		for (int nX = 0; nX < iWidth; nX++)
		{
			if (0x01 == vtDotMark[nY][nX])  // 标记的颜色
			{
				// 左到右扫
				if (vtRectMark[nY][nX].left == 0 || vtRectMark[nY][nX].right == 0)  // 左右边界没有被标记
				{
					// 横着扫
					int iRightMax = nX;
					int iCurX = nX;
					for (; iCurX < iWidth; iCurX++)
					{
						if (vtDotMark[nY][iCurX] == 0) // 终点 到了非标记颜色
						{
							iRightMax = iCurX - 1;
							break;
						}
						if (vtRectMark[nY][iCurX].left == 0)  // 未标记状态
						{
							vtRectMark[nY][iCurX].left = nX;  // 这条线左边是最左边的点 标记下来
						}
					}
					if (iCurX == iWidth)  // 到了图片的最右边边缘了
					{
						iRightMax = iWidth - 1;
					}
					// 把这条横线的右边界标记
					for (iCurX = nX; iCurX <= iRightMax; iCurX++)
					{
						if (vtRectMark[nY][iCurX].right == 0)  // 未标记状态
						{
							vtRectMark[nY][iCurX].right = iRightMax;  // 这条线右边是最右边的点 标记下来
						}
					}
				}

				// 上到下扫
				if (vtRectMark[nY][nX].top == 0 || vtRectMark[nY][nX].bottom == 0)  // 上下边界没有被标记
				{
					// 上到下扫
					int iBottomMax = nY;
					int iCurY = nY;
					for (; iCurY < iHeight; iCurY++)
					{
						if (vtDotMark[iCurY][nX] == 0)  // 终点
						{
							iBottomMax = iCurY - 1;
							break;
						}
						if (vtRectMark[iCurY][nX].top == 0)  // 未标记状态
						{
							vtRectMark[iCurY][nX].top = nY;  // 这条竖线的最上边的点 标记下来
						}
					}
					if (iCurY == iHeight)  // 到了图片的最下边边缘了
					{
						iBottomMax = iHeight - 1;
					}
					// 把这条竖线的下边界标记
					for (int iCurY = nY; iCurY <= iBottomMax; iCurY++)
					{
						if (vtRectMark[iCurY][nX].bottom == 0)  // 未标记状态
						{
							vtRectMark[iCurY][nX].bottom = iBottomMax;  // 这条线右边是最右边的点 标记下来
						}
					}
				}
			}
		}
	}

 4.好了,椭圆内的所有纯白色的像素点对应的矩形(RECT)块已经全部统计到vtRectMark变量中去了。现在就需要将这些所有的RECT进行集合(Union)了,进行Union过程就是把所有有交集的两个Rect进行Union,并放置到一个vector的集合里:

这里要注意,在进行矩形集合(union)的时候,如果两个矩形只有一条线或只有一个点有交集,或者他们是相邻的,那么这些情况用::IntersectRect来判断,会返回false,表示他们是没有交集的,但我们也要将他们进行RECT的合并:

同时也存在着一条横线(矩形高度为0,top==bottom)或者竖线(矩形宽度为0,left==right)穿过矩形的情况,这种情况也算有交集的,是可以进行合并的:

同时对他们有交集进行合并之后,他们之间可能又有了交集,这个时候也要将他们进行合并处理:

// 每个像素统计的矩形,如果有交集就进行矩形合并,合并成一个大矩形
	std::vector<CRect> vtRectTotal;
	CRect rtTmp;
	for (int nY = 0; nY < iHeight; nY++)
	{
		// 左到右扫
		for (int nX = 0; nX < iWidth; nX++)
		{
			if (0 != vtDotMark[nY][nX])  //标记颜色
			{
				DMASSERT_EXPR(vtRectMark[nY][nX].bottom >= vtRectMark[nY][nX].top && vtRectMark[nY][nX].right >= vtRectMark[nY][nX].left, L"RectMark error");
				// valide rect
				if (vtRectMark[nY][nX].bottom >= vtRectMark[nY][nX].top && vtRectMark[nY][nX].right >= vtRectMark[nY][nX].left)
				{
					std::vector<CRect*> vtIntersetRectList;  // 都有交集的矩形进行统计 因为有矩形重合的他们就可以合并为一个矩形
					for (size_t i = 0; i < vtRectTotal.size(); i++)
					{
						bool bIntersect = false;  // 是否跟合并矩形有交集
						if (::IntersectRect(rtTmp, vtRectTotal[i], vtRectMark[nY][nX]))   // 有交集
						{
							vtRectTotal[i].UnionRect(vtRectTotal[i], vtRectMark[nY][nX]);  // 合并有交集的矩形
							bIntersect = true;
						}
						else // 可能是隔一个像素相邻 或者vtRectMark只是一条线
						{
							CRect& rtMark = vtRectMark[nY][nX];
							CRect& rtTotal = vtRectTotal[i];
							CRect rtInflate = rtTotal;
							rtInflate.InflateRect(1, 1, 1, 1);
							CPoint PtTopLeft = rtMark.TopLeft();
							CPoint PtTopRight = { rtMark.right, rtMark.top };
							CPoint PtBottomLeft = { rtMark.left, rtMark.bottom};
							CPoint PtBottomRight = rtMark.BottomRight();
							if (rtInflate.PtInRect(PtTopRight) || rtInflate.PtInRect(PtTopLeft) ||
								rtInflate.PtInRect(PtBottomLeft) || rtInflate.PtInRect(PtBottomRight))  // 交接
							{
								bIntersect = true;
								// unionRect
								rtTotal.left = rtTotal.left <= rtMark.left ? rtTotal.left : rtMark.left;
								rtTotal.right = rtTotal.right >= rtMark.right ? rtTotal.right : rtMark.right;
								rtTotal.top = rtTotal.top <= rtMark.top ? rtTotal.top : rtMark.top;
								rtTotal.bottom = rtTotal.bottom >= rtMark.bottom ? rtTotal.bottom : rtMark.bottom;
							}
							else if (rtMark.Height() == 1)  // 一条横线 穿过矩形 或者跟矩形交接
							{
								if (rtInflate.top <= rtMark.top &&
									rtInflate.bottom >= rtMark.bottom) // 横线在矩形中间 或交界
								{
									if ((rtMark.right < rtInflate.left || rtMark.left > rtInflate.right))
									{
										// 左、右端点超出了矩形范围  横线不跟矩形有交接也不会穿过
									}
									else
									{
										bIntersect = true;
										rtTotal.left = rtTotal.left <= rtMark.left ? rtTotal.left : rtMark.left;
										rtTotal.right = rtTotal.right >= rtMark.right ? rtTotal.right : rtMark.right;
										rtTotal.top = rtTotal.top <= rtMark.top ? rtTotal.top : rtMark.top;
										rtTotal.bottom = rtTotal.bottom >= rtMark.bottom ? rtTotal.bottom : rtMark.bottom;
									}
								}
							}
							else if (rtMark.Width() == 1)  // 一条竖线 穿过矩形 或者跟矩形交接
							{
								if (rtInflate.left <= rtMark.left &&
									rtInflate.right >= rtMark.right)  // 横线在矩形中间 或交界
								{
									if ((rtMark.bottom < rtInflate.top || rtMark.top > rtInflate.bottom))
									{
										// 上、下端点超出了矩形范围  竖线不跟矩形有交接也不会穿过
									}
									else
									{
										bIntersect = true;
										rtTotal.left = rtTotal.left <= rtMark.left ? rtTotal.left : rtMark.left;
										rtTotal.right = rtTotal.right >= rtMark.right ? rtTotal.right : rtMark.right;
										rtTotal.top = rtTotal.top <= rtMark.top ? rtTotal.top : rtMark.top;
										rtTotal.bottom = rtTotal.bottom >= rtMark.bottom ? rtTotal.bottom : rtMark.bottom;
									}
								}
							}
						}
						if (bIntersect)
						{
							vtIntersetRectList.push_back(&vtRectTotal[i]);
						}
					}

					if (vtIntersetRectList.empty())  // 跟汇总的所有Rect都没有交集,加入到矩形列表中
					{
						vtRectTotal.push_back(vtRectMark[nY][nX]);
					}
					else if(vtIntersetRectList.size() > 1)  // 有交集而且两个以上  把这些有交集的矩形集合进行合并处理  保留有交集的最大rect即可
					{
						std::vector<CRect> vtEraseRectList;
						int iLeftMost = INT_MAX, iTopMost = INT_MAX, iRightMost = 0, iBottomMost = 0;
						int iRet = vtRectTotal.size() - vtIntersetRectList.size();
						for (std::vector<CRect*>::iterator it = vtIntersetRectList.begin(); it != vtIntersetRectList.end(); it++)
						{
							iLeftMost = (*it)->left < iLeftMost ? (*it)->left : iLeftMost;
							iTopMost = (*it)->top < iTopMost ? (*it)->top : iTopMost;
							iRightMost = (*it)->right > iRightMost ? (*it)->right : iRightMost;
							iBottomMost = (*it)->bottom > iBottomMost ? (*it)->bottom : iBottomMost;
							// 先擦除 后把归总集合添加
							for (std::vector<CRect>::iterator itT = vtRectTotal.begin(); itT != vtRectTotal.end(); itT++)
							{
								if(itT->left == (*it)->left && itT->right == (*it)->right && 
									itT->top == (*it)->top && itT->bottom == (*it)->bottom)
								{
									vtEraseRectList.push_back(*it);
								}
							}
						}
						for (std::vector<CRect>::iterator itE = vtEraseRectList.begin(); itE != vtEraseRectList.end(); itE++)
						{
							for (std::vector<CRect>::iterator it = vtRectTotal.begin(); it != vtRectTotal.end();)
							{
								if (it->left == itE->left && it->top == itE->top && it->right == itE->right && it->bottom == itE->bottom)
								{
									it = vtRectTotal.erase(it);
								}
								else
								{
									it++;
								}
							}
						}
						DMASSERT_EXPR(iRet == vtRectTotal.size(), L"好像结果不对");
						vtRectTotal.push_back(CRect(iLeftMost, iTopMost, iRightMost, iBottomMost));
					}
				}
			}
		}
	

最后我们将得到的RECT列表进行绘制:

    for (size_t i = 0; i < vtRectTotal.size(); i++)
	{
		CRect& rt = vtRectTotal[i];
		int top = rt.top;
		int bottom = rt.bottom;
		int left = rt.left;
		int right = rt.right;

		DMColor clRed = (0xFF, 0xFF, 0xFF, 0xFF);
		// 绘制上面的线
		for (int iX = left; iX <= right; iX++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + top*nBPS + iX * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
		// 绘制下面的线
		for (int iX = left; iX <= right; iX++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + bottom*nBPS + iX * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
		// 绘制左面的线
		for (int iY = top; iY <= bottom; iY++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + iY*nBPS + left * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
		// 绘制右面的线
		for (int iY = top; iY <= bottom; iY++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + iY*nBPS + right * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
	}

最后的效果:

附录(所有代码):

int main()
{
	for (int i = 1; i < 10; i++)
	{
		wchar_t lpPngSrc[50] = { 0 };
		wchar_t lpPngDest[50] = { 0 };
		wsprintf(lpPngSrc, L"D:\\%dPNG\\inputpng.png", i);
		wsprintf(lpPngDest, L"D:\\%dPNG\\123.png", i);
		QuotePngWhiteRgbByRect(lpPngSrc, lpPngDest);
	}
}

DMCode QuotePngWhiteRgbByRect(LPCTSTR lpPngSrc, LPCTSTR lpPngDest)
{
	DMSmartPtrT<IDMRender> pRender;
	if (!DMSUCCEEDED(g_pDMApp->GetDefRegObj((void**)&pRender, DMREG_Render)))
	{
		DMASSERT_EXPR(0, L"竟然获取默认Render失败,不太可能吧!");
		return DM_ECODE_FAIL;
	}
	DMSmartPtrT<IDMBitmap> pBitmap;
	pRender->CreateBitmap(&pBitmap);

	DMBufT<byte>pBuf;
	DWORD dwSize = GetFileSizeW(lpPngSrc);
	pBuf.Allocate(dwSize);
	DWORD dwRead = 0;
	GetFileBufW(lpPngSrc, (void**)&pBuf, dwSize, dwRead);
	if (!pBuf.get() || dwSize == 0)
	{
		pBitmap->Release();
		return DM_ECODE_FAIL;
	}

	if (!DMSUCCEEDED(pBitmap->LoadFromMemory(pBuf, dwRead, L"png")))
	{
		DMASSERT_EXPR(0, L"pBitmap LoadFromMemory error!");
		pBitmap->Release();
		return DM_ECODE_FAIL;
	}

	int iWidth = pBitmap->GetWidth();
	int iHeight = pBitmap->GetHeight();
	PVOID pPixelBits = pBitmap->GetPixelBits();
	DMBufT<BYTE> pPixCopy;
	BYTE* pPixelCopy = pPixCopy.AllocateBytes(pBitmap->GetSize());

	std::vector<std::vector<BYTE>> vtDotMark;
	std::vector<std::vector<CRect>> vtRectMark;
	vtDotMark.resize(iHeight); // 列 
	vtRectMark.resize(iHeight);
	for (int i = 0; i < iHeight; i++)
	{
		vtDotMark[i].resize(iWidth, 0); // 行
		vtRectMark[i].resize(iWidth, { 0,0,0,0 });
	}

	int nBPS = pBitmap->GetBPS();
	DMColor cl(0xFF, 0xFF, 0xFF, 0xFF);
	DMColor clFill(0x00, 0x20, 0x20, 0x20);

	for (int nY = 0; nY < iHeight; nY++)
	{
		for (int nX = 0; nX < iWidth; nX++)
		{
			LPBYTE lpPixSrc = (LPBYTE)pPixelBits + nY*nBPS + nX * 4;
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + nY*nBPS + nX * 4;
			if (memcmp(&cl, lpPixSrc, 3) == 0)  // 不比较alpha通道
			{
				vtDotMark[nY][nX] = 0x01;
				memcpy(lpPixDest, &clFill, 4);
			}
		}
	}

	// 先把需要标记颜色的像素进行每个像素矩形统计
	// 上到下扫
	for (int nY = 0; nY < iHeight; nY++)
	{
		// 左到右扫
		for (int nX = 0; nX < iWidth; nX++)
		{
			if (0x01 == vtDotMark[nY][nX])  // 标记的颜色
			{
				// 左到右扫
				if (vtRectMark[nY][nX].left == 0 || vtRectMark[nY][nX].right == 0)  // 左右边界没有被标记
				{
					// 横着扫
					int iRightMax = nX;
					int iCurX = nX;
					for (; iCurX < iWidth; iCurX++)
					{
						if (vtDotMark[nY][iCurX] == 0) // 终点 到了非标记颜色
						{
							iRightMax = iCurX - 1;
							break;
						}
						if (vtRectMark[nY][iCurX].left == 0)  // 未标记状态
						{
							vtRectMark[nY][iCurX].left = nX;  // 这条线左边是最左边的点 标记下来
						}
					}
					if (iCurX == iWidth)  // 到了图片的最右边边缘了
					{
						iRightMax = iWidth - 1;
					}
					// 把这条横线的右边界标记
					for (iCurX = nX; iCurX <= iRightMax; iCurX++)
					{
						if (vtRectMark[nY][iCurX].right == 0)  // 未标记状态
						{
							vtRectMark[nY][iCurX].right = iRightMax;  // 这条线右边是最右边的点 标记下来
						}
					}
				}

				// 上到下扫
				if (vtRectMark[nY][nX].top == 0 || vtRectMark[nY][nX].bottom == 0)  // 上下边界没有被标记
				{
					// 上到下扫
					int iBottomMax = nY;
					int iCurY = nY;
					for (; iCurY < iHeight; iCurY++)
					{
						if (vtDotMark[iCurY][nX] == 0)  // 终点
						{
							iBottomMax = iCurY - 1;
							break;
						}
						if (vtRectMark[iCurY][nX].top == 0)  // 未标记状态
						{
							vtRectMark[iCurY][nX].top = nY;  // 这条竖线的最上边的点 标记下来
						}
					}
					if (iCurY == iHeight)  // 到了图片的最下边边缘了
					{
						iBottomMax = iHeight - 1;
					}
					// 把这条竖线的下边界标记
					for (int iCurY = nY; iCurY <= iBottomMax; iCurY++)
					{
						if (vtRectMark[iCurY][nX].bottom == 0)  // 未标记状态
						{
							vtRectMark[iCurY][nX].bottom = iBottomMax;  // 这条线右边是最右边的点 标记下来
						}
					}
				}
			}
		}
	}

	// 每个像素统计的矩形,如果有交集就进行矩形合并,合并成一个大矩形
	std::vector<CRect> vtRectTotal;
	CRect rtTmp;
	for (int nY = 0; nY < iHeight; nY++)
	{
		// 左到右扫
		for (int nX = 0; nX < iWidth; nX++)
		{
			if (0 != vtDotMark[nY][nX])  //标记颜色
			{
				DMASSERT_EXPR(vtRectMark[nY][nX].bottom >= vtRectMark[nY][nX].top && vtRectMark[nY][nX].right >= vtRectMark[nY][nX].left, L"RectMark error");
				// valide rect
				if (vtRectMark[nY][nX].bottom >= vtRectMark[nY][nX].top && vtRectMark[nY][nX].right >= vtRectMark[nY][nX].left)
				{
					std::vector<CRect*> vtIntersetRectList;  // 都有交集的矩形进行统计 因为有矩形重合的他们就可以合并为一个矩形
					for (size_t i = 0; i < vtRectTotal.size(); i++)
					{
						bool bIntersect = false;  // 是否跟合并矩形有交集
						if (::IntersectRect(rtTmp, vtRectTotal[i], vtRectMark[nY][nX]))   // 有交集
						{
							vtRectTotal[i].UnionRect(vtRectTotal[i], vtRectMark[nY][nX]);  // 合并有交集的矩形
							bIntersect = true;
						}
						else // 可能是隔一个像素相邻 或者vtRectMark只是一条线
						{
							CRect& rtMark = vtRectMark[nY][nX];
							CRect& rtTotal = vtRectTotal[i];
							CRect rtInflate = rtTotal;
							rtInflate.InflateRect(1, 1, 1, 1);
							CPoint PtTopLeft = rtMark.TopLeft();
							CPoint PtTopRight = { rtMark.right, rtMark.top };
							CPoint PtBottomLeft = { rtMark.left, rtMark.bottom};
							CPoint PtBottomRight = rtMark.BottomRight();
							if (rtInflate.PtInRect(PtTopRight) || rtInflate.PtInRect(PtTopLeft) ||
								rtInflate.PtInRect(PtBottomLeft) || rtInflate.PtInRect(PtBottomRight))  // 交接
							{
								bIntersect = true;
								// unionRect
								rtTotal.left = rtTotal.left <= rtMark.left ? rtTotal.left : rtMark.left;
								rtTotal.right = rtTotal.right >= rtMark.right ? rtTotal.right : rtMark.right;
								rtTotal.top = rtTotal.top <= rtMark.top ? rtTotal.top : rtMark.top;
								rtTotal.bottom = rtTotal.bottom >= rtMark.bottom ? rtTotal.bottom : rtMark.bottom;
							}
							else if (rtMark.Height() == 1)  // 一条横线 穿过矩形 或者跟矩形交接
							{
								if (rtInflate.top <= rtMark.top &&
									rtInflate.bottom >= rtMark.bottom) // 横线在矩形中间 或交界
								{
									if ((rtMark.right < rtInflate.left || rtMark.left > rtInflate.right))
									{
										// 左、右端点超出了矩形范围  横线不跟矩形有交接也不会穿过
									}
									else
									{
										bIntersect = true;
										rtTotal.left = rtTotal.left <= rtMark.left ? rtTotal.left : rtMark.left;
										rtTotal.right = rtTotal.right >= rtMark.right ? rtTotal.right : rtMark.right;
										rtTotal.top = rtTotal.top <= rtMark.top ? rtTotal.top : rtMark.top;
										rtTotal.bottom = rtTotal.bottom >= rtMark.bottom ? rtTotal.bottom : rtMark.bottom;
									}
								}
							}
							else if (rtMark.Width() == 1)  // 一条竖线 穿过矩形 或者跟矩形交接
							{
								if (rtInflate.left <= rtMark.left &&
									rtInflate.right >= rtMark.right)  // 横线在矩形中间 或交界
								{
									if ((rtMark.bottom < rtInflate.top || rtMark.top > rtInflate.bottom))
									{
										// 上、下端点超出了矩形范围  竖线不跟矩形有交接也不会穿过
									}
									else
									{
										bIntersect = true;
										rtTotal.left = rtTotal.left <= rtMark.left ? rtTotal.left : rtMark.left;
										rtTotal.right = rtTotal.right >= rtMark.right ? rtTotal.right : rtMark.right;
										rtTotal.top = rtTotal.top <= rtMark.top ? rtTotal.top : rtMark.top;
										rtTotal.bottom = rtTotal.bottom >= rtMark.bottom ? rtTotal.bottom : rtMark.bottom;
									}
								}
							}
						}
						if (bIntersect)
						{
							vtIntersetRectList.push_back(&vtRectTotal[i]);
						}
					}

					if (vtIntersetRectList.empty())  // 跟汇总的所有Rect都没有交集,加入到矩形列表中
					{
						vtRectTotal.push_back(vtRectMark[nY][nX]);
					}
					else if(vtIntersetRectList.size() > 1)  // 有交集而且两个以上  把这些有交集的矩形集合进行合并处理  保留有交集的最大rect即可
					{
						std::vector<CRect> vtEraseRectList;
						int iLeftMost = INT_MAX, iTopMost = INT_MAX, iRightMost = 0, iBottomMost = 0;
						int iRet = vtRectTotal.size() - vtIntersetRectList.size();
						for (std::vector<CRect*>::iterator it = vtIntersetRectList.begin(); it != vtIntersetRectList.end(); it++)
						{
							iLeftMost = (*it)->left < iLeftMost ? (*it)->left : iLeftMost;
							iTopMost = (*it)->top < iTopMost ? (*it)->top : iTopMost;
							iRightMost = (*it)->right > iRightMost ? (*it)->right : iRightMost;
							iBottomMost = (*it)->bottom > iBottomMost ? (*it)->bottom : iBottomMost;
							// 先擦除 后把归总集合添加
							for (std::vector<CRect>::iterator itT = vtRectTotal.begin(); itT != vtRectTotal.end(); itT++)
							{
								if(itT->left == (*it)->left && itT->right == (*it)->right && 
									itT->top == (*it)->top && itT->bottom == (*it)->bottom)
								{
									vtEraseRectList.push_back(*it);
								}
							}
						}
						for (std::vector<CRect>::iterator itE = vtEraseRectList.begin(); itE != vtEraseRectList.end(); itE++)
						{
							for (std::vector<CRect>::iterator it = vtRectTotal.begin(); it != vtRectTotal.end();)
							{
								if (it->left == itE->left && it->top == itE->top && it->right == itE->right && it->bottom == itE->bottom)
								{
									it = vtRectTotal.erase(it);
								}
								else
								{
									it++;
								}
							}
						}
						DMASSERT_EXPR(iRet == vtRectTotal.size(), L"好像结果不对");
						vtRectTotal.push_back(CRect(iLeftMost, iTopMost, iRightMost, iBottomMost));
					}
				}
			}
		}
	}

	for (size_t i = 0; i < vtRectTotal.size(); i++)
	{
		CRect& rt = vtRectTotal[i];
		int top = rt.top;
		int bottom = rt.bottom;
		int left = rt.left;
		int right = rt.right;

		DMColor clRed = (0xFF, 0xFF, 0xFF, 0xFF);
		// 绘制上面的线
		for (int iX = left; iX <= right; iX++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + top*nBPS + iX * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
		// 绘制下面的线
		for (int iX = left; iX <= right; iX++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + bottom*nBPS + iX * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
		// 绘制左面的线
		for (int iY = top; iY <= bottom; iY++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + iY*nBPS + left * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
		// 绘制右面的线
		for (int iY = top; iY <= bottom; iY++)
		{
			LPBYTE lpPixDest = (LPBYTE)pPixelCopy + iY*nBPS + right * 4;
			memcpy(lpPixDest, &clRed, 4);
		}
	}

	memcpy(pPixelBits, pPixelCopy, pBitmap->GetSize());
	pBitmap->SaveFile(lpPngDest);
	pBitmap->Release();
	return DM_ECODE_OK;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值