• OpenCV 4.0.0学习笔记 (一) 图像与视频的读写


    目录

    读取图片

    imread方法

    图片读取出错处理

    读取的图片属性

    写入图片

    imwrite方法

    带透明度的png图像

    读取视频

    capture结构体

    下一帧与释放

    读取视频属性 get()方法

    写入视频

    VideoWriter类

    显示窗口Windows

    窗口函数

    一个窗口显示多张图片

    Python numpy组合

    matplotlib划分窗口显示多张图:

    热门文章

    最新评论

    您愿意向朋友推荐“博客详情页”吗?

    最新文章

    目录

    分类专栏


    读取图片

    文档地址

    imread方法

    Mat imread(const String & filename ,int flags=IMREAD_COLOR);
    
    • 1

    imread读取图像,返回Mat对象,两个参数,第一个是文件名,支持位图bmp、dib,JPEG图像,PNG,webp,pbm,pgm,ppm,TIFF等多种图像,读取失败则返回空矩阵

    第二个读取方式,默认值是1 flag = 1 返回三通道彩色图 flag = 0 灰度图 flag = -1 原图带alpha通道

    如果返回三通道,编码顺序是BGR。支持读取的图像格式如下

    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)
    • 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)
    1. #include
    2. #include
    3. using namespace cv;
    4. using namespace std;
    5. int main(int argc, char **argv) {
    6. // 图片路径 可以是/或者//或者\\或者 /、//、\\混合;但不能是单独的反斜杠 \
    7. Mat src = imread("C:/Users/muyi/Pictures/pic/5503.jpg");
    8. imshow("input", src);
    9. waitKey(0);
    10. destroyAllWindows();
    11. return 0;
    12. }
    1. import cv2
    2. img = cv2.imread(filename,flags) # 返回array矩阵,读取失败则是nonetype

    图片读取出错处理

    文件损坏、不存在、权限错误等问题导致读取图片失败,程序不会报错,但是返回的是空矩阵,不处理后续使用可能会出错。

    1. Mat src = imread("test.png");
    2. if (src.empty()) {
    3. printf("could not load image...\n");
    4. return -1;
    5. }
    1. src = cv2.imread("test.png")
    2. # 断言判断读取是否成功
    3. assert type(src)==None,"load image error"
    4. # 或者if语句判断
    5. if src.all() == None: #
    6. print("load image error")
    7. # 或者 try,自定义异常
    8. def read_img(path):
    9. img = cv2.imread(path)
    10. if img == None:
    11. raise Exception("load image error")
    12. return img
    13. try :
    14. read_img('eee.png')
    15. except Exception as err:
    16. print(err)
    17. else:
    18. 后续处理

    读取的图片属性

    C++版本,读取图像以Mat对象形式存储,Python版本以np.array形式存储

    1. Mat img = imread("C:\Users\muyi\Pictures\pic\5503.jpg");
    2. // Mat类的部分属性
    3. cout << "dims:" << img.dims << endl; // 矩阵的维度
    4. cout << "rows:" << img.rows << endl; // 矩阵的行数
    5. cout << "cols:" << img.cols << endl; // 矩阵列数
    6. cout << "channels:" << img.channels() << endl; // 图像通道数
    7. cout << "type:" << img.type() << endl; // 表示了矩阵中元素的类型以及矩阵的通道个数
    8. cout << "depth:" << img.depth() << endl; // 深度
    9. cout << "elemSize:" << img.elemSize() << endl; // 矩阵一个元素占用的字节数
    10. cout << "elemSize1:" << img.elemSize1() << endl; //矩阵元素一个通道占用的字节数= elemSize / channels
    11. // type为 CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes

    图像矩阵的type取值:参考博客
    它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。 C1,C2,C3,C4则表示通道是1,2,3,4

    type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用depth

    depth 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
    将type的预定义值去掉通道信息就是depth值: CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F

    表1. Mat对象type的取值表

    CV_8UC1CV_8UC2CV_8UC3CV_8UC4
    CV_8SC1CV_8SC2CV_8SC3CV_8SC4
    CV_16UC1CV_16UC2CV_16UC3CV_16UC4
    CV_16SC1CV_16SC2CV_16SC3CV_16SC4
    CV_32SC1CV_32SC2CV_32SC3CV_32SC4
    CV_32FC1CV_32FC2CV_32FC3CV_32FC4
    CV_64FC1CV_64FC2CV_64FC3CV_64FC4
    1. Mat img(3, 4, CV_16UC4, Scalar_(1, 2, 3, 4));//3X4的矩阵 16位无符号4通道
    2. //Scalar_是一个模板向量,用来初始化矩阵的每个像素,因为矩阵具有4个通道,Scalar_有四个值。

    python版本cv图像格式,type(img) = numpy.ndarray 因此,它具有array的一切属性和方法,而不同于c++版本中Mat的deepth()、type()等写法。

    1. img = cv2.imread("test.png")
    2. print(img.shape) # [M,N,K] 行 列 通道数
    3. print(img.size) # M*N*K
    4. # 一些特殊方法
    5. print(img.mean()) #平均值 img.sum() 元素和 std()标准差 等等
    6. img = img.flatten()
    7. img = img.ravel()
    8. img = img.reshape()

    写入图片

    [文档地址][OpenCV: Image file reading and writing]

    imwrite方法

    1. bool cv::imwrite(const String & filename, InputArray img , const std::vector<int>¶ms = std::vector<int>)
    retval = cv2.imwrite(filename, img[, params])
    

    支持写入的图像格式与imread一致。8位单通道或3通道BGR编码数据才可以使用该函数。

    16位无符号图像数据,可以保存为PNG, JPEG2000 和TIFF

    32位浮点图像数据,可以保存为TIFF,OpenEXR,Radiance HDR格式

    3通道32位浮点图像数据被保存TIFF格式,则存储为高动态范围图像(High-Dynamic Range,HDR),每个像素4字节

    8位带透明度的PNG图像保存时需要创建第四个通道,最终数据格式是BGRA

    对于非上述类型数据,可以通过mat.convertTo函数进行转换,然后写入图像文件

    In general, only 8-bit single-channel or 3-channel (with ‘BGR’ channel order) images can be saved using this function

    • 16-bit unsigned (CV_16U) images can be saved in the case of PNG, JPEG 2000, and TIFF formats
    • 32-bit float (CV_32F) images can be saved in TIFF, OpenEXR, and Radiance HDR formats; 3-channel (CV_32FC3) TIFF images will be saved using the LogLuv high dynamic range encoding (4 bytes per pixel)
    • PNG images with an alpha channel can be saved using this function. To do this, create 8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535 (see the code sample below).

    带透明度的png图像

    c++源码,创建带透明度的PNG图像。(出自cv文档imwrite函数说明)

    1. #include
    2. using namespace cv;
    3. using namespace std;
    4. static void createAlphaMat(Mat &mat)
    5. {
    6. CV_Assert(mat.channels() == 4); // 等同于c++里面的assert,条件为false返回错误信息
    7. for (int i = 0; i < mat.rows; ++i)
    8. {
    9. for (int j = 0; j < mat.cols; ++j)
    10. { // 行列遍历进行赋值 Vec是OpenCV定义的向量模板类
    11. Vec4b& bgra = mat.at(i, j);
    12. bgra[0] = UCHAR_MAX; // Blue define UCHAR_MAX 0xff
    13. bgra[1] = saturate_cast((float (mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX); // Green saturate_cast是一个防止颜色操作溢出的函数,数据小于0置0;大于255置255
    14. bgra[2] = saturate_cast((float (mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX); // Red
    15. bgra[3] = saturate_cast(0.5 * (bgra[1] + bgra[2])); // Alpha
    16. }
    17. }
    18. }
    19. int main()
    20. {
    21. // Create mat with alpha channel
    22. Mat mat(480, 640, CV_8UC4);
    23. createAlphaMat(mat); // 创建矩阵数据
    24. vector<int> compression_params;
    25. compression_params.push_back(IMWRITE_PNG_COMPRESSION); // 枚举值16
    26. compression_params.push_back(9);
    27. bool result = false;
    28. try
    29. {
    30. result = imwrite("alpha.png", mat, compression_params);
    31. }
    32. catch (const cv::Exception& ex)
    33. {
    34. fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
    35. }
    36. if (result)
    37. printf("Saved PNG file with alpha data.\n");
    38. else
    39. printf("ERROR: Can't save PNG file.\n");
    40. return result ? 0 : 1;
    41. }

    python实现

    1. import cv2
    2. import numpy as np
    3. img = np.zeros((480,640,4),np.float)
    4. def saturate(num): ## 不清楚cv2防止颜色溢出函数,因此自定义了一个类似的函数
    5. if num >255.0:
    6. return 255.0
    7. if num <0.0:
    8. return 0.0
    9. else:
    10. return num
    11. rows = img.shape[0]
    12. cols = img.shape[1]
    13. for i in range(rows):
    14. for j in range(cols):
    15. temp = img[i][j] # 像素[i][j]位置的四个通道数据
    16. temp[0] = 0xff
    17. temp[1] = saturate((cols-j)/rows*255.0)
    18. temp[2] = saturate((rows-j) /cols *255.0)
    19. temp[3] = saturate(temp[2]+temp[1]) #可以看到不同透明度,显示效果不一样
    20. cv2.imwrite('alpha.png',img)

    效果如图
    在这里插入图片描述

    读取视频

    videoCapture结构体,可以读取文件视频、网页视频流和摄像头的数据。

    [文档地址][OpenCV: cv::VideoCapture Class Reference]

    capture结构体

    函数原型 VideoCapture (const String &filename, int apiPreference=CAP_ANY)

    cv2.VideoCapture(filename[, apiPreference])

    Opens a video file or a capturing device or an IP video stream for video capturing with API Preference

    第一个参数是数据流或视频路径,第二个是API设置,读取的摄像头编号,默认CAP_ANY=0,自动检测摄像头。多个摄像头时,使用索引0,1,2,…进行编号调用摄像头。 apiPreference = -1时单独出现窗口,选取相应编号摄像头

    通常使用 ==bool isOpened()==判断是否打开视频或摄像头成功。

    下一帧与释放

    cap >> frame或者 ==cap.read(frame)==读取下一帧,函数定义是CV_WRAP virtual bool read(OutputArray image);读取帧失败会返回布尔值false,因此可以进行判断

    视频帧读取的 read 、grab 、retrieve三种方式:

    retrieve速度比grab慢很多,有时可以通过grab跳过不需要的帧,而不需要用read解码每一帧。

    1. cap.read(frame) 结合grab和retrieve的功能,抓取下一帧并解码
    2. cap.grap() 从设备或视频获取下一帧,获取成功返回true否则false
    3. cap.retrieve(frame) 在grab后使用,对获取到的帧进行解码,也返回true或false
    1. #include
    2. #include
    3. using namespace cv;
    4. using namespace std;
    5. int main(int, char**)
    6. {
    7. Mat frame; //定义帧
    8. VideoCapture cap;
    9. int deviceID = 0; // 0 = open default camera
    10. int apiID = cv::CAP_ANY; // 0 = autodetect default API
    11. cap.open(deviceID + apiID); //打开摄像头
    12. // check if we succeeded
    13. if (!cap.isOpened()) {
    14. cerr << "ERROR! Unable to open camera\n";
    15. return -1;
    16. }
    17. //--- GRAB AND WRITE LOOP
    18. cout << "Start grabbing" << endl
    19. << "Press any key to terminate" << endl;
    20. for (;;)
    21. {
    22. // wait for a new frame from camera and store it into 'frame'
    23. cap.read(frame); //读取下一帧,并可以返回读取成功与否
    24. //等价于 cap >> frame 同时等价于 cap.grab();cap.retrieve(frame);
    25. if (frame.empty()) {
    26. cerr << "ERROR! blank frame grabbed\n";
    27. break;
    28. }
    29. imshow("Live", frame);
    30. if (waitKey(100) >= 0)
    31. break;
    32. }
    33. // the camera will be deinitialized automatically in VideoCapture destructor
    34. return 0;
    35. }
    1. import cv2
    2. cap = cv2.VideoCapture(0)
    3. if cap.isOpened():
    4. while True:
    5. ret, prev = cap.read() # ret是读取状态,prev下一帧
    6. """
    7. 等价于
    8. if cap.grab():
    9. ret, prev = cap.retrieve()
    10. """
    11. if ret==True:
    12. cv2.imshow('video', prev)
    13. else:
    14. break
    15. if cv2.waitKey(20)==27:
    16. break
    17. cv2.destroyAllWindows()

    release()在使用完后进行手动释放capture对象

    读取视频属性 get()方法

    函数原型:double cv::VideoCapture::get(int propId)

    retval=cv2.VideoCapture.get(propId)

    1. cv2.VideoCapture.get(0) 视频文件的当前位置(播放)以毫秒为单位
    2. cv2.VideoCapture.get(1) 基于以0开始的被捕获或解码的帧索引
    3. cv2.VideoCapture.get(2) 视频文件的相对位置(播放):0=电影开始,1=影片的结尾。
    4. cv2.VideoCapture.get(3) 在视频流的帧的宽度
    5. cv2.VideoCapture.get(4) 在视频流的帧的高度
    6. cv2.VideoCapture.get(5) 帧速率
    7. cv2.VideoCapture.get(6) 编解码的4字-字符代码
    8. cv2.VideoCapture.get(7) 视频文件中的帧数
    9. cv2.VideoCapture.get(8) 返回对象的格式
    10. cv2.VideoCapture.get(9) 返回后端特定的值,该值指示当前捕获模式
    11. cv2.VideoCapture.get(10) 图像的亮度(仅适用于照相机)
    12. cv2.VideoCapture.get(11) 图像的对比度(仅适用于照相机)
    13. cv2.VideoCapture.get(12) 图像的饱和度(仅适用于照相机)
    14. cv2.VideoCapture.get(13) 色调图像(仅适用于照相机)
    15. cv2.VideoCapture.get(14) 图像增益(仅适用于照相机)(Gain在摄影中表示白平衡提升)
    16. cv2.VideoCapture.get(15) 曝光(仅适用于照相机)
    17. cv2.VideoCapture.get(16) 指示是否应将图像转换为RGB布尔标志
    18. cv2.VideoCapture.get(17) × 暂时不支持
    19. cv2.VideoCapture.get(18) 立体摄像机的矫正标注(目前只有DC1394 v.2.x后端支持这个功能)

    写入视频

    函数文档OpenCV: cv::VideoWriter Class Reference

    VideoWriter类

    1. cv::VideoWriter::VideoWriter(const String & filename,int fourcc,double fps,Size frameSize,bool isColor = true ) // isColor
    1. cv2.VideoWriter(filename, fourcc, fps, frameSize[, isColor])
    2. cv.VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor])

    写入视频需要指定视频的帧率fps , 帧尺寸framesize ,编码格式fourcc

    framesize的大小应该与写入的每一帧图像尺寸大小一致

    输出文件类型要与编码类型一致

    几种常用视频编解码器

    生成文件占用空间最小的编码方式是MPEG-4.2 。在VideoWriter类的构造函数参数为CV_FOURCC(‘M’, ‘P’, ‘4’, ‘2’) 。

    最大的是MPEG-1,对应在VideoWriter类的构造函数参数为CV_FOURCC(‘P’,‘I’,‘M’,‘1’) ,所占磁盘空间是前者的5.7倍。

    fourcc定义

    static int cv::VideoWriter::fourcc(char c1,char c2,char c3,char c4 )
    
    retval  =  cv2.VideoWriter_fourcc(  c1, c2, c3, c4  )
    
    1. cv2.VideoWriter_fourcc('m','p','a','v') AVI或者mp4文件
    2. cv2.VideoWriter_fourcc('M','J','P','G') avi或者mp4 motion-jpeg编码
    3. cv2.VideoWriter_fourcc('P','I','M','I') MPEG-1编码 AVI文件
    4. cv2.VideoWriter_fourcc('X','V','I','D') MPEG-4编码 AVI文件
    5. cv2.VideoWriter_fourcc('T','H','E','O') Ogg Vorbis 后缀名 ogv
    6. cv2.VideoWriter_fourcc('F','L','V','1') flash视频,后缀名 flv

    c++创建writer对象和写入图像帧有两种方式

    1. #include
    2. #include
    3. using namespace cv;
    4. using namespace std;
    5. int main() {
    6. //方法1 定义对象,然后使用open方法开启 opencv2/videoio.hpp定义了fourcc
    7. VideoWriter out;
    8. int fourcc = out.fourcc('M', 'J','P', 'G');
    9. out.open("video.mp4", fourcc, 30.0, cv::Size(640, 480), // 单帧图片分辨率为 640x480
    10. true // 只输入彩色图
    11. );
    12. /* 方法2 构造函数*/
    13. VideoWriter out(
    14. const string& filename, // 输入文件名
    15. int fourcc, // 编码形式,
    16. double fps, // 输出视频帧率
    17. cv::Size frame_size, // 单帧图片的大小
    18. bool is_color = true // 如果是false,可传入灰度图像
    19. );
    20. }

    python将文件夹中所有图片写入视频

    1. import cv2
    2. import os
    3. im_dir = 'det' #图片路径
    4. video_dir = 'out4.avi' #输出视频路径
    5. fps = 23.977 #输出视频路径
    6. #图片数
    7. num = 888
    8. img_size = (500,300)
    9. #fourcc = cv2.cv.CV_FOURCC('M','J','P','G')#opencv2.4
    10. fourcc = cv2.VideoWriter_fourcc('M','J','P','G') #opencv3.0之后的写法
    11. videoWriter = cv2.VideoWriter(video_dir, fourcc, fps, img_size) #定义视频写入类
    12. pics = os.listdir(im_dir)
    13. for i in range(num):
    14. im_name = os.path.join(im_dir, str(i)+'.jpg')
    15. frame = cv2.imread(im_name)
    16. if type(frame) != None:
    17. videoWriter.write(frame) #写入帧
    18. videoWriter.release()
    19. print ('finish')

    显示窗口Windows

    可以自定义窗口显示图像的模式

    窗口函数

    [官方文档][OpenCV: High-level GUI]

    函数原型 void nameWindow(const string& winname,int flags = WINDOW_AUTOSIZE) ;第一个参数是窗口名字,第二个是显示模式,flag 默认值 是window_autosize

    1. WINDOW_AUTOSIZE 窗口大小自动适应图片大小,并且不可手动更改
    2. WINDOW_NORMAL 用户可以改变这个窗口大小
    3. WINDOW_OPENGL 窗口创建的时候会支持OpenGL ,且可以更改窗口大小

    定义窗口名称,imshow时可以指定让图像在该窗口显示

    1. namedWindow("input", WINDOW_AUTOSIZE);
    2. imshow("input", src);
    1. cv2.namedWindow("input", cv2.WINDOW_AUTOSIZE)
    2. cv2.imshow("input", src)

    一个窗口显示多张图片

    另一种方法是matplotlib

    OpenCV一个窗口只能显示一张图片。但可以通过组合多张图,实现多张图在一个窗口的视觉效果。

    Python numpy组合

    用np.hstack或者np.vstack组合矩阵 若是要让各个图片之间显示间隔,可以插入0或255的列做间隔

    1. #图1
    2. img = cv2.imread(r'C:\Users\muyi\Pictures\pic\489321.jpg')
    3. #图2
    4. img2 = cv2.imread(r'C:\Users\muyi\Pictures\pic\489323.jpg')
    5. #图集
    6. imgs = np.hstack([img,img2])
    7. #展示多个
    8. cv2.imshow("mutil_pic", imgs)
    9. #等待关闭
    10. cv2.waitKey(0)

    在这里插入图片描述

    OpenCV一个窗口显示多个图片的注意事项:

    读入的多个图片必须具有相同的尺寸、通道数。若是彩图、灰度图同时显示,会出现窗口黑屏现象

    matplotlib划分窗口显示多张图:

    利用 plt.subplot(m,n,k)划分窗口即可,类似matlab的绘图,而且各个窗口可以显示不同格式的图像

    参考网址:

    https://blog.csdn.net/qq_39987952/article/details/90734205

  • 相关阅读:
    给 Pod 添加 DNS 记录
    汽车制造业安全有效的设计图纸文件外发系统是什么样的?
    axios、vue-axios请求模块
    Vue中使用百度地图引发内存泄露的分析与解决方案
    钾含量是香蕉12倍!3种高钾食物,自己在家做,常吃有精神
    随笔感悟:Mysql悲观锁和乐观锁
    代码随想录第39天 | ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III
    MacBook安装使用腾讯柠檬清理Lemon
    UE4游戏传奇4的SDK的部分数据之-移动状态
    第四章 教学实施
  • 原文地址:https://blog.csdn.net/zaf0516/article/details/122418620