C++OpenCV(3):基础交互(视频与鼠标操作)

这篇具有很好参考价值的文章主要介绍了C++OpenCV(3):基础交互(视频与鼠标操作)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🔆 文章首发于我的个人博客:欢迎大佬们来逛逛
🔆 OpenCV项目地址及源代码:点击这里

鼠标交互

openCV中使用鼠标的交互的函数是:setMouseCallback

可以使得激活winname为标题的窗口进行onMouse回调函数执行的鼠标交互操作,并且可以传递用户自定义变量给userdata

void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
/*******************************************************************
*			winname: 			监听窗口名称
*			onMouse:			  鼠标事件回调函数
*			userdata:			递给回调函数的可选参数
*********************************************************************/

关于MouseCallBack回调函数:

  • 就是一个函数指针
  • 传递的参数必须一致:event鼠标点击事件, ∗ ∗ ( x , y ) ∗ ∗ **(x,y)** (x,y)坐标,flag鼠标拖拽事件,void* 类型的param可以转为用户自定义变量。
typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);
//MouseCallback onMouse
void onMouse(int event,int x,int y,int flag,void *param)
/*******************************************************************
*			event: 			事件类型
*			x:				鼠标所在图像的坐标
*			y:				
*		    flags:			代表拖拽事件
*			param:			自己定义的onMouse事件的ID
*********************************************************************/

关于event和flag的枚举类型:

  • event:鼠标点击
enum MouseEventTypes {
       EVENT_MOUSEMOVE      = 0, 	//鼠标移动
       EVENT_LBUTTONDOWN    = 1, 	//鼠标左键按下
       EVENT_RBUTTONDOWN    = 2,	//鼠标右键按下 
       EVENT_MBUTTONDOWN    = 3, 	//鼠标中键按下
       EVENT_LBUTTONUP      = 4, 	//鼠标左键弹起
       EVENT_RBUTTONUP      = 5, 	//鼠标右键弹起
       EVENT_MBUTTONUP      = 6, 	//鼠标中键弹起
       EVENT_LBUTTONDBLCLK  = 7, 	//鼠标左键双击
       EVENT_RBUTTONDBLCLK  = 8, 	//鼠标右键双击
       EVENT_MBUTTONDBLCLK  = 9, 	//鼠标中间双击
       EVENT_MOUSEWHEEL     = 10,	//鼠标滚轮 正数和负数分别表示向前和向后滚动
       EVENT_MOUSEHWHEEL    = 11 	//鼠标滚轮 正数和负数分别表示向右和向左滚动  
     };
  • flag:鼠标拖拽
enum MouseEventFlags {
       EVENT_FLAG_LBUTTON   = 1,    //左键拖动
       EVENT_FLAG_RBUTTON   = 2, 	//右键拖动
       EVENT_FLAG_MBUTTON   = 4, 	//中键拖动
       EVENT_FLAG_CTRLKEY   = 8, 	//ctr拖动
       EVENT_FLAG_SHIFTKEY  = 16,	//shift拖动
       EVENT_FLAG_ALTKEY    = 32 	//alt拖动
     };

案例

在一张图片中左键点击画圆,右键点击画矩形。

有关如何在openCV中绘制图形请参见:

OpenCV(2):图像处理基础概念与操作 | HugeYlh

关键之处就是要有一个鼠标回调函数:

可以声明为类的静态成员函数,然后利用params进行强制转换为本类类型。

接着判断event点击的事件进行绘制即可。

static void mouseEvent(int event, int x, int y, int flag, void* params) {
		DrawShape* obj = static_cast<DrawShape*>(params);
		if (event == cv::EVENT_FLAG_LBUTTON) {
			//左键画圆
			obj->drawCircle(x, y, 20);
		}
		else if (event == cv::EVENT_FLAG_RBUTTON) {
			obj->drawRectangle(x, y, 20, 20);
		}
	}

然后在主程序中我们要激活这个回调函数,设置鼠标点击:

  • 其中传递回调函数要使用函数指针的形式,即传递类的静态成员函数的地址
  • p表示我们需要传递自定义变量,以便在回调函数中params转换为我们需要操作的obj
int main()
{
	DrawShape* p = new DrawShape();

	//鼠标处理过程
	cv::namedWindow("mainWindow");
	//typedef void (*MouseCallback)
	//			(int event, int x, int y, int flags, void* userdata);
	cv::setMouseCallback("mainWindow", &DrawShape::mouseEvent, p);

	while (true) {
		p->show();
		if (cv::waitKey(10) == 27) {
			break;
		}
	}
	delete p;
	p = nullptr;
	return 0;
}

完整代码参见此Github项目


视频读写交互

使用openCV做视频操作可能不会如你想象的那么容易,因为openCV是一个强大的计算机视觉库,而不是专注于视频操作的多媒体库。

