四小时学习opencv+qt系列(第四天)

    技术2022-07-11  71

    四小时学习opencv+qt系列(第四天)

    一、OpenCV中关于Mat类

    首先Mat类是一个n维数组,计算机视觉中的图像就是像素矩阵(二维数组),宽度就是列数,高度就是行数。

    在灰度图中是单通道,一个像素点可以用一个数字表示,min=0(黑色),max=255(白色)。

    在标准的RGB彩色图像中,一个像素有三个不同的元素,所以对应三个通道,分别是红、蓝、绿三个通道。

    1.构造函数

    //创建一个10*10的矩阵,每个元素有一个单通道8位无符号的整数或者字节 Mat matrix(10,10,CV_8UC(1)); //创建一个10*10的矩阵,每个元素有一个单通道8位无符号的整数或者字节,用0初始化所有的元素 Mat matrix(10,10,CV_8UC(1),Scalar(0));

    第一个参数是行数,第二个参数是列数,解析第三个参数

    CV_<bits><type>C(<channels>) //<bits> 8,16用于无符号和有符号的整数;32用于无符号和有符号的整数及浮点数;64用于无符号和有符号的浮点数; //<type> U:用于无符号整数;S:用于有符号整数;F:用于有符号的浮点数 //<channels> 通道数,理一般不会大于四

    创建一个立方体,边长为10,类型为双精度(64)的双通道元素,用1.0初始化所有值。

    int sizes[]={10,10,10}; Mat cube(3,sizes,CV_64FC(2),Scalar:all(1.0));

    通过Mat类的create方法来更改大小和类型

    Mat matrix; //... matrix.create(10,10,CV_8UC(1)); //之前的Mat类的内容会被安全清除

    可以创建一个Mat类,他是另一个Mat类的一部分,这个称为感兴趣区域(ROI),需要访问图像的一部分是就把这部分作为一个独立的图像来处理。

    创建一个图像中以(25,25)为起点,创建一个包含50*50像素正方形ROI Mat类:

    Mat roi(image,Rect(25,25,50,50));

    上面这种方法创建的感兴趣区域如果改变会影响原始图像,所以可以借助clone函数:

    Mat imageCopy = image.clone();

    接下来是一个对行和列的操作实列,新建工程,.pro文件中添加opencv路径,在mainwindow.cpp中:

    #include "mainwindow.h" #include "ui_mainwindow.h" #include<opencv2/opencv.hpp> using namespace cv; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); Mat image = imread("../input.jpg"); Mat imageCopy = image.clone(); Mat r=imageCopy.row(0);//选中图片的第一行存到r中 Mat c=imageCopy.col(0);//选中图片的第一列存到c中 Mat centralRows=imageCopy.rowRange(imageCopy.rows/2-10,imageCopy.rows/2+10);//选择中心行宽20的区域 Mat centralColumns=imageCopy.colRange(imageCopy.cols/2-10,imageCopy.cols/2+10);//选择中心列宽20的区 centralRows=Scalar(0);//像素值初始化为0 centralColumns=Scalar(255);//像素值初始化值为255 imshow("input",image);//初始图像 imshow("output",imageCopy);//处理后的图像 } MainWindow::~MainWindow() { delete ui; }

    效果:

    使用ROI的方式获取父图像的大小及父图像内ROI左上角的位置,将mainwindow.cpp修改如下:

    #include "mainwindow.h" #include "ui_mainwindow.h" #include<QDebug> #include<opencv2/opencv.hpp> using namespace cv; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); Mat image = imread("../input.jpg"); Mat imageCopy = image.clone(); // Mat r=imageCopy.row(0);//选中图片的第一行存到r中 // Mat c=imageCopy.col(0);//选中图片的第一列存到c中 // Mat centralRows=imageCopy.rowRange(imageCopy.rows/2-10,imageCopy.rows/2+10); // Mat centralColumns=imageCopy.colRange(imageCopy.cols/2-10,imageCopy.cols/2+10); // centralRows=Scalar(0); // centralColumns=Scalar(255); //使用ROI的方式获取父图像的大小及父图像内ROI左上角的位置 Mat centralRows =imageCopy.rowRange(imageCopy.rows/2-10,imageCopy.rows/2+10); Size parentSize; Point offset; centralRows.locateROI(parentSize,offset); int parentWidth=parentSize.width; int parentHeight=parentSize.height; int x=offset.x; int y=offset.y; centralRows=Scalar(0); qDebug()<<parentWidth<<parentHeight<<x<<y; imshow("input",image); imshow("output",imageCopy); } MainWindow::~MainWindow() { delete ui; }

    效果图:由图可知图像高720,宽1280,ROI区域左上角为(0,350)

    //Mat类的属性 depth:包含Mat类的深度,CV_8U,CV_8S,CV_16U,CV_16S,CV_32S,CV_32F,CV_64F channels:通道数,一般为3 type:Mat的类型 rows:行数(高度) cols:列数(宽度) elemSize:用来获取Mat类中每个元素的大小(单位:字节) elemSize1:用来获取Mat类中每个元素的大小(单位:字节),不考虑通道数 empty:if Mat无元素,return true;else return false; isContinuous:用来检查Mat中元素是否以连续的方式存储。 isSubmatrix:如果Mat类是另一个Mat类的子矩阵则return true total:return Mat类中元素的总数 step:return 元素数 at:访问Mat类中的元素 image.at<Vec3b>(X,Y)=C; Vec类型举例:typedef Vec<uchar,2> Vec2b; typedef Vec<short,2> Vec2s; typedef Vec<ushort,2> Vec2w; typedef Vec<int,2> Vec2i; typedef Vec<float,2> Vec2f; typedef Vec<double,2> Vec2d; begin和end:可以使用类似C++STL的迭代器来检索和访问Mat类的元素 forEach:可以用来对Mat类的所有元素并行运行一个函数

    如果将图像中每个像素的值除以5,使图像变暗就有四种方法:

    //第一种,传统对每个像素操作 for(int i=0;i<image.rows;i++) { for(int j=0;j<image.cols;j++) { imageCopy.at<Vec3b>(i,j)/=5; } } //第二种类似STL的迭代器 MatIterator_<Vec3b> it_begin=imageCopy.begin<Vec3b>(); MatIterator_<Vec3b> it_end=imageCopy.end<Vec3b>(); for (;it_begin!=it_end;it_begin++) { *it_begin/=5; } //第三种forEach函数 imageCopy.forEach<Vec3b>([](Vec3b &p,const int *) { p/=5; }); //第四种 imageCopy=image*0.2; //imageCopy=image/5;

    效果图:

    adjustROI:改变子矩阵(ROI)大小 clone:创建一个图像的副本 convertTo:用于改变Mat类的数据类型,也可以有选择地缩放图像 copyTo:将图像全部或者部分复制到另一个Mat。 ptr:可以用来在Mat中获取指针和访问图像数据。 release:在Mat的析构中调用,作用是Mat类所需的内存清理任务 reserve:用来为一定数量的指定行保留内存空间 reserveBuffer:用来为一定数量的字节保留内存空间 reshape:需要改变通道数以获得矩阵数据的一个不同的表示时很有用 resize:改变Mat类中的行数 setTo:可以用于将矩阵中的全部或是部分值设置为指定的值 cross:计算两个三元素矩阵的叉积 diag:提取矩阵的对角线 dot:计算两个矩阵的点积 eye:静态函数,用于创建单位矩阵 inv:创建逆矩阵 mul:计算两个矩阵的元素乘法或者除法 ones:静态函数,可以创建一个所有元素值都为1的矩阵 t:可以用来得到Mat类的转置矩阵的函数,等价于对一个图像进行镜像和90°旋转

    2.一些常见的类

    Mat_<_Tp>类是Mat类的子类有相同的成员,优势在于如果编译时矩阵的类型已知就很有用,还具备了比Mat类的at更好的访问方法。

    Mat_<Vec3b> imageCopy(image); imageCopy(10,10)=Vec3b(0,0,0);

    Matx<_Tp,m,n>仅用于编译时已知的类型、宽和高的最小矩阵的情况。

    UMat类在opencv3.0以后才能用,UMat类是统一Mat类,优点在于运行平台上是否存在OpenCL层,OpenCL就是一个允许CPU、GPU及系统上其他计算机资源协同工作的框架,甚至可以实现并行。只要系统存在OpenCL层那么将UMat传递给Opencv时将调用底层的Opencl层,从而提高计算机视觉程序的性能,否则的话只转化为Mat类,调用CPU实现。

    //UMat和Mat可以互相转换 Mat::getUMat UMat::getMat //上面两个函数都需要一个访问标志,如下 ACCESS_READ ACCESS_WRITE ACCESS_RW ACCESS_FAST

    3.利用opencv读取图像

    //以灰度图像读入,忽略EXIF的旋转标志 Mat image=imread("../input.jpg",IMREAD_GRAYSCALE | IMREAD_IGNORE_ORIENTATION); imshow("input",image);//显示图像

    效果图:

    读取多页图像文件.tif/.tiff,imreadmulti函数

    std::vector<Mat> multiplePages; bool success=imreadmulti("../1.tif",multiplePages,IMREAD_COLOR);

    4.opencv写入图像,下面的操作进行了写到JPG文件并设置渐进模式和相对较低的质量

    Mat image = imread("../input.jpg"); std::vector<int> params; params.push_back(IMWRITE_JPEG_QUALITY); params.push_back(20); params.push_back(IMWRITE_JPEG_PROGRESSIVE); params.push_back(1); imwrite("../copy_1.jpg",image,params);

    5.opencv中视频的读写

    视频的读取本质就是抓取每一张图片

    VideoCapture video; video.open(0); if(video.isOpened()) { Mat frame; while(true) { if(video.read(frame)) { frame*=0.5;//简单的处理过程 } else { break; } } } video.release();

    读取视频的帧数:

    double frameCount=video.get(CAP_PROP_FRAME_COUNT);//读取视频的帧数

    抓取第100帧

    video.set(CAP_PROP_POS_FRAMES,100);//抓取第100帧

    保存视频

    VideoWriter video; video.open("../output.avi",CV_FOURCC('M','P', 'G','4'),30.0,Size(640,480),true); if(video.isOpened()) { while(framesRemain()) { video.write(getFrame()); } } video.release(); //opencv4中把CV_FOURCC('M','P', 'G','4')改为CAP_ANY,CAP_OPENCV_MJPEG //framesRemain()和getFrame()是虚函数

    前几天因为有考试,一个星期没有更新,,,,,,明天继续学习关于Qt中自带的图像和视频处理,欢迎关注,如果我的博客对你有帮助请点个赞吧

    Processed: 0.010, SQL: 9