OpenCV C++ 常用操作从入门到入土

写在前面

opencv基础操作必须要学会!!
常用例子必须学会!!

矩阵(Mat)相关

注意矩阵的rows,cols和Rect!!!!

注意!!矩阵和Rect的区别:

//rows:行数,y,height
//cols:列数,x,width
Mat canvas_b =Mat::zeros(canvas_y, canvas_x, background.type());//行数,列数
cv::Rect(0, 0, background.cols, background.rows)//左上角x,左上角y;x长度,y长度
//如果x地方要填cols(列数),y要填rows(行数)

因为这个写错而造成的异常:
在这里插入图片描述
改正之后:
在这里插入图片描述

矩阵各种初始化

1->Mat() 构造函数:
Mat M(2,2, CV_8UC3, Scalar(0,0,255)); 
int sz[3] = {2,2,2}; 
Mat L(3,sz, CV_8UC(1), Scalar::all(0));
2->Create() function: 函数
M.create(4,4, CV_8UC(2));
3-> 初始化zeros(), ones(), :eyes()矩阵
Mat E = Mat::eye(4, 4, CV_64F);  
Mat O = Mat::ones(2, 2, CV_32F); 
Mat Z = Mat::zeros(3,3, CV_8UC1);
4->用逗号分隔的初始化函数:
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); 
Mat A, C;                                 // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A);                                 // 使用拷贝构造函数
C = A;                                    // 赋值运算符
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries
Mat F = A.clone();
Mat G;
A.copyTo(G);                       //使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵。

矩阵的type有哪些

初学opencv时不太理解图像的深度具体指什么,opencv加入mat后,用矩阵的概念描述图像,使很多概念清晰很多。首先了解什么是type,在Mat的构造函数里就出现过type

Mat(Size size, int type, const Scalar& s);

这里的type是个整数值,但平时我们用的是CV_8UC3等来写入type的值,如下,

Mat ds(src.size(),CV_16SC3);

CV_8UC3等均是是opencv内定义的宏,其与整数之间的映射如下图:

在这里插入图片描述

enum {
CV_8U=0,
CV_8S=1,
CV_16U=2,
CV_16S=3,
CV_32S=4,
CV_32F=5,
CV_64F=6 };

在这里插入图片描述

Vector的type有哪些

typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

例如 :

8U 类型的 RGB 彩色图像可以使用 Vec3b,
3 通道 float 类型的矩阵可以使用 Vec3f。

对于 Vec 对象,可以使用[]符号如操作数组般读写其元素,如:

Vec3b color; //用 color 变量描述一种 RGB 颜色
color[0]=255; //B 分量
color[1]=0; //G 分量
color[2]=0; //R 分量

小矩阵按照原样拷到大矩阵里面(大矩阵取sub矩阵)

在这里插入图片描述

background.copyTo(canvas_b(cv::Rect(0, 0, background.cols, background.rows)));
foreground.copyTo(canvas_f(cv::Rect(xB, yB, foreground.cols, foreground.rows)));

选取图像局部区域

Mat 类提供了多种方便的方法来选择图像的局部区域。使用这些方法时需要注意,这些方法并不进行内存的复制操作。如果将局部区域赋值给新的 Mat 对象,新对象与原始对象共用相同的数据区域,不新申请内存,因此这些方法的执行速度都比较快。

单行或单列选择

提取矩阵的一行或者一列可以使用函数row()或col()。
函数的声明如下:

Mat  Mat::row(int i)  const
Mat  Mat::col  (int j)  const

参数i和j分别是行标和列标。例如取出A矩阵的第i行可以使用如下代码:

Mat line=A.row(i);

例如取出 A 矩阵的第 i 行,将这一行的所有元素都乘以 2,然后赋值给第 j行,可以这样写:

A.row(j) = A.row(i)*2;

用Range选择多行或多列

Range 是 OpenCV 中新增的类,该类有两个关键变量 star 和 end。Range 对
象可以用来表示矩阵的多个连续的行或者多个连续的列。其表示的范围为从 start
到 end,包含 start,但不包含 end。Range 类的定义如下:

class Range
{
	public:
	    ...
	    int start, end;
};

Range 类还提供了一个静态方法 all(),这个方法的作用如同 Matlab 中的“:”,表所有的行或者所有的列。

//创建一个单位阵
    Mat A=Mat::eye(10,10,CV_32S);