使用openCV做视频处理不能添加音频

也许FFmpeg 库会满足你做多媒体开发的需求。


**VideoCapture**类型:对视频进行读取或者打开摄像头。

class VideoCapture
{
public:
    VideoCapture();
    explicit VideoCapture(const String& filename, int apiPreference = CAP_ANY);
    explicit VideoCapture(const String& filename, int apiPreference, const std::vector<int>& params);
    explicit VideoCapture(int index, int apiPreference = CAP_ANY);
    explicit VideoCapture(int index, int apiPreference, const std::vector<int>& params);
    virtual ~VideoCapture();
    virtual bool open(const String& filename, int apiPreference = CAP_ANY);
    virtual bool open(const String& filename, int apiPreference, const std::vector<int>& params);
    virtual bool open(int index, int apiPreference = CAP_ANY);
    virtual bool open(int index, int apiPreference, const std::vector<int>& params);
    virtual bool isOpened() const;
    virtual void release();
    virtual bool grab();
    virtual bool retrieve(OutputArray image, int flag = 0);
    virtual VideoCapture& operator >> (CV_OUT Mat& image);
    virtual VideoCapture& operator >> (CV_OUT UMat& image);
    virtual bool read(OutputArray image);
    virtual bool set(int propId, double value);
    virtual double get(int propId) const;
    String getBackendName() const;
    void setExceptionMode(bool enable) { throwOnFail = enable; }
    bool getExceptionMode() { return throwOnFail; }
    bool waitAny(const std::vector<VideoCapture>& streams,CV_OUT std::vector<int>& readyIndex,int64 timeoutNs = 0);
protected:
    Ptr<CvCapture> cap;
    Ptr<IVideoCapture> icap;
    bool throwOnFail;
    friend class internal::VideoCapturePrivateAccessor;
};

读取一个视频:传递给视频的文件地址即可,如果我们传递了 0,则会打开摄像头(如果存在,否则报错)

cv::VideoCapture vap("cat.MP4");
	if (!vap.isOpened()) {
		std::cout << "视频打开失败!\n";
		return -1;
	}

cv::VideoCapture vap2(0);
	if (!vap2.isOpened()) { 
		std::cout << "摄像头打开失败!\n";
		return;
	}

获取基本视频中的信息:get 函数

通过传递枚举类型来获取指定的信息:

enum VideoCaptureProperties {
       CAP_PROP_POS_MSEC       =0, //视频文件的当前位置,单位为毫秒  
       CAP_PROP_POS_FRAMES     =1, //解码/捕获的帧的基于0的索引
       CAP_PROP_POS_AVI_RATIO  =2, //视频文件的相对位置:0=影片开始,1=影片结束
       CAP_PROP_FRAME_WIDTH    =3, //视频宽度
       CAP_PROP_FRAME_HEIGHT   =4, //视频高度
       CAP_PROP_FPS            =5, //帧率
       CAP_PROP_FOURCC         =6, //4个字符的编解码器代码
       CAP_PROP_FRAME_COUNT    =7, //视频文件中的帧数
       CAP_PROP_FORMAT         =8, //视频格式
                                  
       CAP_PROP_MODE           =9, 
       CAP_PROP_BRIGHTNESS    =10, //图像亮度(摄像模式)
       CAP_PROP_CONTRAST      =11, //图像对比度(摄像模式)
       CAP_PROP_SATURATION    =12, //图像饱和度(摄像模式)
       CAP_PROP_HUE           =13, //图像的色调(摄像模式)
       CAP_PROP_GAIN          =14, //图像增益(摄像模式)
       CAP_PROP_EXPOSURE      =15, //曝光(摄像模式)
       CAP_PROP_CONVERT_RGB   =16, //图像是否应该转换为RGB的布尔标记
	     
				.......
     };

例如:

void testProerity(cv::VideoCapture vap) {
	std::cout << "宽度: " << vap.get(cv::CAP_PROP_FRAME_WIDTH) << '\n';
	std::cout << "高度: " << vap.get(cv::CAP_PROP_FRAME_HEIGHT) << '\n';
	std::cout << "帧数: " << vap.get(cv::CAP_PROP_FRAME_COUNT) << '\n';
	std::cout << "帧率: " << vap.get(cv::CAP_PROP_FPS) << '\n';
}

视频(摄像头)转图像显示

我们加载好视频后,注意到VideoCapture重载了 >> 运算符,因此可以将其重定向到一张Mat上面。

然后再一直显示这张Mat,就可以做到一张一张的图片显示,看起来像视频一样。

如果我们按下ESC则退出或者播放完成后,图片为null

void testCameraToImageShow(cv::VideoCapture vedio) {
	//显示视频
	cv::Mat image;
	while (true) {
		vedio >> image;
		if (image.empty() || cv::waitKey(10) == 27) {
			break;//为null则结束
		}
		cv::imshow("cat", image);
	}
	vedio.release();
}

