OpenCV4 快速入门笔记

本文是OpenCV4快速入门的学习笔记摘要,涵盖Mat类基础、图像存储容器、特征点检测与匹配技术,包括SIFT、SURF、ORB特性及RANSAC优化方法,还有FLANN匹配策略,适合初学者快速掌握OpenCV核心概念。

OpenCV4 快速入门 (学习笔记 全)

Excerpt

《OpenCV4 快速入门》学习笔记


第1章 基础知识

1.1 基础结构介绍

作者博客https://blog.youkuaiyun.com/shuiyixin?type=blog

https://blog.youkuaiyun.com/shuiyixin/article/details/106046827

1.1.1 Mat类

https://blog.youkuaiyun.com/shuiyixin/article/details/106014341

Mat src, src_roi;
src = imread("./image/cat.jpg");

if (!src.data)
{
cout << "ERROR : could not load image.\n";
waitKey(0);
return -1;
}
 
imshow("input image", src);
src_roi = Mat(src, Rect(100, 100, 300, 200));//很常用的构建mat类型的方法
imshow("roi image", src_roi);

image-20220307152613133

1.1.2 Rect_结构

https://blog.youkuaiyun.com/shuiyixin/article/details/106085233

1.1.3 Scalar_结构

Scalar其实是一个从Vec派生得到的四元向量的模板类

    Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);

    Scalar(0, 0, 255); // 红色

    Scalar(0, 255, 0); // 绿色

    Scalar(255, 0, 0); // 蓝色

一般情况,我们只赋值前三个,第一个参数表示蓝色,第二个参数表示绿色,第三个参数表示红色,也就是BGR

https://blog.youkuaiyun.com/shuiyixin/article/details/106111460

1.1.4 RNG类

https://blog.youkuaiyun.com/weixin_43588171/article/details/104810295

1.2 基础函数

1.2.1 saturate_cast()

https://blog.youkuaiyun.com/qq_27278957/article/details/88648737

opencv中,saturate_cast的作用是防止数据溢出,作出保护。原理可理解为如下代码功能:

if(data < 0)
     data = 0;
else if(data > 255)
 data = 255;

1.2.2 reshape()

C++: Mat Mat::reshape(int cn, int rows=0) const

cn: 表示通道数(channels), 如果设为0,则表示保持通道数不变,否则则变为设置的通道数。

rows: 表示矩阵行数。 如果设为0,则表示保持原有的行数不变,否则则变为设置的行数。

1.2.3 Create()

函数原型:
        inline void Mat::create(int _rows, int _cols, int _type)
inline void Mat::create(Size _sz, int _type)
void Mat::create(int ndims, const int* sizes, int type)
函数功能:
        1)如果需要,分配新的数组数据
2)创建一个图像矩阵的矩阵体

1.3 程序

1.3.1 退出程序

char key = (char)waitKey();
if (key == 27 || key == 'q' || key == 'Q')
{
break;
}

第2章 数据载入、显示和保存

2.1 图像存储容器

数字图像在计算机中是以矩阵形式存储的,矩阵中的每一个元素都描述一定的图像信息,如亮度、颜色等

OpenCV利用Mat 类用于存储数据,利用自动内存管理技术很好地解决了内存自动释放的问题,当变量不再需要时, 立即释放内存.

image-20220402172122725

image-20220306111549663

CV_8U是 unsign 的8位/像素-即一个像素的值在0-255区间,这是大多数图像和视频格式的正常范围。

CV_32F是 float -像素是在0-1.0之间的任意值,这对于一些数据集的计算很有用,但是它必须通过将每个像素乘以255来转换成8位来保存或显示。

2.1.2 Mat类构造与赋值

1.Mat类的构造
cv::Mat::Mat(3,3,CV_8S);//对应代码清单2-5
cv::Mat::Mat(cv::Size(3, 3), CV_8UC1);//对应代码清单2-6

cv::Mat A = cv::Mat_<double>(3, 3);
cv::Mat f = cv::Mat::Mat(3,3,CV_8S);
cv::Mat g = cv::Mat::Mat(cv::Size(3, 3), CV_8UC1);
cv::Mat h(cv::Size(3, 3), CV_8UC1);
cv::Mat i = g;
cv::Mat j(g);//修改j会修改g的值
cv::Mat k(g, cv::Range(2, 5), cv::Range::all());//range表示下标索引,,从0开始,0是第一行,这个意思是表示为下标为2的第3行到下标为5的第6行前(不包括下标为5的第6行)
cv::Mat k(g, cv::Range(2, 5), cv::Range::all());//修改k会修改g的值
cv::Mat::Mat(g, cv::Range(2, 5), cv::Range::all());//修改xx会修改g的值
2.Mat类的赋值
cv::Mat g = cv::Mat::Mat(cv::Size(3, 3), CV_8UC1, cv::Scalar(255));
cv::Mat g = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);

cv::Mat c = cv::Mat_<int>(3, 3);
for (int i = 0; i < c.rows; i++)
{
for (int j = 0; j < c.cols; j++)
{
c.at<int>(i, j) = i + j;//与cv::Mat_<int>(3, 3);的int必须一致
}
}

//生成单位矩阵
cv::Mat a(cv::Mat::eye(3, 3, CV_8UC1));
//生成特定对角矩阵
cv::Mat b = (cv::Mat_<int>(1, 3) << 1, 2, 3);
cv::Mat c = cv::Mat::diag(b);
//生成1矩阵、零矩阵
cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);
cv::Mat e = cv::Mat::zeros(3, 3, CV_8UC3);