//提取第1到3列(不包含3列)
    Mat B=A(Range::all(),Range(1,3));
//提取 B 的第 5 至 9 行(不包括 9)
    Mat C = B(Range(5, 9), Range::all());
//其实等价于 C = A(Range(5, 9), Range(1, 3))

感兴趣区域

从图像中提取感兴趣区域(Region of interest)有两种方法:

使用构造函数
//创建宽度为 320,高度为 240 的 3 通道图像
Mat img(Size(320,240),CV_8UC3);
//roi 是表示 img 中 Rect(10,10,100,100)区域的对象
Mat roi(img, Rect(10,10,100,100));
//使用构造函数
Mat roi4(img, Range(10,100),Range(10,100));
使用括号运算符
Mat roi2 = img(Rect(10,10,100,100));

当然也可以使用 Range 对象来定义感兴趣区域,
如下使用括号运算符:

 Mat roi3 = img(Range(10,100),Range(10,100));

取对角线元素

矩阵的对角线元素可以使用 Mat 类的 diag()函数获取,该函数的定义如下:

Mat Mat::diag(int d) const



    参数 d=0 时,表示取主对角线;
    当参数 d>0 是,表示取主对角线下方的次对角线,
    如 d=1 时,表示取主对角线下方,且紧贴主多角线的元素;
    当参数 d<0 时,表示取主对角线上方的次对角线。
    如同row()col()函数,diag()函数也不进行内存复制操作,其复杂度也是O(1)

取元素

(以下遍历均为赋值到另一Mat中,在内存连续的情况下用指针遍历,isContinuous可判断是否连续)
1.at成员函数遍历

Mat src = imread("face4.jpg",0);
Mat dst(src.size(),src.type());
for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			dst.at<uchar>(i, j) = src.at<uchar>(i, j);
		}
	}

类型应该给什么:
在这里插入图片描述

cout输出矩阵

cout << "C = " << endl << " " << C << endl << endl;

在这里插入图片描述

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

Matlab->OpenCV专题

Matlab的repMat 的对应实现(根据需求进行修改)

//rows:行数,y,height
//cols:列数,x,width
//coder:WhStudio @2021
Mat repMat(int rows,int cols, Mat BaseMat)
{
	Mat alphaMat = Mat::zeros(rows, cols, CV_32FC3);
	for (int i = 0; i < rows; i++)
	{
		BaseMat.copyTo(alphaMat(cv::Rect(0, i, BaseMat.cols, BaseMat.rows)));
	}
	return alphaMat;
}

Matlab的linspace 的对应实现

#include <vector>
using std::vector;

vector<double> generateRange(double a, double b, double c) {
    vector<double> array;
    while(a <= c) {
        array.push_back(a);
        a += b;         // could recode to better handle rounding errors
    }
    return array;
}

vector<double> linspace(double a, double b, int n) {
    vector<double> array;
    double step = (b-a) / (n-1);

    while(a <= b) {
        array.push_back(a);
        a += step;           // could recode to better handle rounding errors
    }
    return array;
}
Mat linspace(double &startP,double &Endp,int &interval)
{
    double spacing = interval-1;
    Mat y(spacing,1,CV_64FC1);
    for (int i = 0; i < y.rows; ++i)
    {
        y.at<double>(i) = startP + i*(Endp - startP)/spacing;
    }
    return y;
}

Matlab的meshgrid的对应实现

/***************************************
* opencv实现meshgrid函数
* opencv2.4.9 VS2010 Ultimate
* by 垚
****************************************/
 
#include <opencv2\core\core.hpp>
#include <vector>
 
/***************************************
* xgv -- 【输入】指定X输入范围
* ygv -- 【输入】指定Y输入范围
* X   -- 【输出】Mat
* Y   -- 【输出】Mat
****************************************/
void meshgrid(const cv::Range &xgv, const cv::Range &ygv, cv::Mat &X, cv::Mat &Y)
{
	std::vector<int> t_x, t_y;
	for(int i = xgv.start; i <= xgv.end; i++) t_x.push_back(i);
	for(int j = ygv.start; j <= ygv.end; j++) t_y.push_back(j);
 
	cv::repeat(cv::Mat(t_x).t(), t_y.size(), 1, X);
	cv::repeat(cv::Mat(t_y), 1, t_x.size(), Y);
}
#include "meshgrid.h"
#include <opencv2\core\core.hpp>
#include <iostream>
 