对于摄像头的转图片显示,我们只需要传递一个 VideoCapture xxx(0) 即可其他的全是一样的。


视频转图片并且保存

基本操作与上类似,只不过在**imshow**的地方我们改成了保存的操作(当然你也可以一边显示一边保存)

保存过程:imwrite函数

  • name:第一个参数表示保存的路径,传递前缀文件名字后缀来完成:cat/1.png
  • image:保存的图片
void testCameraToPngImageSave(cv::VideoCapture vedio,std::string& prefilename) {
	cv::Mat image;
	int index = 1;
	while (true) {
		vedio >> image;
		if (image.empty() || cv::waitKey(10) == 27) {
			break;
		}
		std::string name = prefilename + std::to_string(index++) + ".png";
		cv::imwrite(name, image);
	}
	vedio.release();
}

摄像头转图片并保存

如果是摄像头则我们不能用:imwrite

有一个**VideoWriter** 提供了这样的操作:通过创建一个VideoWriter类型的变量,然后通过 << 重载往它里面写入来完成。

注意**VideoWriter** 的创建:

  • “save.avi” :表示保存的视频路径
  • cv::VideoWriter::fourcc(‘M’, ‘J’, ‘P’, ‘G’):视频的解码器,MJPG表示mp4格式,点击了解更多格式
  • 30:fps帧率
  • cv::Size(width,height):保存的尺寸大小
  • true:是否显示颜色(三通道)
