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

文章目录

  • 问题
  • 对策
  • 代码
    • 完整工程
  • 结果
    • 使用Picture控件的RedrawWindow()的效果
    • 使用Dialog的RedrawWindow()的效果
    • 使用Picture控件的RedrawWindow(),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;elsem_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;elsem_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的坐标范围解决。

结论

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/815279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

测试计划和测试报告

1、软件测试计划简介 测试计划&#xff0c;一般是主管写&#xff0c;在需求分析之后&#xff0c;测试工作开始之间做的一些准备划工作。一般包含以下内容&#xff1a;5W1H 目的、测试范围、测试进度安排、测试人员、测试环境、测试方法工具&#xff0c;风险评估 &#xff08;w…

redis知识整理

redis知识整理 什么是缓存穿透&#xff0c;怎么解决布隆过滤器 什么是缓存击穿&#xff0c;怎么解决互斥锁和分布式锁 什么是缓存雪崩&#xff0c;怎么解决Redis作为缓存&#xff0c;mysql如何与redis进行同步呢&#xff1f;&#xff08;双写一致&#xff09;一致性要求高排他锁…

如何在 MySQL 中开启日志记录并排查操作记录

在数据库管理中&#xff0c;能够追踪和审查操作记录是至关重要的。这不仅有助于识别和分析正常的数据库活动&#xff0c;还可以在数据泄露或未经授权的更改发生时进行调查和响应。本文将介绍如何在 MySQL 中开启通用日志记录&#xff0c;并如何排查操作记录。 开启 MySQL 通用…

2024/4/14周报

文章目录 摘要Abstract文献阅读题目创新点CROSSFORMER架构跨尺度嵌入层&#xff08;CEL&#xff09;CROSSFORMER BLOCK长短距离注意&#xff08;LSDA&#xff09;动态位置偏置&#xff08;DPB&#xff09; 实验 深度学习CrossFormer背景维度分段嵌入&#xff08;DSW&#xff09…

c++ 根据ip主机号和子网掩码随机生成ip

在C中&#xff0c;可以使用以下方法根据给定的IP地址和子网掩码来随机生成IP地址。这里使用了库来生成随机数&#xff0c;以及<arpa/inet.h>库来处理IP地址。 #include <iostream> #include <random> #include <arpa/inet.h>std::string random_ip(co…

面试经典150题 删除有序数组中的重复项 II

面试经典150题 day4 题目来源我的题解方法一 双指针 题目来源 力扣每日一题&#xff1b;题序&#xff1a;80 我的题解 方法一 双指针 left和right分别指向相同元素的左右边界&#xff0c;count记录重复元素的个数&#xff0c;index记录最终数组的长度。 当元素没有重复时&am…

数据库工程师题目

【软考通】 程序中全局变量的存储空间在&#xff08;B&#xff09;分配。 A 代码区 B 静态数据区 C 栈区 D 堆区 程序运行时的用户内存空间一般划分为代码区、静态数据区、栈区和堆区&#xff0c;其中栈区和堆区也称为动态数据区。全局变量的存储空间在静态数据区。 以下…

1.0 Hadoop 教程

1.0 Hadoop 教程 分类 Hadoop 教程 Hadoop 是一个开源的分布式计算和存储框架&#xff0c;由 Apache 基金会开发和维护。 Hadoop 为庞大的计算机集群提供可靠的、可伸缩的应用层计算和存储支持&#xff0c;它允许使用简单的编程模型跨计算机群集分布式处理大型数据集&#xf…

IntelliJ IDEA 配置攻略:提高开发效率的 5 个秘诀

一、前言 A. 主要介绍内容&#xff1a; 本篇博客将着重介绍如何通过高效使用 IntelliJ IDEA&#xff0c;提高开发效率以及编写出更好的代码。主要内容包括 IntelliJ IDEA 快速配置&#xff0c;自动编写代码&#xff0c;快捷代码提示&#xff0c;快速生成代码段&#xff0c;快…

SAFe认证Leading SAFe官方认证班/Leading SAFe领导大规模敏捷认证课