//矩阵运算
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat c = (cv::Mat_<double>(3, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2, 2, 2, 2);
cv::Mat d = (cv::Mat_<double>(3, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2, 2, 2, 2);
cv::Mat e, f, g, h, i;
e = a + b;//四则运算超了最大值按照最大值算
f = c - d;
g = 2 * a;
h = d / 2.0;
i = a - 1;

cv::Mat j, m;/*Mat 类中的数据类型必须是CV_32FC I 、CV_64FCI 、CV _32FC2 , CV 64FC2
这4 种中的一种,也就是对于一个二维的Mat 类矩阵, 其保存的数据类型必、须是float类型或者
double 类型*/
double k;
j = c * d;
k = a.dot(b);//相当于向量运算中的点成,也叫向量的内积、数量积
 //Mat矩阵的dot方法扩展了一维向量的点乘操作,把整个Mat矩阵扩展成一个行(列)向量,之后执行向量的点乘运算,仍然要求参与dot运算的两个Mat矩阵的行列数完全一致。
 //dot操作不对参与运算的矩阵A、B的数据类型做要求
 //若参与dot运算的两个Mat矩阵是多通道的,则计算结果是所有通道单独计算各自.dot之后,再累计的和,结果仍是一个double类型数据。
m = a.mul(b);//对应位相乘,放在原位
 // 对数据类型没有要求,但A、B必须一致
 // 产生的数据类型默认与A、B一致

/*在图像处理领域, 常用的数据类型是CV_8U ,其范围是0-255 ,当两个比较大的整数相乘时,
就会产生结果溢出的现象, 输出结果为255 ,因此,在使用mu1 0方法时, 需要防止出现数据溢出
的问题.*/

image-20220305105838052

image-20220305110324496

2.1.4 Mat元素的读取

image-20220305122015202

cv::Mat b(3, 4, CV_8UC3, cv::Scalar(0, 0, 1));
cv::Vec3b vc3 = b.at<cv::Vec3b>(0, 0);//cv::Vec3x对应三通道数据,cv::Vec2x对应二通道数据,cv::Vec4x对应四通道数据
int first = (int)vc3.val[0];
int second = (int)vc3.val[1];
int third = (int)vc3.val[2];
std::cout << first << " " << second << " " << third << "" << std::endl;

for (int i = 0; i < b.rows; i++)
{
uchar* ptr = b.ptr<uchar>(i);
for (int j = 0; j < b.cols*b.channels(); j++)
{
std::cout << (int)ptr[j] << " ";
}
std::cout << std::endl;
}
//当读取第2行数据中第3个数据时, 可以用b.ptr<uchar>(1)[2]的形式来直接访问.




cv::MatIterator_<cv::Vec3b> it = b.begin<cv::Vec3b>();//mat是什么类型,就要用什么类型的iterator接受指针,不然会报错
cv::MatIterator_<cv::Vec3b> it_end = b.end<cv::Vec3b>();
for (int i = 0; it != it_end; it++)
{
std::cout << *it << " ";//可以直接输出vec3b类型的值,,,*it
if ((++i % b.cols) == 0)
{
std::cout << std::endl;
}
}

std::cout << (int)(*(b.data + b.step[0] * 1 + b.step[1] * 1 + 1)) << std::endl;//https://blog.youkuaiyun.com/baoxiao7872/article/details/80210021讲解了data和step的用法
image.at<uchar>(i, j)

其中有一个要注意的地方是i对应的是点的y坐标,j对应的是点的x坐标,而不是我们习惯的(x,y)

image-20220306104303657

image-20220306113754298

2.2 图像的读取与显示

cv::Mat cv::imread(const string & filename, int flags = IMREAD_COLOR)//这些标志参数在功能不冲突的前提下可以同时声明多个,不同参数之间用" 1 " 隔开。
    //在默认情况下,读取图像的像章数目必须小于2^30,可以通过修改革统变量中的OPENCV 10 MAX IMAGE: PIXELS 参数调整能够读取的最大像章数目.
        //empty()函数是否为真来判断是否成功读取图像,如果读取图像失败,那么data 属性返回值为0. emptyO函数返回值为1
    
   void cv::namedWindow(const String & winname, int flags = WINDOW_AUTOSIZE)//namedWindow()窗口函数属性标志餐数
        
    void cv::imshow(const string & winname, InputArray mat)//如果后面程序执行完直接退出,那么显;示的图像有可能闪一下就消失,因此在需要显示图像的程序中,往往会在imshow()函数后跟有cv::waitKey()函数,用于将程序暂停一段时间.cv::waitKey()函数是以毫秒计的等待时长,如果参数默认或者为"0" ,那么表示等待用户按键结束该函数.

image-20220306154802090image-20220306154912279

2.3 视频加载与摄像头调用

//这是打开视频
cv::VideoCapture::VideoCapture();
cv::VideoCapture::VideoCapture(const String & filename, int apiPreference = CAP_ANY);
//需要通过自Opened()函数进行判断.如果读取成功, 则返回值为true; 如果读取失败,则返回值为false .

//这事调用摄像头
cv::VideoCapture::VideoCapture(int index, int apiPreference = CAP_ANY);//摄像头号从0开始

image-20220306162300019

2.4 数据保存

bool cv::imwrite(const String & filename, InputArray img, const std::vector<int>& params = std::vectro<int>());
    
    cv::VideoWriter::VideoWriter(const String & filename, int fourcc, double fps, Size frameSize, bool isColor=true);

cv::FileStroage::FileStorage();//需要用open()函数操作
cv::FileStroage::FileStorage(const String & filename, int flags, const String & encoding  = String());
cv::FielStorage::open(const String & filename, int flags, const String & encoding  = String());
cv::FielStorage::write(const String & name, int val);//存在多个重载,可以写多种类型的变量值
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
system("color F0");  //修改运行程序背景和文字颜色
//string fileName = "datas.xml";  //文件的名称
string fileName = "datas.yaml";  //文件的名称
//以写入的模式打开文件
cv::FileStorage fwrite(fileName, cv::FileStorage::WRITE);

//存入矩阵Mat类型的数据
Mat mat = Mat::eye(3, 3, CV_8U);
fwrite.write("mat", mat);  //使用write()函数写入数据
//存入浮点型数据,节点名称为x
float x = 100;
fwrite << "x" << x;
//存入字符串型数据,节点名称为str
String str = "Learn OpenCV 4";
fwrite << "str" << str;
//存入数组,节点名称为number_array
fwrite << "number_array" << "[" <<4<<5<<6<< "]";
//存入多node节点数据,主名称为multi_nodes
fwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year"
<< 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";

//关闭文件
fwrite.release();

//以读取的模式打开文件
cv::FileStorage fread(fileName, cv::FileStorage::READ);
//判断是否成功打开文件
if (!fread.isOpened())
{
cout << "打开文件失败,请确认文件名称是否正确!" << endl;
return -1;
}

//读取文件中的数据
float xRead;
fread["x"] >> xRead;  //读取浮点型数据
cout << "x=" << xRead << endl;

//读取字符串数据
string strRead;
fread["str"] >> strRead;
cout << "str=" << strRead << endl;

//读取含多个数据的number_array节点
FileNode fileNode = fread["number_array"];
cout << "number_array=[";
//循环遍历每个数据
for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++)
{
float a;
*i >> a;
cout << a<<" ";
}
cout << "]" << endl;

//读取Mat类型数据
Mat matRead;
fread["mat="] >> matRead;
cout << "mat=" << mat << endl;

//读取含有多个子节点的节点数据,不使用FileNode和迭代器进行读取
FileNode fileNode1 = fread["multi_nodes"];
int month = (int)fileNode1["month"];
int day = (int)fileNode1["day"];
int year = (int)fileNode1["year"];
cout << "multi_nodes:" << endl 
<< "  month=" << month << "  day=" << day << "  year=" << year;
cout << "  time=[";
for (int i = 0; i < 4; i++)
{
int a = (int)fileNode1["time"][i];
cout << a << " ";
}
cout << "]" << endl;

cout << "  time=[";
for (FileNodeIterator i = fileNode1["time"].begin(); i != fileNode1["time"].end(); i++)
{
float a;
*i >> a;
cout << a << " ";
}
cout << "]" << endl;//两种读取方法都行,一种使用迭代器,一种使用地址。

system("pause");
//关闭文件
fread.release();
return 0;
}

