热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

imread函数_OpenCV实战|深入理解回调函数

点击上方“计算机视觉life”,选择“星标”快速获得最新干货学弟学妹们开始学opencv了,参看的书籍是毛星云的《oepncv3编程入门》,

点击上方“计算机视觉life”,选择“星标”

快速获得最新干货

学弟学妹们开始学opencv了,参看的书籍是毛星云的《oepncv3编程入门》,编程环境是用的VS2017或VS2019,该项目是给他们留的第一次作业,作业内容:

读取一张图片,在该图片上截取一个ROI区域,将截取的图片在一个新的窗口内展示,并将该图片保持到工程目录下。

这个题一点也不难,因为书上给的例程已经可以完成大部分工作,只需要自己添加几行代码就可以实现上述功能,但添加这几行代码的过程可以帮助你对鼠标回调函数有一个清楚的理解。

首先我们先看一个回调函数原理:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

如果你学过单片机,那你可以将鼠标回调函数理解为单片机的中断函数

运行机制:

⑴定义一个回调函数;⑵函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。

下面我们看一下opencv自带的鼠标回调函数:

void SetMouseCallback(const char* window_name, MouseCallback on_mouse,void* param = NULL);

  1. 参数window_name:为窗口的名字

  2. 参数on_mouse:用来指定窗口每次鼠标时候发生的时候,被调用函数指针

  3. 参数则为用户定义的传递到回调函数的参数

鼠标回调函数有很多已经定义的响应标识符,如下:

EVENT_MOUSEMOVE 鼠标移动EVENT_LBUTTONDOWN 左键按下EVENT_RBUTTONDOWN 右键按下EVENT_MBUTTONDOWN 滚轮按下EVENT_LBUTTONUP 左键抬起EVENT_RBUTTONUP 右键抬起EVENT_MBUTTONUP 滚轮抬起EVENT_LBUTTONDBLCLK 左键双击EVENT_RBUTTONDBLCLK 右键双击EVENT_MBUTTONDBLCLK 中间双击 EVENT_FLAG_LBUTTON 左键拖拽 EVENT_FLAG_RBUTTON 右键拖拽EVENT_FLAG_MBUTTON 中键拖拽EVENT_FLAG_CTRLKEY 按住ctrl不放EVENT_FLAG_SHIFTKEY 按住shift不放EVENT_FLAG_ALTKEY 按住alt不放

下面以一学妹的程序来说一下做这个题的大体思路:

首先我们应该读取一张图片并将他显示出来,以供我们对他进行操作:

Mat srcImage; //定义图片变量srcImage = imread("1.jpg");  //读取图片

然后我们应该定义一个鼠标回调的函数对象,也就是上面鼠标回调函数的第二个参数。我们在截取图片时的过程是这样的:

鼠标左键先按下,然后拖动鼠标成一个矩形框,然后左键松开,截出一个矩形图像。程序如下(详细注释):

//&#64;event&#xff1a;鼠标事件标志(不需要自己传参)//&#64;x:鼠标在窗口中的x坐标//&#64;y:鼠标在窗口中的y坐标//&#64;flags:自定义的宏&#xff0c;做标记符&#xff0c;Ture时开始绘制矩形&#xff0c;false时不绘制//&#64;* param:传入的图像void on_MouseHandle(int event, int x, int y, int flags, void* param) { Mat& image &#61; *(Mat*)param;switch (event) { //检查鼠标事件case EVENT_MOUSEMOVE: { //如果检测到鼠标移动if (g_bDrawingBox) { //如果绘制标识符为真&#xff0c;则记录下移动时矩形的宽&#xff0c;高 g_rectangle.width &#61; x - g_rectangle.x; g_rectangle.height &#61; y - g_rectangle.y; } }break;case EVENT_LBUTTONDOWN: { //检测到鼠标左键按下 g_bDrawingBox &#61; true; //将绘制标识符设为真&#xff0c;准备开始绘图 g_rectangle &#61; Rect(x, y, 0, 0); //用g_rectangle保存下起点 }break;case EVENT_LBUTTONUP: { //检测到鼠标左键抬起 g_bDrawingBox &#61; false; //停止绘图if (g_rectangle.width <0) { //对截取的进行处理(因为向上截取则宽&#xff0c;高为负 g_rectangle.x &#43;&#61; g_rectangle.width; //向下截取宽高为正) g_rectangle.width *&#61; (-1); }if (g_rectangle.height <0) { g_rectangle.y &#43;&#61; g_rectangle.height; g_rectangle.height *&#61; (-1); } srcROI &#61; image(g_rectangle); //用全局变量srcROI将截取的位置保存 shotScreen(srcROI); //自定义函数&#xff0c;用来展示截图并保存截图 }break; }}