int main()
{
	cv::Mat X, Y;
	meshgrid(cv::Range(-5, 5), cv::Range(-5, 5), X, Y);
	std::cout<<"X = "<<X<<std::endl
		     <<"Y = "<<Y<<std::endl;
 
	system("pause");
}

常用举例专题

OpenCV—透视变换warpPerspective例子(直接可使用)

#include "opencv2/opencv.hpp"
#include <iostream>
 
using namespace cv;
using namespace std;
 
/// 全局变量
const char* source_window = "Source image";;
const char* warpPerspective_window = "warpPerspective";
 
/**
 * @主函数
 */
int main( )
{
	Point2f srcTri[4];
	Point2f dstTri[4];
 
	Mat warpPerspective_mat( 3, 3, CV_32FC1 );
	Mat src, warpPerspective_dst;
 
	/// Load the image
	src = imread( "lena.bmp", IMREAD_COLOR );
 
	/// Set the dst image the same type and size as src
	warpPerspective_dst = Mat::zeros( src.rows, src.cols, src.type() );
 
	/// 设置三组点,求出变换矩阵
	srcTri[0] = Point2f( 0,0 );
	srcTri[1] = Point2f( src.cols - 1,0 );
	srcTri[2] = Point2f( 0,src.rows - 1);
	srcTri[3] = Point2f(src.cols - 1,src.rows - 1);
 
	dstTri[0] = Point2f( 0,src.rows*0.13 );
	dstTri[1] = Point2f( src.cols*0.9,0 );
	dstTri[2] = Point2f( src.cols*0.2,src.rows*0.7 );
	dstTri[3] = Point2f( src.cols*0.8,src.rows );
 
	//计算3个二维点对之间的仿射变换矩阵(2行x3列)
	warpPerspective_mat = getPerspectiveTransform( srcTri, dstTri );
 
	//应用仿射变换,可以恢复出原图
	warpPerspective( src, warpPerspective_dst, warpPerspective_mat, src.size() );
 
	//显示结果
	namedWindow( source_window, WINDOW_AUTOSIZE );
	imshow( source_window, src );
 
	namedWindow( warpPerspective_window, WINDOW_AUTOSIZE );
	imshow( warpPerspective_window, warpPerspective_dst );
  /// 等待,直到用户退出
  waitKey(0);
  return 0;
}

图像四则运算举例

代数运算

算术运算包括加、减、乘、除和位运算,这些运算操作的特点是提供两个输入参数,得到一个输出结果。有时候还可以运算操作的权重系数,或者指定掩码。

加法运算拥有多种格式,能够灵活的进行各种不同的加法操作。下面主要介绍几种加法运算的用法。

最普通的加法操作 c[i] = a[i] + b[i] 。

cv::add(imageA,imageB,resultC);

将图像加上一个常数 c[i] = a[i] + k, 注意传入的Scalar是表示颜色的对象,需要根据图像的通道数来具体定义。

//灰度图像
cv::add(imageA,cv::Scalar(k),resultC);    
//彩色图像
cv::add(imageA,cv::Scalar(k,i,j),resultC);    

将两幅图像进行加权混合 c[i] = k1a[i] + k2b[i] + k3 。

cv::addWeighted(imageA,k1,imageB,k2,k3,resultC);

还能进行线性相加 c[i] = k*a[i] + b[i]。

cv::scaleAdd(imageA,k,imageB,resultC);

这些都是图像整体的加法运算,还能够添加一个掩码参数,掩码的作用就是让图像的某一部分参与运算,其他部分保持原样。具体来说,掩码通常与要运算的图像大小一样,当掩码的值非空(即为真),则在对应的图像位置进行运算,否则不运算。

cv::add(imageA,imageB,resultC,mask);

除了加法运算外,还有减法 cv::subtract、乘法 cv::multiply、除法 cv::divede、差的绝对值cv::absdiff,这些函数也有多种格式。

位运算符也是常用的一类运算符,如与运算 cv::bitwise_and、或运算cv::bitwise_or、异或运算 cv::bitwise_xor、非运算 cv::bitwise_not。cv::max 和 cv::min 能够找到两幅图像中的最大或者最小值。 此外还能对单张图像进行数学操作,如 cv::sqrt、cv::pow、cv::abs、cv::exp等等。

图像重映射(remap)举例(直接可使用)

