C/C++开发,opencv读写图像与视频

news2024/9/21 0:21:42

目录

一、opencv的图像缓存表达(cv::mat)

二、图片读写

        2.1 图片读写API

        2.2 图片读写案例

        2.3 案例编译与测试

 三、opencv的视频读写:

        3.1 视频读写接口

        3.2 视频读写案例

        3.3 编译与测试


一、opencv的图像缓存表达(cv::mat)

        OpenCV定义了各式的大型数组类型来表达视觉数据,其中cv::mat是这些类型中最重要的一个,它是opencv的c++实现最重要的核心类型,无数关于图像、视频的处理函数都是围绕cv::mat类型展开的,该类型或作为函数参数、或作为类成员、或作为返回值等。

        cv::mat可以作为任意维度的数组来使用,任意的图像数据在opencv中就是按顺序存储在n维的cv::mat 数组中。假设现在有一个3*3个像素的图片,该图片是一个灰度图,即没个像素仅有一个通道表达的数值,数值范围就是【0~255】。从图像表达的角度来看,该图片就是一个3*3的矩阵,只是每个元素的取值规定了【0~255】的数据范围,例如:

灰度img表达:
12 25 17
22 37 21
56 38 26

        如果从cv::mat 数组存储的角度来说,就是一个顺序存储的一维数组,只是,该数组还会给出类矩阵的行与列:

cv::mat表达:
dims:1
rows:3
cols:3
data:12 25 17 22 37 21 56 38 26

        且看看cv::mat 数组类型的数据存储相关成员变量定义:

         灰度如果看做是一维表达,那么彩色图像就可以看做三维(RGB)或四维(RGBA)表达,还是以3*3大小为例表达一个图像,假设一个像素的颜色排列就是RGB顺序,每个颜色通道的取值同样是【0~255】,假设该图像每个像素的各颜色通道值一样,则:

彩色img表达:
22 22 22 33 33 33 44 44 44
21 21 21 32 32 32 43 43 43
20 20 20 31 31 31 42 42 42

        而对于从cv::mat 数组存储的角度来就是那顺序存储的,只是同时给出了通道数(维度)、行、列表达:

彩色图cv::mat表达:
dims:1
rows:3
cols:3
data:22 22 22 33 33 33 44 44 44 21 21 21 32 32 32 43 43 43 20 20 20 31 31 31 42 42 42

        同样针对更多维度的数据矩阵表达都是类似的采用顺序存储方式,将矩阵数据按通道顺序、行、列顺序地排列存储。数据获取就是通过存储这些数据的指针地址,并通过维度数、行数、列数的偏移计算来取的对应偏移地址,从而获得具体数据值的。

二、图片读写

        2.1 图片读写API

        OpenCV读取和写入图片数据的两个重要函数集就是cv::imread和cv::imwrite,定义在imgcodecs.hpp:


namespace cv
{
//读取
CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR );
CV_EXPORTS_W bool imreadmulti(const String& filename, CV_OUT std::vector<Mat>& mats, int flags = IMREAD_ANYCOLOR);
CV_EXPORTS_W bool imreadmulti(const String& filename, CV_OUT std::vector<Mat>& mats, int start, int count, int flags = IMREAD_ANYCOLOR);
//...

//写入
CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, const std::vector<int>& params = std::vector<int>());
CV_WRAP static inline
bool imwritemulti(const String& filename, InputArrayOfArrays img,const std::vector<int>& params = std::vector<int>())
//...

//其他
};

        目前OpenCV支持读写和操作的图像格式有(cv::imread的API函数描述信息):

Currently, the following file formats are supported:

-   Windows bitmaps - \*.bmp, \*.dib (always supported)
-   JPEG files - \*.jpeg, \*.jpg, \*.jpe (see the *Note* section)
-   JPEG 2000 files - \*.jp2 (see the *Note* section)
-   Portable Network Graphics - \*.png (see the *Note* section)
-   WebP - \*.webp (see the *Note* section)
-   Portable image format - \*.pbm, \*.pgm, \*.ppm \*.pxm, \*.pnm (always supported)
-   PFM files - \*.pfm (see the *Note* section)
-   Sun rasters - \*.sr, \*.ras (always supported)
-   TIFF files - \*.tiff, \*.tif (see the *Note* section)
-   OpenEXR Image files - \*.exr (see the *Note* section)
-   Radiance HDR - \*.hdr, \*.pic (always supported)
-   Raster and Vector geospatial data supported by GDAL (see the *Note* section)

        2.2 图片读写案例

        下来看看这两个函数集的用法,下列代码中,先读取一张图片,存储在前面讲述的cv::mat类型中,然后对图像数据做了一些转换(这不是重点),在将数据另存储为新的一张图片:

