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;
}