[图像处理] MFC OnMouseMove()绘制ROI矩形时的闪烁问题

发布于:2024-04-15 ⋅ 阅读:(165) ⋅ 点赞:(0)

问题

最近想通过业余时间,写一个简单的图像处理软件,一点点学习图像处理的基础知识。
目前想实现一个在图像上鼠标选取ROI矩形区域的功能。
想通过OnMouseMove()绘制一个随着鼠标拖动实时显示位置的矩形。

网上推荐双缓冲方案,但是怎么用MFC Picture控件和CImage实现双缓冲,还没查到。

目前采用了一个折中策略,虽然没有完全解决闪烁的问题,但是还可以接收。

对策

每次走进OnMouseMove(),调用Picture控件的RedrawWindow(),清空Piture控件区域,连同Image和ROI矩形一起清除。
重新把存在程序中的CImage Draw到Picture控件中。
通过OnMouseMove()传入的坐标,重画Roi矩形。
==不要调用Dialog的RedrawWindow,否则会严重闪烁。 ==

代码

void CGDITESTDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	ClientToScreen(&point);
	GetDlgItem(IDC_PIC_DISPLAY)->GetWindowRect(&rc_dsp);
	if (rc_dsp.PtInRect(point)) //判断鼠标左键落下时,point是否在控件的rect范围内
	{
		m_startPt = point; 
		isStartPtValid = true;

		isDrawingRoi = true;
		isDrawRoiFinish = false;
	}
	CDialogEx::OnLButtonDown(nFlags, point);
}
void CGDITESTDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
	ClientToScreen(&point);
	GetDlgItem(IDC_PIC_DISPLAY)->GetWindowRect(&rc_dsp);
	if (rc_dsp.PtInRect(point) && m_IsChoose)//判断鼠标左键抬起时,point是否在控件的rect范围内
	{
		if (isDrawingRoi && isStartPtValid)
		{
			isDrawRoiFinish = false;

			isDrawingRoi = false;
			isStartPtValid = false;
		}
	}
	CDialogEx::OnLButtonUp(nFlags, point);
}
void CGDITESTDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	if ((nFlags & MK_LBUTTON) == MK_LBUTTON)  //判断鼠标move的时候,鼠标左键是否按下
	{
		ClientToScreen(&point);
		GetDlgItem(IDC_PIC_DISPLAY)->GetWindowRect(&rc_dsp);
		if (rc_dsp.PtInRect(point) && isDrawingRoi) //判断鼠标左键落下时,point是否在控件的rect范围内
		{
			//就清空picture控件区域,重新画图片,这样就把原来的ROI消除了
			{
				CRect rc_dsp;
				m_ctl_pic_dsp.SetBitmap(NULL);
				GetDlgItem(IDC_PIC_DISPLAY)->GetWindowRect(&rc_dsp);
				ScreenToClient(&rc_dsp);
				//this->RedrawWindow(rc_dsp); //重绘主窗口指定区域 //使用这个函数,会重绘整个picture控件的rc_dsp区域,但是会引起严重闪烁
				m_ctl_pic_dsp.RedrawWindow(); //重绘主窗口指定区域 //使用这个控件,只会重绘图像区域,但是闪烁程度相对温柔(观察图片边缘仍可发现闪烁)
				//如果有绘制在picture控件内但是在图像外的线条,则不会被重绘
				CDC* pdc = nullptr;
				switch (m_IsChoose) //重绘image
				{
				case PIC_ORIGIN:
					pdc = m_ctl_pic_dsp.GetDC();
					image_origin.Draw(pdc->m_hDC, rc_convert);
					m_ctl_pic_dsp.ReleaseDC(pdc);
					break;
				case PIC_BINARIZATION:
					pdc = m_ctl_pic_dsp.GetDC();
					image_binarization.Draw(pdc->m_hDC, rc_convert);
					m_ctl_pic_dsp.ReleaseDC(pdc);
					break;
				case PIC_GREY:
					pdc = m_ctl_pic_dsp.GetDC();
					image_grey.Draw(pdc->m_hDC, rc_convert);
					m_ctl_pic_dsp.ReleaseDC(pdc);
					break;
				default:
					break;
				}
			}

			//画Rect
			{
				CRect rect;
				int rect_width = 0;
				int rect_height = 0;
				CPoint sel_pos;
				//GetCursorPos(&sel_pos);
				GetDlgItem(IDC_PIC_DISPLAY)->GetWindowRect(&rc_dsp);
				if (rc_dsp.PtInRect(point) && m_IsChoose && isStartPtValid && !isDrawRoiFinish)//判断鼠标左键抬起时,point是否在控件的rect范围内
				{
					//ClipCursor(&rc_dsp);

					//计算ROI的topleft和rightbottom坐标
					if (point.x > m_startPt.x)
						m_endPt.x = point.x < rc_dsp.right ? point.x : rc_dsp.right;
					else
						m_endPt.x = point.x > rc_dsp.left ? point.x : rc_dsp.left;

					if (point.y > m_startPt.y)
						m_endPt.y = point.y < rc_dsp.bottom ? point.y : rc_dsp.bottom;
					else
						m_endPt.y = point.y > rc_dsp.top ? point.y : rc_dsp.top;

					CPoint tplt, rtbt;
					tplt.x = m_startPt.x < m_endPt.x ? m_startPt.x : m_endPt.x;
					tplt.y = m_startPt.y < m_endPt.y ? m_startPt.y : m_endPt.y;
					rtbt.x = m_startPt.x > m_endPt.x ? m_startPt.x : m_endPt.x;
					rtbt.y = m_startPt.y > m_endPt.y ? m_startPt.y : m_endPt.y;
					m_ctl_pic_dsp.ScreenToClient(&tplt);
					m_ctl_pic_dsp.ScreenToClient(&rtbt);

					CDC* pDC = m_ctl_pic_dsp.GetWindowDC();
					CPen pen(PS_SOLID, 2, RGB(255, 0, 0));
					pDC->SelectStockObject(NULL_BRUSH);
					pDC->SelectObject(&pen);
					//在重新draw的image上再画矩形
					m_rc = CRect(tplt, rtbt);
					pDC->Rectangle(m_rc);
				}
			}
		}
	}

	CDialogEx::OnMouseMove(nFlags, point);
}

完整工程

MFC简单的图片处理工程

结果

使用Picture控件的RedrawWindow()的效果

在这里插入图片描述
可以看到,和图片接触的Picture控件边框会有闪烁,而图片部分本身没什么闪烁。
如果调整图像的Rect坐标,使其离开Picture控件的边框一点距离,会不会就没有闪烁了?

使用Dialog的RedrawWindow()的效果

在这里插入图片描述
这里是由于我采集GIf图的帧率太低(CSDN不能传超过5m的图),导致无法还原实际的闪烁效果。
实际上闪烁非常明显,但是没有这样大面积空白的情况。

使用Picture控件的RedrawWindow(),ROI绘制到图像外的效果

在这里插入图片描述
当ROI矩形,拉倒图像外但是还在Picture控件内的区域时,可以看到出现了异常的重叠矩形框,这些异常的矩形框无法随着新绘制ROI刷新掉。
我感觉Picture控件的RedrawWindow()只能重绘图像区。
这个问题可以通过控制ROI的坐标范围解决。

结论

仍然在探索双缓冲方案的实现。


网站公告

今日签到

点亮在社区的每一天
去签到