#include "opencv2/opencv.hpp" 

int main( int argc, char** argv )
{
	cv::Mat img1,img2;
	cv::namedWindow("Example1",cv::WINDOW_AUTOSIZE );
	cv::namedWindow("Example2",cv::WINDOW_AUTOSIZE);
	img1=cv::imread( argv[1]);//读取图片
	cv::imshow("Example1",img1);
	cv::pyrDown( img1, img2);//高斯模糊及降采样
	cv::imshow("Example2",img2);
	cv::imwrite("2.png",img2);//保存转换后的图片
	cv::waitKey(0);
	return 0;
}

        代码中为了程序演示效果,采用了highgui模块的创建可视化窗口cv::namedWindow和显示图片cv::imshow的函数接口,程序最后通过在显示窗口中捕获用户输入(cv::waitKey)而结束程序应用。

        2.3 案例编译与测试

        本文编译这段程序采用的是win10下搭建的mingw编译环境(可请查看本专栏的C/C++开发,win下OpenCV+MinGW编译环境搭建博文),Makefile文件配置如下:

#/bin/sh
CX= g++ 

BIN 		:= ./
TARGET      := show_img3.exe
FLAGS		:= -std=c++11 -static
SRCDIR 		:= ./
#INCLUDES
INCLUDEDIR 	:= -I"../../opencv_MinGW/include" 
staticDir   := ../../opencv_MinGW/x64/mingw/staticlib/

#opencv_world放在前,然后是opencv依赖的第三方库,后面的库是MinGW编译工具的库

LIBDIR 	    := -L $(staticDir) -lopencv_world460 -lade -lIlmImf -lquirc -lzlib \
				-llibjpeg-turbo -llibopenjp2 -llibpng -llibprotobuf -llibtiff -llibwebp \
				-lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid 
