四小时学习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_C()
// 8,16用于无符号和有符号的整数;32用于无符号和有符号的整数及浮点数;64用于无符号和有符号的浮点数;
// U:用于无符号整数;S:用于有符号整数;F:用于有符号的浮点数
// 通道数,理一般不会大于四
创建一个立方体,边长为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
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
#include
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()<
效果图:由图可知图像高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(X,Y)=C;
Vec类型举例:typedef Vec Vec2b;
typedef Vec Vec2s;
typedef Vec Vec2w;
typedef Vec Vec2i;
typedef Vec Vec2f;
typedef Vec Vec2d;
begin和end:可以使用类似C++STL的迭代器来检索和访问Mat类的元素
forEach:可以用来对Mat类的所有元素并行运行一个函数
如果将图像中每个像素的值除以5,使图像变暗就有四种方法:
//第一种,传统对每个像素操作
for(int i=0;i(i,j)/=5;
}
}
//第二种类似STL的迭代器
MatIterator_ it_begin=imageCopy.begin();
MatIterator_ it_end=imageCopy.end();
for (;it_begin!=it_end;it_begin++)
{
*it_begin/=5;
}
//第三种forEach函数
imageCopy.forEach([](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_ 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 multiplePages;
bool success=imreadmulti("../1.tif",multiplePages,IMREAD_COLOR);
4.opencv写入图像,下面的操作进行了写到JPG文件并设置渐进模式和相对较低的质量
Mat image = imread("../input.jpg");
std::vector 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中自带的图像和视频处理,欢迎关注,如果我的博客对你有帮助请点个赞吧