第3章 图像基本操作

3.1 图像颜色空间

3.1.1 颜色模型与转换——converTo()

RGB模型再OpenCV中是BGR的顺序。

RGB模型增加透明度就是RGBA模型

HSV、YUV、Lab、GRAY

void cv::Mat::convertTo(OutputArray m, int rtype, double alpha =1, double beta = 0);//rtype转换图像的数据类型,alpha转换过程中的缩放因子,beta转换过程中的偏置因子

alpha和beta两个餐数用于声明两个数据类型间的转换关系,形式如m(x,y)=saturate_cast(α(*this)(x,y)+β),该转换方式就是将原有数据进行线性转换

3.1.2 多通道分离与合并——split(), merge()

//多通道分离
void cv::split(const Mat & src, Mat * mvbegin);//需要定义mvbegin的长度
void cv::split(InputArray m, OutputArrayofArrays mv);//mv是vector形式,不需要提前定义数组长度

//多通道融合
void cv::merge(const Mat *mv, size_t count, OutputArray dst);//mv需要合并的图像数组,每个图像必须拥有相同的尺寸和数据类型.count输入的图像数组长度
void cv::merge(InputArrayofArrays mv, OutputArray dst);//mv需要合并的图像向量vector,每个图像必须拥有相同的尺寸和数据类型

//多通道融合可以超过4个通道,image watch上每个像素点内不显示4之后的通道值,但是上面会显示每个通道的值

下面这个图是把1个lena的img拆分了三个通道,分别放在一个mat[6]内,使用merge合并出来的6通道图片

image-20220307113229489

3.2 图像像素操作处理

3.2.1 图像像素统计

1.寻找像素最大值与最小值——minMaxLoc()
void cv::minMaxLoc(InputArray src, 
                   double * minVal, 
                   double * maxVal = 0, 
                   Point * minLoc = 0, 
                   Point * maxLoc =0, 
                   InputArray mask = noArray());//src输入的单通道矩阵

数据类型Point用于表示图像的像素坐标,

图像的像素坐标轴以左上角为坐标原点, 水平方向为x 轴, 垂直方向为y 轴

Point(x.y)对应于图像的行和列表示为Point (列数,行数)

在OpenCV 中对于二维坐标和三维坐标都设置了多种数据类型,针对二维坐标数据类型, 定义了整型坐标cv::Point2i (或者cv: :Point ) 、double 型坐标cv : :Point2d、浮点型坐标cv: :Point2f. 对于三维坐标,同样定义了上述的坐标数据类型,只需要将其中的数字" 2" 变成"3 气对于坐标中x、y、z 轴的具体数据, 可以通过变量的x、y、z 属性进行访问, 例如Point.x可以读取坐标的x 轴数据.

寻找多通道矩阵需要使用cv::Mat::reshape()将多通道变成单通道

cv::Mat MatA_reshape = MatA.reshape(int cn, int rows = 0)//cn指通道数,rows指转换后矩阵的行数,默认0与转换前相同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SPdtTvkr-1666660001256)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE3-7.png)]