save.open("save.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'),30, cv::Size(width,height), true);

具体操作如下:

void CameraSave() {
	cv::VideoCapture cap(0);
	if (!cap.isOpened()) {
		std::cout << "摄像头打开失败!\n";
		return;
	}
	//获取宽度和高度
	int width = cap.get(cv::CAP_PROP_FRAME_WIDTH);
	int height = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
	cv::VideoWriter save{};
	save.open("save.avi", 
		cv::VideoWriter::fourcc('M', 'J', 'P', 'G'),
		30, cv::Size(width,height), true);
	cv::Mat image;
	while (true) {
		cap >> image;
		cv::imshow("摄像头", image);
		save << image; //往流中写入
		if (cv::waitKey(10) == 27) {
			break;
		}
	}
	cap.release();
	save.release();
}

滑动条交互

对于滑动条交互也是和鼠标交互类似的:

int createTrackbar(const String& trackbarname, const String& winname,int* value, int count,TrackbarCallback onChange = 0,void* userdata = 0);
/*******************************************************************
*			trackbarname: 		滑动条名字
*			winname:			    依附窗口名
*			value:				    滑块位置
*			count:				    滑块最大值(最小值是0)
*			onChange:			  滑块回调函数
*			userdata:			  用户回传给回调函数的数据
*********************************************************************/

TrackbarCallback 回调函数:

typedef void (*TrackbarCallback)(int pos, void* userdata);
void On_Trackbar(int pos, void* userdata);
/*******************************************************************
*			pos: 			    位置
*			userdata:			用户回传给回调函数的数据
*********************************************************************/

滑动条调整图片的亮度

涉及到对图片的像素操作

详细请看上节内容:

OpenCV(2):图像处理基础概念与操作 | HugeYlh

我们规定一个初始值:current和一个最大值:maxValue,通过调节可以调整此值:

定义:

  • 一个beta表示偏移量
  • alpha表示亮度的调整值
  • copyMat:对一个新的Mat进行像素运算操作,否则无法还原。

对于三通道RGB来说:

  • 如果我们调整为最小值:0
    • 则RGB为(30,30,30)此时为黑色,因此beta就是我们的最小颜色值,即偏移量
  • 然后alpha会根据我们当前滑动条的值进行调整,变大
    • 然后执行像素运算后(xxx,xxx,xxx)就会越来越大,达到亮度提高的效果。

最后显示此图像即可。


对于此回调函数的实现:

//滑动条回调函数
	static void OnTrack(int pos,void* params){
		TrackBar* obj = static_cast<TrackBar*>(params);
		cv::Mat copyMat = cv::Mat::zeros(obj->mt.size(), obj->mt.type());
		int dims = obj->mt.channels();
		float beta = 30, alpha = 0.1 + (float)pos / 10.0;
		for (int i = 0; i < obj->mt.rows; i++) {
			for (int j = 0; j < obj->mt.cols; j++) {
				if (dims == 1) {
					uchar pix = obj->mt.at<uchar>(i, j);
					copyMat.at<uchar>(i,j) = cv::saturate_cast<uchar>(pix * alpha + beta);
				}
				else if (dims == 3) {
					cv::Vec3b vec = obj->mt.at<cv::Vec3b>(i, j);
					float b = vec[0], g = vec[1], r = vec[2];
					copyMat.at<cv::Vec3b>(i, j)[0] = cv::saturate_cast<uchar>(b * alpha + beta);
					copyMat.at<cv::Vec3b>(i, j)[1] = cv::saturate_cast<uchar>(g * alpha + beta);
					copyMat.at<cv::Vec3b>(i, j)[2] = cv::saturate_cast<uchar>(r * alpha + beta);
				}
				else {
					return;
				}
			}		
		}
		cv::imshow("trackBarWindow", copyMat); //显示操作后的图像
	}

完整代码详见Github项目源码。

https://github.com/luumod/openCV-learning-record文章来源地址https://www.toymoban.com/news/detail-603587.html

到了这里,关于C++OpenCV(3):基础交互(视频与鼠标操作)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Ubuntu18.04下opencv基础操作(打开图片及视频)

    开源计算机视觉(OpenCV)是一个主要针对实时计算机视觉的编程函数库。 OpenCV的应用领域包括: 2D和3D功能工具包 运动估计 面部识别系统 手势识别 人机交互 移动机器人 动作理解 物体识别 分割和识别 实体影像立体视觉:来自两个摄像机的深度感知 运动中的结构(SFM) 运动跟踪 增

    2024年04月17日
    浏览(32)
  • opencv鼠标响应与操作

    这节讲得好,建议仔细揣摩 和有一节课调整亮度对比度的有共同点. 代码注释都已标注,欢迎评论 实现效果

    2024年02月20日
    浏览(32)
  • opencv鼠标操作与响应

    2024年02月21日
    浏览(36)
  • OpenCV9-窗口交互操作

    图像窗口滑动条就是显示在图像的窗口中,能够通过滑动改变数值的滑动条。OpenCV中使用createTrackbar函数在显示图像的窗口中创建滑动条: 下面是拖动滑动条改变图像亮度的代码: OpenCV提供了鼠标响应相关函数setMouseCallback: 鼠标响应的回调函数: event鼠标响应事件:EVENT_

    2024年02月07日
    浏览(43)
  • OpenCV-Python学习(15)—— OpenCV 鼠标操作和响应(cv.setMouseCallback)

    1. 学习目标 学习如何在OpenCV中处理鼠标事件; 学习鼠标事件与回调; 学习鼠标事件回调函数的基本流程。 2. Callback 基本流程 3. 鼠标事件 cv.setMouseCallback 函数说明 3.1 cv.setMouseCallback() 函数使用 3.2 参数说明 参数 说明 winname 表示监听鼠标事件的窗口。 onMouse 表示响应函数,即

    2024年02月10日
    浏览(50)
  • 计算机视觉OpenCv学习系列:第四部分、键盘+鼠标响应操作

    键盘响应中有一个函数叫做 waitKey ,所有的获取键盘键值都是通过waitKey函数实现的。 1.键盘响应事件 cv.waitKey( [, delay] ) -- retval delay如果没有声明或者delay=0,表示一直阻塞 delay大于0,表示阻塞指定毫秒数 retval返回的对应键盘键值,注意:在不同的操作系统中可能会有差异! 典

    2024年02月09日
    浏览(49)
  • Opencv-C++笔记 (6) : opencv-图片和视频操作

    filename:需要读取图像的文件名称,包含图像地址、名称和图像文件扩展名 flags:读取图像形式的标志,如将彩色图像按照灰度图读取,默认参数是按照彩色图像格式读取,可 选参数在表2-3给出。 函数用于读取指定的图像并将其返回给一个Mat类变量,如果图像文件不存在、破

    2024年02月09日
    浏览(48)
  • 【OpenCV实现鼠标绘图,轨迹栏做调色板,图像的基本操作】

    在OpenCV中操作鼠标事件 函数:cv.setMouseCallback() 目的是在鼠标双击的地方画一个圆。首先,我们需要创建一个鼠标回调函数,该函数会在鼠标事件发生时执行。鼠标事件包括左键按下、左键松开、左键双击等等。通过获取每个鼠标事件的坐标(x, y),我们就能实现画圆的功能。

    2024年02月05日
    浏览(64)
  • openCV视频的读写操作

    在opencv当中视频的打开主要依靠一个类VideoCapture,在构造对象时,打开视频和摄像头主要区别就是初始的参数 如下列 显然我们不难看出,如果你想要打开摄像头,那么你的初始化参数就应该是0; 如果你要打开视频文件的时候,那么就应该是你当前视频的 路径+名字 用来检测

    2024年02月01日
    浏览(53)
  • OpenCV---视频操作

    没有摄像头,我们可以使用一款APP IP摄像头 cap = cv.VideoCapture(0) 0改成IP摄像头内容中的局域网IP

    2024年02月09日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包