一、opencv宽高对应关系:
Mat.rows = Mat.size().height = 高
Mat.cols = Mat.size().width = 宽
int sz_1[2] = { 200, 400 }; // {高,宽} {Mat.rows,Mat.cols}
Mat m = cv::Mat(2, sz_1, CV_8UC1,Scalar::all(255));
or
Mat m = cv::Mat::ones(2, sz_1, CV_8UC1);
or
Mat m = cv::Mat::zeros(200, 400, CV_8UC1); // 高:200 宽:400
二、opencv矩阵创建以及元素级访问
2.1 二维、三维数据的创建和访问
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int main(){
Mat img = imread("123.jpg");
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
cv::Mat retMat = cv::Mat(600, 800, CV_8UC3, Scalar::all(255)); // 高, 宽, 通道数, 像素值
// 创建单通道图片
int sz[2] = { gray.rows, gray.cols }; // {高,宽}
// 或者使用:Mat fgCount = Mat(2, sz, CV_8UC1, Scalar::all(0));
Mat fgCount = cv::Mat::zeros(2, sz, CV_8UC1);
cv::imshow("fgCount", fgCount);
cv::waitKey(2000);
fgCount.at<uchar>(20, 20) = 20;
fgCount.at<uchar>(21, 21) = 21;
std::cout << "hahhahh------------------" << std::endl;
std::cout << int(fgCount.at<uchar>(20, 20)) << std::endl;
std::cout << int(fgCount.at<uchar>(21, 21)) << std::endl;
// 创建三维矩阵
int samples_size[3];
int height = gray.size().height;
int width = gray.size().width;
samples_size[0] = 20;
samples_size[1] = gray.rows;
samples_size[2] = gray.cols;
Mat m_3 = cv::Mat::zeros(3, samples_size, CV_8UC1);
std::cout << "Yes" <<std::endl;
cout << "三维图像的维度:" << m_3.dims << endl;
cout << "三维图像的通道数:" << m_3.channels() << endl;
// 创建二维多通道(类似三维矩阵)
typedef cv::Vec<double, 20> Vec20f;
Mat m_2 = cv::Mat::zeros(gray.size().width, gray.size().height, CV_64FC(20));
std::cout << "Yes" << std::endl;
cout << "图像的维度:" << m_2.dims << endl;
cout << "图像的通道数:" << m_2.channels() << endl;
m_2.at<Vec20f>(20, 20)[11] = 123.33;
cout << m_2.at<Vec20f>(20, 20)[11] << endl;
// 遍历图片
Mat src = imread("haha.png");
Mat img = imread("haha.png");
Mat img_2 = imread("haha.png");
int nl = img.rows; //行数
int nc = img.cols * img.channels(); //每行元素的总元素数量
int div = 64;
int cols = img.cols;
// 采用指针
double start_time = (double)getTickCount();
for (int j = 0; j < nl; j++)
{
uchar* data = img.ptr<uchar>(j);
for (int i = 0; i < cols; i++)
{
data[i* img.channels()] = 151;
}
}
double end_time = (double)getTickCount();
double fps = (end_time - start_time) / getTickCount();
cout << "采用指针所消耗时间:" << fps << endl;
// 采用内置at方法
start_time = (double)getTickCount();
for (int j = 0; j < nl; j++)
{
for (int i = 0; i < cols; i++)
{
img_2.at<Vec3b>(j, i)[1] = 151;
}
}
end_time = (double)getTickCount();
fps = (end_time - start_time) / getTickCount();
cout << "采用内置at方法消耗时间:" <<fps << endl;
imshow("src", src);
imshow("dst", img);
imshow("img_2", img_2);
waitKey(0);
}
初始化Mat对象:
Mat grad_x = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
Mat grad_y = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
cout << grad_x << endl;
cout << grad_y << endl;
2.2 四、五等高维数据创建和访问:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int main() {
int size4[4] = { 100, 100, 100, 100 };
cv::Mat mat4D(4, size4, CV_8UC1, cv::Scalar(0)); // 创建四维Mat对象
cout << (int)mat4D.ptr<uchar>(0, 0, 0)[4] << endl;
mat4D.ptr<uchar>(0, 0, 0)[4] = 111; // 给(0,0,0,4)赋值
cout << (int)mat4D.ptr<uchar>(0, 0, 0)[4] << endl;
cout << endl;
int size5[5] = {100,100,100,100,100};
cv::Mat mat5D(5, size5, CV_8UC1, cv::Scalar(0)); // 创建五维Mat对象
// 输出[1,2,3,4,5]位置的值
cout << (int)*(mat5D.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5) << endl;
*(mat5D.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5) = 112;
cout << (int)*(mat5D.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5) << endl;
cout << endl;
cv::Mat mat5D_float(5, size5, CV_16FC1, cv::Scalar(0)); // 创建五维Mat对象
// 输出[1,2,3,4,5]位置的值
// *((float*)(mat5D_float.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5))这样也可以
cout << *((float*)mat5D_float.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5) << endl;
*((float*)mat5D_float.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5) = 113;
cout << *((float*)mat5D_float.data + 1 * 100 * 100 * 100 * 100 + 2 * 100 * 100 * 100 + 3 * 100 * 100 + 4 * 100 + 5) << endl;
cout << endl;
waitKey(10000);
}
大于四维的Mat,既不能使用at,也不能使用ptr访问元素。解释一下上面的方法:使用Mat类中的data成员变量,它指向Mat数据区的首地址,对于Mat类型的数据,它们是按照线性方式存值的,我们知道首地址之后,就可以通过指针偏移的方式,去索引任意元素的值。对于 100x100x100x100x100 的五维Mat,如果索引要 [a,b,c,d,e] 中的值,则可以使用下面的方式:
uchar a = *(mat5D.data + a*100*100*100*100 + b*100*100*100 + c*100*100 + d*100 + e);
如果你在Mat中存储的不是CV_8UC1,而是别的类型,比如是CV_32FC1,那么就不能直接使用data
,因为它默认是uchar
类型的指针,需要将它强转为float
类型。代码如下:
float a = *((float*)mat5D.data + a*100*100*100*100 + b*100*100*100 + c*100*100 + d*100 + e);
上述代码结果:
其他方式访问:
// 利用Mat的 Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0) 这一成员函数
int p = 1;
int q = 2;
int t = 3;
int u = 4;
int sizes[] = { p,q,t,u };
int all = p * q*t*u;
float *d1 = new float[all];
for (int i = 0; i < all; i++)
{
d1[i] = i * 1.0f;
}
Mat a = Mat(4, sizes, CV_32S, d1);
int n, c, h, w, id;
//四维数据的访问为:
for (n = 0; n < p; n++)
{
for (c = 0; c < q; c++)
{
for (h = 0; h < t; h++)
{
for (w = 0; w < u; w++)
{
id = a.step[0] * n + a.step[1] * c + a.step[2] * h + w * a.step[3];
//cout << id << endl;
float *p = (float*)(a.data + id);
cout << *p << endl;
}
}
}
}
三、opencv矩阵运算
3.1 基础运算
现在opencv的Mat类矩阵运算操作已经和numpy很像了,一些案例如下:
void matOP() {
Mat mat_1(4, 9, CV_16SC1);
Mat mat_2(4, 9, CV_16SC1);
Mat mat_3(4, 9, CV_16SC1);
RNG rng(unsigned int(time(NULL)));
rng.fill(mat_1, RNG::UNIFORM, -4, 4, true);
rng.fill(mat_2, RNG::UNIFORM, 0, 10, true);
mat_3 = mat_1 + mat_2;
cout << "mat_1:" << endl;
cout << mat_1 << endl;
cout << "mat_2:" << endl;
cout << mat_2 << endl;
cout << "mat_3:" << endl;
cout << mat_3 << endl;
Mat big_zero = Mat::zeros(4, 9, CV_16SC1);
big_zero = mat_3 >= 0;
cout << "big_zero:" << endl;
cout << big_zero << endl;
Mat ret_1 = Mat::zeros(4, 9, CV_16SC1);
bitwise_and(mat_3, mat_3, ret_1, big_zero);
cout << "ret_1:" << endl;
cout << ret_1;
int a;
cin >> a;
}
结果截图:
3.2 乘除运算
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int main() {
//点乘运算:矩阵的点乘即两个矩阵对应位置的数值相乘
// 点乘方法一
Mat m1 = (Mat_<uchar>(3, 2) << 200, 12, 21, 22, 31, 32);
Mat m2 = (Mat_<uchar>(3, 2) << 150, 11, 22, 21, 32, 31);
Mat dst = m1.mul(m2); // 这种方法的两个Mat对象的数据类型必须相同才可以进行点乘,返回的矩阵的数据类型不变
cout << "方法一点乘运算结果为:" << dst << endl;
// 点乘方法二
Mat m1a = (Mat_<uchar>(3, 2) << 200, 12, 21, 22, 31, 32);
Mat m2a = (Mat_<float>(3, 2) << 150, 11, 22, 21, 32, 31);
Mat dsta;
multiply(m1a, m2a, dsta, 1.0, CV_64FC1);
cout << "方法二点乘运算结果为:" << dsta << endl;
// 点除运算:矩阵的点乘即两个矩阵对应位置的数值相除
// 点除方法一
Mat m1b = (Mat_<uchar>(3, 2) << 301, 22, 21, 22, 31, 32);
Mat m2b = (Mat_<uchar>(3, 2) << 150, 11, 22, 21, 32, 0);
Mat dstb = m1b / m2b; // 如果分母为0的除法运算时,默认得到的值为0
cout << "方法一点除运算结果为:" << dstb << endl;
// 点除方法二
Mat m1c = (Mat_<uchar>(3, 2) << 200, 12, 21, 22, 31, 32);
Mat m2c = (Mat_<float>(3, 2) << 150, 11, 22, 21, 32, 31);
Mat dstc;
divide(m1c, m2c, dstc, 1.0, CV_64FC1);
cout << "方法二点除运算结果为:" << dstc << endl;
// 矩阵运算
// 单通道矩阵运算
Mat m1d = (Mat_<float>(3, 2) << 11, 12, 21, 22, 31, 32);
Mat m2d = (Mat_<float>(2, 3) << 11, 12, 13, 21, 22, 23);
Mat dstd = m1d * m2d; // 两个Mat只能同时是float或者double类型
cout << "单通道矩阵运算结果为:" << dstd << endl;
waitKey(10000);
}
需要说明的是,对于多通道矩阵乘法opencv有特殊处理,即将两通道矩阵当做复数相乘。详细可前往这里。乘除运算也参考Ta。
四、opencv产生随机数
比RNG方便,如下(每次产生的随机是一样的):
void rand_test() {
Mat R = Mat(3, 2, CV_16SC3);
// randu(dst, low, high);dst – 输出数组或矩阵 ;low – 区间下界(闭区间); high - 区间上界(开区间)
randu(R, -4, 5); // 返回均匀分布的随机数,填入数组或矩阵
cout << R << endl;
// randn(dst, mean, stddev);dst – 输出数组或矩阵; mean – 均值; stddev - 标准差
randn(R, -4, 4); // 返回高斯分布的随机数,填入数组或矩阵
cout << R << endl;
/*randShuffle(InputOutputArray dst, 输入输出数组(一维)
double iterFactor = 1., 决定交换数值的行列的位置的一个系数...
RNG* rng = 0) (可选)随机数产生器,0表示使用默认的随机数产生器,即seed = -1。rng决定了打乱的方法*/
unsigned int seed = time(NULL);
RNG rng(seed);
randShuffle(R, 1, &rng); // 将原数组(矩阵)打乱
cout << R << endl;
}
结果: