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

问题

最近想通过业余时间,写一个简单的图像处理软件,一点点学习图像处理的基础知识。
目前想实现一个在图像上鼠标选取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的坐标范围解决。

结论

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

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MFC中,可以使用CDC类来进行绘图。要实现橡皮筋绘制矩形,可以通过鼠标移动事件获取矩形的位置和大小,并在每次移动重新绘制矩形。 以下是一个简单的示例代码: ```cpp void CMyView::OnMouseMove(UINT nFlags, CPoint point) { if (nFlags & MK_LBUTTON) { CDC* pDC = GetDC(); // 绘制之前的矩形,先用背景色擦掉 pDC->DrawFocusRect(m_oldRect); // 计算新的矩形位置和大小 m_newRect.left = min(m_startPoint.x, point.x); m_newRect.right = max(m_startPoint.x, point.x); m_newRect.top = min(m_startPoint.y, point.y); m_newRect.bottom = max(m_startPoint.y, point.y); // 绘制新的矩形 pDC->DrawFocusRect(m_newRect); ReleaseDC(pDC); } CView::OnMouseMove(nFlags, point); } void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { m_startPoint = point; m_oldRect = m_newRect = CRect(point, point); SetCapture(); CView::OnLButtonDown(nFlags, point); } void CMyView::OnLButtonUp(UINT nFlags, CPoint point) { ReleaseCapture(); CView::OnLButtonUp(nFlags, point); } ``` 在这个示例中,我们使用了三个变量来保存矩形的信息: - `m_startPoint`:鼠标按下的位置,即矩形的左上角坐标。 - `m_oldRect`:上一次绘制矩形,用于在绘制新的矩形前擦除旧的矩形。 - `m_newRect`:当前正在绘制矩形。 在 `OnLButtonDown` 函数中,我们记录下鼠标按下的位置,并将 `m_oldRect` 和 `m_newRect` 初始化为一个点的矩形。 在 `OnMouseMove` 函数中,我们首先判断鼠标左键是否按下,如果按下了,就获取设备上下文(Device Context,简称DC),然后擦除旧的矩形(即 `m_oldRect`)并计算新的矩形位置和大小(即 `m_newRect`),最后绘制新的矩形。 在 `OnLButtonUp` 函数中,我们释放鼠标捕获,并调用基类的函数。 通过这样的方式,我们就可以实现橡皮筋绘制矩形的功能了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值