2.计算图像的平均值和标准差——mean, meanStdDev

图像的平均值表示图像整体的亮暗程度, 图像的平均值越大,则图像整体越亮。

标准差表示图像中明暗变化的对比程度, 标准差越大, 表示图像中明暗变化越明显。

//求平均值
cv::Scalar cv::mean(InputArray src, InputArray mask = noArray())//用scalar结构接收
   //求平均值和标准差
    void cv::meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, InputArray mask = noArray())//用mat结构接收

3.2.2 图像间的像素操作

1.两幅图像的比较运算——max(), min()
void cv::max(InputArray src1,InputArray src2,OutputArray dst)
void cv::min(InputArray src1,InputArray src2,OutputArray dst)

这种比较运算主要用在对矩阵类型数据的处理上. 与掩模图像进行比较运算可以实现抠图或者选择通道的效果.

//对两张彩色图像进行比较运算
Mat img0 = imread("E:/graduate/learn/OpenCV/《OpenCV 4快速入门》数据集/data/lena.png");
Mat img1 = imread("E:/graduate/learn/OpenCV/《OpenCV 4快速入门》数据集/data/noobcv.jpg");

if (img0.empty() || img1.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat comMin, comMax;
max(img0, img1, comMax);
min(img0, img1, comMin);
imshow("comMin", comMin);
imshow("comMax", comMax);

//与掩模进行比较运算
Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);//生成一个512*512的0矩阵
Rect rect(100, 100, 300, 300);//创建一个坐标点(100,100)开始的300*300的矩形
src1(rect) = Scalar(255, 255, 255);  //生成一个低通300*300的掩模,,,,把rect矩形叠加进src1矩阵中,rect中的值用scalar修改为255,255,255
Mat comsrc1, comsrc2;
min(img0, src1, comsrc1);
imshow("comsrc1", comsrc1);

Mat src2 = Mat(512, 512, CV_8UC3, Scalar(0, 0, 255));  //生成一个显示红色通道的低通掩模
min(img0, src2, comsrc2);
imshow("comsrc2", comsrc2);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UUdc5njU-1666660001256)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE3-11.png)]image-20220307154002824

用黑框图片作为一个低通掩膜和lena图片求min(),就可以把外面的值剪掉。红色掩膜同理

2.两幅图片的逻辑运算——bitwise_()
//逻辑与
void cv::bitwise_and(InputArray src1, Input Array src2, OutputArray dst, InputArray mask = noArray())
    //逻辑与
    void cv::bitwise_or(InputArray src1, Input Array src2, OutputArray dst, InputArray mask = noArray())
    //逻辑异或
    void cv::bitwise_xor(InputArray src1, Input Array src2, OutputArray dst, InputArray mask = noArray())
    //逻辑非
    void cv::bitwise_not(InputArray src, OutputArray dst, InputArray mask = noArray())

image-20220307155741961

image-20220307155721970

3.2.3 图像二值化——threshold()

double cv::threshold(InputArray src, 
                         OutputArray dst, 
                         double thresh, 
                         double maxval, 
                         int type
                        )
        //thresh二值化的阈值
        //maxval二值化过程的最大值,只有THRESH_BINARY和THRESH_BINARY_INV两种二值化方法才使用,
        //type二值化方法

image-20220308093000075

THRESH_OTSU和THRESH_TRIANGLE这两种标志是获取阈值的方法,并不是阈值比较方法的标志, 这两个标志可以与前面5 种标志一起使用, 例如" THRESH_BINARY | THRESH_OTSU" . 前面5 种标志在调用函数时都需要人为地设置阈值,如果对图像不了解,设置的阈值不合理, 就会对处理后的效果造成严重的影响.这两个标志分别表示利用大津法(OTSU)和三角形法(TRlANGLE)结合国像灰度值分布特性获取二值化的阈值,并将阈值以函数返回值的形式给出.因此,如果该函数最后一个参数设置了这两个标志中的任何一个,那么该函数第三个参数thresh将由系统自动给出,但是在调用函数时仍然不能默认,只是程序不会使用这个数值.需要注意的是,到目前为止. OpenCV 4 中针对这两个标志只支持输入CV_8UC1类型的图像.

大津法——最大类间方差法:https://blog.youkuaiyun.com/yxswhy/article/details/77838622?locationNum=10&fps=1

