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

8 篇文章 18 订阅

四小时学习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中自带的图像和视频处理,欢迎关注,如果我的博客对你有帮助请点个赞吧

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

visual_eagle

欢迎交流学习

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值