source		:= $(wildcard $(SRCDIR)/*.cpp) 

$(TARGET) :
	$(CX) $(FLAGS) $(INCLUDEDIR) $(source)  -o $(BIN)/$(TARGET) $(LIBDIR)

clean:
	rm  $(BIN)/$(TARGET)

        编译命令如下:

         然后运行程序来看看如何:

         程序顺利打开图片1.PNG,然后将采样后,另存储为2.png图片。可以通过在两个显示窗口激活情况下输入任意信息结束程序运行,就可以在运行目录下可以顺利看到存储好的图片。

 三、opencv的视频读写:

        3.1 视频读写接口

        opencv读取视频有两种主要方式,一就是从视频中逐帧读取图片,然后不断刷新显示,就是一段视频流,即所谓的视频数据就是一组连续的图片集。

        另一种方式就是从已经存储的视频文件逐帧读取图片,同样不断刷新显示,形成视频。

        从视频获取图片通过一个关键类cv::VideoCapture来实现,该类定义在videoio.hpp文件中,支持打开视频、获取视频信息及图片数据等操作:

class CV_EXPORTS_W VideoCapture
{
public:
CV_WRAP virtual bool open(const String& filename, int apiPreference = CAP_ANY);
CV_WRAP virtual bool open(const String& filename, int apiPreference, const std::vector<int>& params);
CV_WRAP virtual bool open(int index, int apiPreference = CAP_ANY);
//...
virtual VideoCapture& operator >> (CV_OUT Mat& image);
virtual VideoCapture& operator >> (CV_OUT UMat& image);
//...
CV_WRAP virtual bool read(OutputArray image);
//..

//其他
};

        而存储视频是通过cv::VideoWriter类来实现的,cv::VideoWriter可以新建打开一个视频文件,将图片数据逐帧输出到视频文件中。

class CV_EXPORTS_W VideoWriter
{
public:
CV_WRAP virtual bool open(const String& filename, int fourcc, double fps,
                      Size frameSize, bool isColor = true);
CV_WRAP bool open(const String& filename, int apiPreference, int fourcc, double fps,
                      Size frameSize, bool isColor = true);
//...

virtual VideoWriter& operator << (const Mat& image);
virtual VideoWriter& operator << (const UMat& image);
//...
CV_WRAP virtual void write(InputArray image);
//..

//其他
};

        3.2 视频读写案例

        下来看看这段代码,这是一个从摄像头读取视频数据或从视频文件中读取数据的例子:

#include <opencv2/opencv.hpp>
#include <iostream>

int main( int argc,char* argv[])
{
    const char* win_in_name = "Example_in";
    cv::namedWindow(win_in_name,cv::WINDOW_AUTOSIZE );
    cv::VideoCapture capture;
    double fps = 25;          //摄像头频率
    cv::Size size = {640,480};//摄像头像素
    if(argc==1){
        capture.open(0);// open the first camera
    }else{
        capture.open(argv[1]);
        fps = capture.get( cv::CAP_PROP_FPS );  //视频频率
        size = {    //视频显示像素大小
        (int)capture.get(cv::CAP_PROP_FRAME_WIDTH ), (int)capture.get( cv::CAP_PROP_FRAME_HEIGHT )
    };
    }
    if(!capture.isOpened())
    {  
        // check if we succeeded 
        std::cerr << "Couldn't open capture."<< std::endl;
        return-1;
    }
    cv::VideoWriter writer;
    const char* out_video = "cpy.avi";
    std::cout << "fps="<<fps <<","<< "size="<<size<<"\n";
    writer.open( out_video,cv::VideoWriter::fourcc('M','J','P','G'),fps,size);
    cv::Mat bgr_frame;
    for(;;){
        capture >> bgr_frame;
        if(bgr_frame.empty()) break; // end if done 
        cv::imshow(win_in_name,bgr_frame );

        writer << bgr_frame;    //原视频直接写入测试
        char c = cv::waitKey(10);
        if( c == 27 )break;// allow the user to break out
    }
    capture.release();
}

        上述代码中,如果成行输出参数1是一个视频文件,则cv::VideoCapture对象读取视频文件,并读取源视频文件的频率和像素大小作为存储格式参数传递给新存储,如果是空则cv::VideoCapture对象读取摄像头视频,采用的是摄像头的频率和像素大小(本文采用的笔记本摄像头是25fps/640*480),然后cv::VideoWriter打开一个视频文件,将将采样后的视频输出存储。按键esc可以退出程序(if( c == 27 )break;)。

        3.3 编译与测试

        工程配置文件Makefile和前面图片处理工程类似:

#/bin/sh
CX= g++ 

BIN 		:= ./
TARGET      := show_video4.exe
FLAGS		:= -std=c++11 -static
SRCDIR 		:= ./
#INCLUDES
INCLUDEDIR 	:= -I"../../opencv_MinGW/include" 
staticDir   := ../../opencv_MinGW/x64/mingw/staticlib/

#opencv_world放在前,然后是opencv依赖的第三方库,后面的库是MinGW编译工具的库

LIBDIR 	    := -L $(staticDir) -lopencv_world460 -lade -lIlmImf -lquirc -lzlib \
				-llibjpeg-turbo -llibopenjp2 -llibpng -llibprotobuf -llibtiff -llibwebp \
				-lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid 
source		:= $(wildcard $(SRCDIR)/*.cpp) 

$(TARGET) :
	$(CX) $(FLAGS) $(INCLUDEDIR) $(source)  -o $(BIN)/$(TARGET) $(LIBDIR)

clean:
	rm  $(BIN)/$(TARGET)

        编译命令如下(由于引用全包头文件opencv.hpp因此编译会慢些,这也是前面博文建议大家在实际项目中精准引用对于头文件的原因):

         运行程序,打开一个本地存储的视频(mp4格式):

          视频播放完成后自动结束,输出一个新的视频文件cpy.avi,用播放器可以打开该新视频文件:

         直接运行程序(参数1为空),本文是打开了笔记本摄像头:

         同样可以在程序运行目录下(esc按键退出录像)存储摄像头录取的视频cpy.avi,覆盖了前面视频文件的另存内容;

         将程序输出存储文件判定一下大小,然后再次打开一个新文件,不间断地录像就是我们行车记录仪类似的录像功能了,大家感兴趣的可以尝试修改下。

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

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

相关文章

2023网络搭建项目改革

好久没更新了&#xff0c;哈哈哈&#xff0c;也废话不多说&#xff0c;直接进入正题。 3月的时候就有人吓我说什么网搭取消了&#xff0c;当时我还觉得高兴&#xff0c;主要是不喜欢这个行业&#xff0c;要是没了我就可以跑路了&#xff0c;哈哈&#xff0c;然后我就觉得很奇怪…

【嵌入式系统与入门】Day02 Arduino 按键、蜂鸣器与湿温度传感器

文章目录 1. 按键控制1.1 认识按键1.2 工作原理1.3 Arduino代码展示1.4 原理图1.5 实现去抖【消抖动延时】 2. 蜂鸣器控制2.1 认识蜂鸣器2.2 分类2.3 工作原理2.4 连线2.5 Arduino代码展示 3. PWM模拟量输出3.2 Arduino代码展示 4. 湿温度测量4.1 认识器件4.2 传感器接口4.3 Ar…

CMU-自主探索导航系统(TARE FAR Planner)学习-All in one

参考引用 Autonomous Exploration Development EnvironmentTARE机器人自主导航系统社区-CSDN社区云TARE机器人自主导航系统公开课1TARE机器人自主导航系统公开课2CMU团队开发的全套开源自主导航算法FAR Planner —— IROS2022 最佳学生论文&#xff1c;论文阅读&#xff1e;TAR…

【源码分析】XXL-JOB的执行器的注册流程

目的&#xff1a;分析xxl-job执行器的注册过程 流程&#xff1a; 获取执行器中所有被注解(xxlJjob)修饰的handler执行器注册过程执行器中任务执行过程 版本&#xff1a;xxl-job 2.3.1 建议&#xff1a;下载xxl-job源码&#xff0c;按流程图debug调试&#xff0c;看堆栈信息…

【ONE·C++ || stack queue (一)】

总言 主要介绍栈和队列的基本函数使用&#xff1a;栈和队列、优先级队列、适配器、反向迭代器。 文章目录 总言1、栈和队列接口基本介绍1.1、基本介绍1.2、相关例题1.2.1、最小栈1.2.2、栈的压入、弹出序列1.2.3、逆波兰表达式求值 2、适配器介绍2.1、引入&#xff1a;如何实现…

儿童用灯哪个品牌好?分享五款儿童护眼台灯品牌

家中有小朋友上了幼儿园就已经戴上了眼镜&#xff0c;太让人心疼了 近视已经成为世界难题&#xff0c;而我国儿童近视形式尤为严峻 据官方数据显示&#xff0c;我国儿童青少年总体近视率竟高达52.7% 如何保护孩子眼睛&#xff0c;儿童用灯哪个品牌好&#xff1f; 那今天&am…

Open vSwitch 入门实践(8) VXLAN实验

目录 什么是VXLAN&#xff1f; VXLAN解决了什么问题&#xff1f; VXLAN网络如何工作&#xff1f; 简单VXLAN实验 主机A 主机B 测试 什么是VXLAN&#xff1f; VXLAN&#xff08;Virtual eXtensible Local Area Network&#xff0c;虚拟扩展局域网&#xff09;&#xff0…

Spring依赖注入 - Resource注解详解及与Autowired注解区别

上篇博客我们讲了Spring中的自动注入(byName,byType)和Autowired注解的工作原理以及源码分析&#xff0c;那么这次&#xff0c;我们来分析还没讲完的&#xff0c;剩下的核心的方法&#xff1a; Nullable Object resolveDependency(DependencyDescriptor descriptor, Nullable …

0.96寸OLED液晶显示器

在日常的小项目制作中我们经常会接触到OLED液晶显示器&#xff0c;本文介绍0.96寸液晶显示器的基本原理&#xff0c;辅助我们后续的小项目开发 OLED被称为有机激光二极管&#xff0c;也被称为有机激光显示&#xff0c;OLED采用有机材料涂层和玻璃基板&#xff0c;当有电流通过…

#Chrome扩展程序开发教程--02:Hello Extensions

#Chrome扩展程序开发教程--02&#xff1a;Hello Extensions 引言1、Hello Extensions2、固定扩展程序3、重新加载扩展程序4、查看扩展程序的输出 引言 本系列博客旨在带来最新的Chrome扩展程序开发入门教程。 1、Hello Extensions 本节博客中&#xff0c;笔者将带领读者创建一个…

C++附加篇: 空间适配器

"我有时难过&#xff0c;却还有些抚慰和感动。" 一、我们来谈谈空间适配器 (1) 什么是空间配置器? STL的六大组件&#xff0c;容器、算法、迭代器、适配器、仿函数&#xff0c;最后一个也就是"空间适配器"。 所谓"空间适配器"&#x…

轻松掌握K8S使用kubectl操作配置文件挂载ConfigMap和密钥Secret知识点05

1、挂载应用配置文件配置集ConfigMap 当有许多应用如redis、mysql&#xff0c;希望将它的配置文件挂载出去&#xff0c;以便随时修改&#xff0c;可以用ConfigMap配置集 具体用法查看使用命令行操作里的 3、ConfigMap配置集实战 2、挂载应用配置文件的敏感信息Secret Secre…

JAVA开发运维(云基础设备监控)

在大型的商用系统中&#xff0c;经常需要监控云设备的健康状态&#xff0c;性能情况&#xff0c;流量数据等。及时发现系统问题&#xff0c;及时修复&#xff0c;以确保系统的高可用。检查云资源的工作内容主要包括基础监控、主动拨测、用户体验、APM监控、指标体系、业务分析、…

Java运行时内存管理

一、前言 希望能在我们平时开发写代码的时候&#xff0c;能够知道当前写的这段代码&#xff0c;内存方面是如何分配的。 我们深知&#xff0c;一个Java程序员在很多时候根本不用操心内存的释放&#xff0c;而是依靠JVM去管理&#xff0c;以前写C代码的时候&#xff0c;却要时刻…

SAP 自定义生产订单状态

1、生产订单通常系统有一整套订单状态&#xff0c;做PP的各位同学都应该知道。 CRTD状态 REL已下达 CNF已报工 DLV已入库 TECO技术性完成 等等状态这里就不在罗列了&#xff0c;可以自行在生产订单中看到 2、这篇文章主要是在生产订单系统外&#xff0c;在自定义一套状态。这个…

Spring更简单的读取和存储Bean(基于注解)

目录 ①从Maven中央仓库获取spring-context和spring-beans的依赖&#xff0c;将依赖引入到pom.xml中 ②配置扫描路径 ③添加注解存储Bean对象&#xff08;可以使用5大类注解和方法注解&#xff09; 类注解&#xff08;写在类上&#xff0c;作用于类上&#xff09; Contro…

【致敬未来的攻城狮计划】— 连续打卡第十一天:FSP固件库开发点亮第一个灯。

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

leetcode刷题--辅助工具

idea插件 插件商店搜索leetcode&#xff0c;可以让你利用idea调试leetcode的题目 插件首先需要填写用户名密码登录&#xff0c;登录上就可以在idea搜题、做题、提交等 注意&#xff1a; 一些版本登录可能登录失败&#xff0c;解决方法是换leetcode地址为leetcode.cn。 有些可…

通过用户名密码认证保障 MQTT 接入安全

认证是一种安全措施&#xff0c;用于识别用户并验证他们是否有权访问系统或服务器。它能够保护系统免受未经授权的访问&#xff0c;确保只有经过验证的用户才能使用系统。 物联网连接万物&#xff0c;对试图访问基础设施的用户进行认证至关重要。未经授权的访问存在重大的安全…

数据保管库的数据质量错误

数据保管库的数据质量错误 在过去的几年里&#xff0c;数据仓库发生了巨大的变化&#xff0c;但这并不意味着支撑健全数据架构的基本原理需要被抛在窗外。事实上&#xff0c;随着GDPR等数据法规的日益严格以及对优化技术成本的重新重视&#xff0c;我们现在看到了“Data Vault…