三角形法——适用于单峰直方图:https://blog.youkuaiyun.com/qq_45769063/article/details/107102117

由threshold()函数全局只使用一个阈值,在实际情况中, 由于光照不均匀以及阴影的存在, 全局只有一个阈值会使得在阴影处的白色区域也会被函数二值化成黑色, 因此adaptiveThreshold() 函数提供了两种局部自适应阈值的二值化方法,基本的思路就是以目标像素点为中心选择一个块,然后对块区域里面的像素点进行高斯或者均值计算,将得到的平均值或者高斯值作为目标像素点的阈值,以此来对目标像素格进行二值化。对图像每一个像素格进行如此操作就完成了对整个图像的二值化处理。

void cv::adaptiveThreshold(InputArray src, 
                           OutputArray dst, 
                           double maxValue,
                           int adaptiveMehtod,
                           int threshType,
                           int blockSize,
                           double C
                          )
        //maxValue二值化的最大值
        //adaptiveMethod自适应确定阈值的方法,均值法ADAPTIVE_THRESH_MEAN_C和高斯法ADAPTIVE_GAUSSIAN_MEAN_C两种.
        //thresholdType选择图像二值化的方法,只能是THRESH_BINARY和THRESH_BINARY_INV两种二值化方法
    //blockSize自适应确定阈值的像素邻域大小,一般为3,5,7的奇数
    //从平均值或者加权平均值中减去的常数

3.2.4 查找表——LUT()

查找表,Look-Up-Table

void cv::LUT(InputArray src,//只能输入CV_8U类型
 InputArray lut,//256 个像素灰度值的查找表,单通道或者与src 通道数相同.
 OutputArray dst//输出图像矩阵,尺寸与src 相同,数据类型与lut 相同.
 )

函数输出图像的数据类型与LUT的数据类型保持一致,不与原图像保持一致。

如果第二个参数是单通道,则输入变量中的每个通道都按照一个LUT进行映射,如果第二个参数是多通道,则输入变量中的第i个通道按照第二个参数的第i个通道LUT进行映射.

3.4 图像变换

3.3.1 图像连接——cv::vconcat, cv::hconcat

void cv::vconcat(const Mat * src, size_t nsrc, OutputArray dst)//src是Mat矩阵类型的数组,nsrc数组中Mat类型数据的数目
    void cv::vconcat(InputArray src1, InputArray src2, OutputArray dst)

vconcat是纵向连接**,两者需要具有相同的宽度、数据类型及通道数**

void cv::hconcat(const Mat * src, size_t nsrc, OutputArray dst)//src是Mat矩阵类型的数组,nsrc数组中Mat类型数据的数目
    void cv::hconcat(InputArray src1, InputArray src2, OutputArray dst)

hconcat是纵向连接**,两者需要具有相同的高度、数据类型及通道数**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jM9akR32-1671457381121)(https://gitee.com/lyz-npu/typora-image/raw/master/img/%E5%9B%BE3-19.png)]

3.3.2 图像尺寸变换——resize()

void cv::resize(InputArray src, 
                OutputArray dst, 
                Size dsize, //输出图像的尺寸
                double fx = 0,//水平轴的比例因子 
                double fy = 0,//垂直轴的比例因子
                int interpolation = INTER_LINEAR//插值方法的标志
               )

该函数的dsize 和fx ( fy ) 同时可以调整输出图像的参数,因此两类参数在实际使用时只需要使用一类,当根据两个参数计算出来的输出图像尺寸不一致时,以dsize 设置的图像尺寸为准.

dsize=Size(round(fx*src.cols), round(fy*src.rows))

缩小图像,通常使用INTER_AREA标志会有较好的效果

放大图像,采用INTER_CUBIC和INTER_LINEAR标志通常会有比较好的效果

标志参数 简记 作用
INTER_NEAREAST 0 最近邻插值法
INTER_LINEAR 1 双线性插值法
INTER_CUBIC 2 双三次插值
INTER_AREA 3 使用像素区域关系重新采样,首选用于图像缩小,图像放大时效果与INTER_NEAREST相似
INTER_LANCZOS4 4 Lanczos插值法
INTER_LINEAR_EXACT 5 位精确双线性插值法
INTER_MAX 7 用掩码进行插值

最邻近

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值