用来展示截图并保存截图的自定义函数&#xff1a;

//&#64;mat&#xff1a;截取的图片void shotScreen(Mat& mat) { imshow("截图", mat); imwrite("截取的图片.jpg", mat);}

然后开始编写主函数&#xff0c;首先是一些自变量的定义&#xff0c;并读取目标图片。

然后我们要对鼠标回调函数进行初始化&#xff1a;

  namedWindow(WINDOW);   //定义一个窗口 setMouseCallback(WINDOW, on_MouseHandle, (void*)&srcImage);//对该窗口进行鼠标检测

之后是while循环&#xff1a;

while (1) { srcImage.copyTo(tempImage); //不断的用读取的图片更新临时图片tempImageif (g_bDrawingBox) DrawRectangle(tempImage, g_rectangle);//将矩形g_rectangle画到tempImage上 imshow(WINDOW, tempImage); //展示tempImageif (waitKey(10) &#61;&#61; 27) break;//当按下Esc时程序结束 }

本程序的最终运行效果如下(这个过程是无限次可循环的&#xff0c;看视频时声音调小一点啊/)&#xff1a;

而我们的工程目录下也有了一张截图&#xff1a;

04480fc05c6be00fecd68e50f7407206.png

到此我们的小项目就完成啦&#xff01;学到了什么吗&#xff1f;

附程序源码&#xff1a;

#includeusing namespace cv;#define WINDOW "程序窗口"void on_MouseHandle(int event, int x, int y, int flags, void* param);void DrawRectangle(Mat& img, Rect& box);void shotScreen(Mat& mat);Rect g_rectangle; //用来保存截图的位置信息bool g_bDrawingBox &#61; false; //绘制标识符Mat srcROI; //用来保存截图int main(int argc, char** argv) { g_rectangle &#61; Rect(-1, -1, 0, 0); Mat srcImage, tempImage; srcImage &#61; imread("1.jpg"); //读取一张图片 srcImage.copyTo(tempImage); namedWindow(WINDOW); //定义一个窗口 setMouseCallback(WINDOW, on_MouseHandle, (void*)&srcImage);//对该窗口进行鼠标检测while (1) { srcImage.copyTo(tempImage); //不断的用读取的图片更新临时图片tempImageif (g_bDrawingBox) DrawRectangle(tempImage, g_rectangle);//将矩形g_rectangle画到tempImage上 imshow(WINDOW, tempImage); //展示tempImageif (waitKey(10) &#61;&#61; 27) break;//当按下Esc时程序结束 }return 0;}void on_MouseHandle(int event, int x, int y, int flags, void* param) { Mat& image &#61; *(Mat*)param;switch (event) { //检查鼠标事件case EVENT_MOUSEMOVE: { //如果检测到鼠标移动if (g_bDrawingBox) { //如果绘制标识符为真&#xff0c;则记录下移动时矩形的宽&#xff0c;高 g_rectangle.width &#61; x - g_rectangle.x; g_rectangle.height &#61; y - g_rectangle.y; } }break;case EVENT_LBUTTONDOWN: { //检测到鼠标左键按下 g_bDrawingBox &#61; true; //将绘制标识符设为真&#xff0c;准备开始绘图 g_rectangle &#61; Rect(x, y, 0, 0); //用g_rectangle保存下起点 }break;case EVENT_LBUTTONUP: { //检测到鼠标左键抬起 g_bDrawingBox &#61; false; //停止绘图if (g_rectangle.width <0) { //对截取的进行处理(因为向上截取则宽&#xff0c;高为负 g_rectangle.x &#43;&#61; g_rectangle.width; //向下截取宽高为正) g_rectangle.width *&#61; (-1); }if (g_rectangle.height <0) { g_rectangle.y &#43;&#61; g_rectangle.height; g_rectangle.height *&#61; (-1); } srcROI &#61; image(g_rectangle); //用全局变量srcROI将截取的位置保存 shotScreen(srcROI); //自定义函数&#xff0c;用来展示截图并保存截图 }break; }}void DrawRectangle(Mat& img, Rect& box) { rectangle(img, box.tl(), box.br(), Scalar(0, 0, 255));}void shotScreen(Mat& mat) { imshow("截图", mat); imwrite("截取的图片.jpg", mat);}

交流群

欢迎加入公众号读者群一起和同行交流&#xff0c;目前有SLAM、检测分割识别、三维视觉、医学影像、GAN、自动驾驶、计算摄影、算法竞赛等微信群(以后会逐渐细分)&#xff0c;请扫描下面微信号加群&#xff0c;备注&#xff1a;”昵称&#43;学校/公司&#43;研究方向“&#xff0c;例如&#xff1a;”张三 &#43; 上海交大 &#43; 视觉SLAM“。请按照格式备注&#xff0c;否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告&#xff0c;否则会请出群&#xff0c;谢谢理解~

616661af701dbf47c5b04c0304918f9e.png

投稿也欢迎联系&#xff1a;simiter&#64;126.com

440ccaffb077d14adf7e693dfe8a4e5e.png

长按关注计算机视觉life

推荐阅读

实战 | 相机标定

实战 | 图像矫正技术

实战 | Unity下ARKit与OpenCV的结晶

实战 | 基于SegNet和U-Net的遥感图像语义分割

实战 | 文字定位与切割

实战 | 源码入门之Faster RCNN

实战 | 自己实现扫描全能王

实战 | 用OpenCV轻松生成微信国庆版头像

实战 | OpenCV 实现多张图像拼接

实战 | OpenCV相机标定与畸变校正

实战 | 教你自动查找拍糊的图

我用MATLAB撸了一个2D LiDAR SLAM

实战 | 哪个瞬间让你突然觉得CV技术真有用&#xff1f;

干货 | 史上最全 OpenCV 活体检测教程

解决方案 | 如何用普通手机准确测量物体体积&#xff1f;

Mask-RCNN模型的实现自定义对象(无人机)检测

scikit-image图像处理入门

还在用肉眼找不同吗&#xff1f;这个技术轻松搞定

OpenCV测量物体的尺寸技能 get&#xff5e;

超详讲解图像拼接/全景图原理和应用 | 附源码

最新AI干货&#xff0c;我在看  c13c6c48036bc392651d8491478e8800.gif




推荐阅读
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本文探讨了 Objective-C 中的一些重要语法特性,包括 goto 语句、块(block)的使用、访问修饰符以及属性管理等。通过实例代码和详细解释,帮助开发者更好地理解和应用这些特性。 ... [详细]
  • Python 异步编程:深入理解 asyncio 库(上)
    本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • 本文探讨了如何在模运算下高效计算组合数C(n, m),并详细介绍了乘法逆元的应用。通过扩展欧几里得算法求解乘法逆元,从而实现除法取余的计算。 ... [详细]
  • 本文介绍如何使用 Python 提取和替换 .docx 文件中的图片。.docx 文件本质上是压缩文件,通过解压可以访问其中的图片资源。此外,我们还将探讨使用第三方库 docx 的方法来简化这一过程。 ... [详细]
author-avatar
tomorrow
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有