课程简介 SAFe – Scaled Agile Framework是目前全球运用最广泛的大规模敏捷框架&#xff0c;也是全球敏捷相关认证成长最快、最被认可、最有价值的规模化敏捷认证&#xff0c;目前全球SAFe认证专业人士已达120万人。 据官方统计&#xff0c;获得新证书的IT专业人士的平均工资…

排序算法之快速排序

目录 一、简介二、代码实现三、应用场景 一、简介 算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度排序方式稳定性快速排序O( N N N log ⁡ 2 N \log_{2}N log2​N)O( N N N log ⁡ 2 N \log_{2}N log2​N)O(n^2)O( log ⁡ 2 N \log_{2}N log2​N)In-place不稳定 稳…

HLOOKUP函数结合数据验证实现的动态图表

小伙伴们&#xff0c;大家好啊&#xff01;今天我们分享一个使用HLOOKUP函数结合数据验证实现的动态图表技巧&#xff1b; 接下来我们具体说说操作步骤吧。 步骤1&#xff1a;选中A列的【路口车辆通过数】单元格区域&#xff0c;复制粘贴后到右边的空白区域&#xff0c;如I列…

加权 [Picard皮卡尔法 ](或非加权)最小二乘相位解包裹-matlab

phase_unwrap.m %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 根据Ghiglia和Romero(1994)提出的方法,基于加权和非加权最小二乘法进行相位解包裹 % 链接:https://doi.org/10.1364/JOSAA.11.000107 % 输…

【ArcGIS Pro二次开发】(85):Aspose.Cells中的Excel操作

Aspose.Cells是一款功能强大的Excel文档处理和转换控件&#xff0c;开发人员和客户电脑无需安装Microsoft Excel也能在应用程序中实现类似Excel的强大数据管理功能。 1、获取工作薄Workbook string excelFile "C:\Users\Administrator\Desktop\FE.xlsx"; Workbook …

日志数据采集存储

海量日志采集 在业务高峰期下&#xff0c;会有同时成千上万个客户端建立连接&#xff0c;实时上报日志数据。 在上面的高峰期场景下&#xff0c;日志采集服务会有不小的压力&#xff0c;如果程序代码逻辑处理稍有不当&#xff0c;就会造成服务卡顿、CPU 占用过高、内存溢出等…

数组(java)

目录 数组的定义和使用&#xff1a; 数组的初始化&#xff1a; 遍历数组&#xff1a; 数组是引用类型 初始JVM的内存分布 再读引用变量 认识null 数组的应用场景 作为函数的参数 作为函数的返回值 数组练习 数组转字符串 排序 冒泡排序 数组逆序 数组求平均…

代码随想录-算法训练营day07【哈希表02:四数相加、赎金信、三数之和、四数之和】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第三章 哈希表part02 今日任务 ● 454.四数相加II ● 383. 赎金信 ● 15. 三数之和 ● 18. 四数之和 ● 总结 详细布置 454.四数相加II 建议&#xff1a;本题是 使用map 巧妙解决的问题&#xff0c;好好体会一下 …

tsReact 创建项目

全局安装create-react-app 脚手架 npm install -g create-react-app 或者 yarn add -g create-react-app初始化项目 $ npx create-react-app demo --template typescriptnpm run eject 释放配置文件 通过脚手架创建完项目后&#xff0c;会发现没有任何 配置文件&#xff08;…

C++| QT自定义信号和槽

前言&#xff1a;之前学习QT都是使用自带的信号进行槽函数触发&#xff0c;一直没有找到需要使用自定义信号的场景&#xff0c;但最近读QT项目代码的时候&#xff0c;应用到了自定义信号&#xff0c;所以特地写这篇总结一下。 QT自定义信号和槽 信号和槽为什么要自定义信号如何…

CSRF漏洞初解

CSRF漏洞初解 1.什么是CSRF漏洞 CSRF&#xff08;Cross-Site Request Forgery&#xff09;漏洞是一种Web应用程序安全漏洞&#xff0c;也被称为"One-Click Attack"或"Session Riding"。它利用了Web应用程序对用户在其他网站上已经验证过的身份的信任&…