如果只想改变像素的位置,可以使用 remap 函数,只需要定义好映射参数,然后将映射参数应用到输入图像上就可以。映射参数分为 X 坐标轴 和 Y 坐标轴,它们都用浮点数类型的 cv::Mat 表示,然后根据某种规则创建映射参数。下面展示一段用remap函数反转图像的代码(虽然已有方法 flip() 可以直接做到) :

using namespace cv;
int main()
{
	//变量定义
	Mat srcImage, dstImage;
	Mat map_x, map_y;
	//载入原始图
	srcImage = imread("E:\\写作插图\\下载.jpg", 1);
	if (!srcImage.data) 
	{ 
		printf("error\n"); 
		return false; 
	}
	imshow("原始图", srcImage);
	//创建和原始图一样的效果图,x重映射图,y重映射图
	dstImage.create(srcImage.size(), srcImage.type());
	map_x.create(srcImage.size(), CV_32FC1);
	map_y.create(srcImage.size(), CV_32FC1);
	//双层循环,创建映射参数
	for (int j = 0; j < srcImage.rows; j++)
	{
		for (int i = 0; i < srcImage.cols; i++)
		{
			//改变map_x & map_y的值.
			map_x.at<float>(j, i) = static_cast<float>(srcImage.cols - i);
			map_y.at<float>(j, i) = static_cast<float>(j);
		}
	}
	//进行重映射操作
	remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	//显示效果
	imshow("映射图像", dstImage);
	waitKey(0);
	return 0;
}
课程目的:OpenCV是应用非常广泛的开源视觉处理库,在图像处理、计算机视觉和自动驾驶中有着非常重要的作用。课程设计特色:(课程当前为第一期)1、C++与Python双语教学Python语言是在计算机视觉中应用最多的一种语言,在工作中,深度学习模型的训练基本上都是使用Python语言编写的训练代码。OpenCV在这个过程中用于图像的预处理(例如图像读取、数据增强)和后处理,还可以用于显示处理的结果,功能强大,使用方便。但是在功能的部署的时候,不管是部署在服务端还是PC端,开发语言基本上用的是C++,所以如何有效的使用OpenCV进行模型或者功能的部署尤为重要。C++语言应用的好坏,在面试中可以看出一个面试者的工程实践能力的强弱,两种语言的开发掌握好了可以使工作如虎添翼。2、全模块讲解我出版了一本图书《学习OpenCV4:基于Python的算法实战》,虽然这本书是写的基于Python的算法实战,但是实际上这本书有详细的介绍算法的C++接口,还有一些C++方向的案例,是以Python为主。图书出版的时候就想双语写作,只是限于篇幅没有成行。本课程不仅采用双语教学,更是对C++的每个模块都做讲解,我们知道,很多的书其实只讲imgproc,如果你翻开一本书图像的形态学运算和图像滤波都是作为独立章节讲解的,那么这本书基本上就可以确定是只是讲解了imgproc模块,但是其他的模块在工作中也有很重要的作用。例如:core模块定义C++的基本数据结构和基本运算(如四则运算);highgui模块是可视化与交互的模块;feature2d是特征点与特征匹配相关算法所在的模块;ml是机器学习相关的模块;dnn是深度学习相关的模块,可以使用OpenCV进行深度学习模型的部署。这些是很多的书和课程都不会讲的。3、讲解细致本课程会从环境搭建开始讲解,环境搭建尤为重要。从我多年的授课经验总结来看,如果只是给了代码,很多的入门用户环境问题处理不好的话,后面的学习很难进行下去,甚至会丧失学习的信心。4、会讲解C++和Python的开发语法问题是入门用户的一大难关,特别是C++语言。大学只是教授了C语言相关的内容,C++很多同学只懂一点皮毛,所以写代码步履维艰,我们在讲解代码的过程中会顺带讲解C++和Python的内容。我们还会讲解编译相关知识,还有库的装载与链接,这些是学校里不会教的,目前也几乎没有课程讲解。5、讲师经验丰富我讲解过C++OpenCV的多个课程,广受学员好评。我出版过两本图书《深度学习计算机视觉实战》和《学习OpenCV4》,两本书都是细致入微的讲解,主要针对的就是初学者,所以能够很好的处理课程的难易程度。6、讲义准备充分讲义准备的充分细致,标识清楚明确,重点和疑难点突出。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值