1、C++的数据类型+字节数+取值范围
数据类型 | 字节数 | 取值范围 |
---|---|---|
bool型(布尔型) | 1 | [0 or 1] |
BOOL型(int型) | 4 | [TRUE or FALSE] |
sbyte型(有符号8位整数) | 1 | [128 ~ 127] |
bytet型(无符号8位整数)8U | 2 | [0 ~ 255] |
short型(有符号16位整数)16S | 2 | [-32,768 ~ 32,767] |
ushort型(无符号16位整数)16U | 2 | [0 ~ 65,535] |
int型(有符号32位整数)32S | 4 | [-2,147,483 ~ 2,147,483,647] |
uint型(无符号32位整数) | 4 | [0 ~ 4,294,967,295] |
long型(64位有符号整数) | 8 | [9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807] |
ulong型(64位无符号整数) | 8 | [0 ~ 18,446,744,073,709,551,615] |
float型(32位单精度实数)32F | 4 | [3.4E+10的负38次方 ~ 3.4E+10的38次方] |
double型(64位双精度实数)64F | 8 | [1.7E+10的负308次方 ~ 1.7E+10的正308次方] |
指针 | 4 |
注意:int,float,double占多少个字节是由编译器决定的,不同的编译器,规定也不一样。ANSI标准定义int是占2个字节。
2、Mat对象:n 维单/多通道的密集矩阵
Mat(n-dimensional dense array class):用于存储数值、向量、矩阵、灰度或彩色图像、体素体积、向量场、点云、张量、直方图。
Mat类:创建并访问二维、三维、四维、五维矩阵
Mat类:矩阵相乘——点乘、dot、mul运算详解
OpenCV C++使用 Mat 来存储图像数据,而 Mat 由多个像素点组成。常规图像的存储格式是8位(8bite),即一个像素点占用8个字节空间。
图像的数据格式由两个部分组成:存储格式 + 通道数。包括:CV_{8U, 16S, 16U, 32S, 32F, 64F}C{1, 2, 3, 4}。
(1)存储格式包括: 8U(unsigned char), 16S(short int), 16U(unsigned short int),
32S(signed int), 32F(float), 64F(double)
(2)通道数包括:C{1, 2, 3, 4}。
其中,通道数代表每个像素点能存放多少个像素值。如:RGB彩色图中,每个像素点都有三个像素值。
C1(单通道图像)。如:灰度图。一般图像是8位(8bite)存储格式,即CV_8UC1。
C3(3通道图像)。如:RGB彩色图
C4(4通道图像)。如:带有透明度通道的RGB图像
例如:CV_8UC3表示8位无符号整型三通道矩阵
00、假设3通道矩阵Mat=(100, 100, 3)
11、RGB图像有100 x 100 x 3=30000个像素点
22、每个像素点在内存空间所占的空间大小是8位(8bite),即CV_8;。
33、每个像素点都存放3个值。
- 提问:
在对图像的像素处理时,为什么要把读取图像时的CV_8UC3(Vec3b)转换成CV_32F(Vec3F),且处理完之后还需要转换回CV_8UC3(Vec3b)无符号整型?- 回答:
8U取值范围=[0, 255],16U=[0, 65535],32f=[3.4E+10的负38次方 ~ 3.4E+10的38次方]。
(1)对像素进行图像处理时,无需转换可以直接处理。
(2)对像素进行数学计算时,需要保持高精度,避免信息损失。
2.1、创建 Mat 矩阵
函数说明:cv::Mat M_temp(row, column, data_type, cv::Scalar(0, 0, 255))
输入参数:
row 矩阵的行。
column 矩阵的列。
data_type 图像的数据格式。
Scalar(0, 0, 255) 初始化RGB的像素值。表示R/G全为0,B全为255。
//
cv::Mat img1(480, 640, CV_8UC3); //新建矩阵
cv::Mat img2(img1); //复制矩阵
cv::Mat img7 = cv::Mat::zeros(rows, cols, CV_8UC3); //值全0矩阵。
cv::Mat img8 = cv::Mat::ones(rows, cols, CV_64FC1); //值全1矩阵。
cv::Mat img9 = cv::Mat::eye(rows, cols, CV_16SC2); //单位矩阵。
2.2、获取像素1:img.at(y,x)
(0)
CV_8UC1(uchar)
:8位无符号整型单通道矩阵。
(1)CV_8UC3(Vec3b)
:8位无符号整型三通道矩阵。
(2)CV_32FC3(Vec3F)
:32位浮点型三通道矩阵。
cv::Mat img = cv::Mat::ones(240, 320, CV_8UC1); //创建单通道Mat矩阵
cv::Mat img1 = cv::Mat::ones(480, 640, CV_8UC3); //创建多通道Mat矩阵
float elem = img.at<uchar>(10, 10); //获取(单通道)像素点的像素值:img.at<uchar>(y,x)
cv::Vec3b elem = img1.at<cv::Vec3b>(10, 10); //获取(多通道)像素点的像素值:img.at<cv::Vec3b>(y,x)
elem_B = elem[0]; //蓝色通道数值(全255)
elem_G = elem[1]; //绿色通道数值(全255)
elem_R = elem[2]; //红色通道数值(全0)
2.3、获取像素2(防止颜色溢出):saturate_cast(y,x)
可以解决边界溢出:若像素值大于255,则赋值255;若像素值小于0,则赋值0。
//原理如下
if (data_value < 0)
data_value = 0;
else if (data_value > 255)
data_value = 255;
#include <opencv2/opencv.hpp>
//using namespace cv;
//using namespace std;
int main(int argc, const char* argv[])
{
//(1)读取图像
std::string img_path = "test.jpg";
cv::Mat src = cv::imread(img_path, 1);
//(2)判断图像是否读取成功
if (!src.data)
{
std::cout << "can't read image!" << std::endl;
return -1;
}
//(3)获取元素
cv::Mat dst1, dst2;
dst1 = cv::Mat::zeros(src.size(), src.type());
dst2 = cv::Mat::zeros(src.size(), src.type());
//三个for循环:dst(i,j) =a * src(i,j) + b
for (int y = 0; y < src.rows; y++)
{
for (int x = 0; x < src.cols; x++)
{
for (int c = 0; c < 3; c++) //三个通道
{
dst1.at<cv::Vec3b>(y, x)[c] = src.at<cv::Vec3b>(y, x)[c] * 2; //不饱和滤除
dst2.at<cv::Vec3b>(y, x)[c] = cv::saturate_cast<uchar>(src.at<cv::Vec3b>(y, x)[c] * 2); //饱和滤除
}
}
}
//(4)显示图像
cv::imshow("src", src);
cv::imshow("dst1", dst1);
cv::imshow("dst2", dst2);
cv::waitKey(0);
return 0;
}
2.4、Mat矩阵常用属性
cv::Mat img(320, 320, CV_8UC3, cv::Scalar(255, 255, 0)); //创建320×320的3通道彩色图像。
Mat.rows 获取矩阵 行数
Mat.cols 获取矩阵 列数
Mat.dims 获取矩阵 维数(单通道是二维,多通道是三维)
Mat.channels() 获取矩阵 通道数
Mat.size(); 获取矩阵 大小
Mat.total() 获取矩阵 面积=[行数*列数](与通道数无关)
Mat.depth() 获取矩阵 存储格式(返回0~6):enum{CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6}
Mat.row(0) 获取矩阵 第一行元素组成的数组。
Mat.col(0) 获取矩阵 第一列元素组成的数组。
Mat.rowRange(0, 10) 获取矩阵 第0-10行元素组成的数组。
Mat.colRange(0, 10) 获取矩阵 第0-10列元素组成的数组。
Mat.empty(); 判断矩阵是否为空,若为空,则返回true。
Mat.clone() 复制矩阵并赋值给新矩阵。
Mat.setTo(0); 将矩阵全部设置为指定值(0)。如:src.setTo(0, src<10);当src中的某个像素值小于10,就将该值设置成0。
MatA.copyTo(MatB) 将矩阵A复制到矩阵B中。
Mat.convertTo(img, CV_32F) 将img的数据格式转换为CV_32F。
Mat.push_back() 将一个或多个元素添加到矩阵底部。其类型和列数必须与Mat矩阵中的相同。
3、基本数据类型
3.1、Point类:cv::Point()
cv::Point类由两个部分组成:cv::Point{2, 3}{b, s, i, f, d}。
(1)维度 2、3代表维度(分别代表2个点、3个点)
(2)存储类型 b 表示无符号字符
s 表示短整型
i 表示32位整型
f 表示32位浮点数
d 表示64位浮点数
Point类支持的操作:
默认构造函数: cv::Point2i p;
cv::Point3i p;
复制构造函数: cv::Point3f p2(p1);
值构造函数: cv::Point2i(x0,x1);
cv::Point3d p(x0,x1,x2);
构造固定向量类: cv::Vec3f p;
成员访问: p.x, p.y;
点乘: float x = p1.dot(p2);
双精度点乘: double x =p1.ddot(p2);
叉乘: p1.cross(p2);
3.2、Scalar类:cv::Scalar()
cv::Scalar本质上是一个四维向量,其可以产生任意四元素的向量,一般是double类型。
Scalar类支持的操作:
默认构造函数: cv::Scalar s;
复制构造函数: cv::Scalar s2(s1);
值构造函数: cv::Scalar s(x0);
cv::Scalar s(x0, x1, x2, x3);
返回所有元素都为标量k: cv::Scalar::all(k)
元素相乘: s1.mul(s2);
(四元数)共轭: s.conj(); //return cv::Scalar(s0,-s1,-s2,-s2)
(四元数)真值测试: s.isReal(); //return ture, if s1==s2==s3==0.
3.3、Size类:cv::Size()
Size类支持的操作:
默认构造函数: cv::Size sz;
cv::Size2i sz;
cv::Size2f sz;
复制构造函数: cv::Size sz2(sz1);
值构造函数: cv::Size2f sz(w,h);
成员访问: sz.width, sz.height;
计算面积: sz.area();
3.4、Rect类:cv::Rect()
Rect类包含:
(1)Point类的成员(x, y)。 矩形左上角
(2)Size类的成员(width, height)。 代表矩形的大小
4.1 Rect类支持的操作:
默认构造函数: cv::Rect r;
复制构造函数: cv::Rect r2(r1);
值构造函数: cv::Rect(x, y, w, h);
由起始点和大小构造: cv::Rect(p, sz);
由两个对角构造: cv::Rect(p1, p2);
成员访问: r.x, r.y, r.width, r.height;
计算面积: r.area();
提取左上角和右下角: r.tl(), r.br();
判断p点是否在矩形r内: r.contains(p);
4.2 Rect类的覆写操作:
矩形r1和矩形r2的交集: cv::Rect r3 = r1&r2; r1 &= r2;
矩形r1和矩形r2的并集: cv::Rect r3 = r1|r2; r1 |= r2;
平移矩形r x个数量: cv::Rect rx = r+x; r += x;
扩大矩形r s大小: cv::Rect rs = r+s; r += s;
比较矩形r1和矩形r2是否相等: bool eq = (r1 == r2);
比较矩形r1和矩形r2是否不相等: bool ne = (r1 != r2);
3.5、Matx类:cv::Matx()
固定矩阵类(Matx类):
是为了使编译时就已知矩阵的维度而设计,其内部所有数据都是在堆栈上分配的。
其是Opencv的c++接口基本类型的核心。
其继承于固定矩阵类。而其他的类:要么继承于固定向量类,要么转换成固定向量类。
其是一个模板cv::Matx<>,但独立的矩阵通常通过别名分配。别名的基础格式为cv::Matx{1,2,3,4,5,6}{1,2,3,4,5,6}{f,d}。
5.1 Matx类支持的操作:
默认构造函数: cv::Matx33f m22f;
cv::Matx43d m43d;
复制构造函数: cv::Matx22d m22d(n22d);
值构造函数: cv::Matx21f m(x0,x1);
含相同元素的矩阵: m33f = cv::Matx33f::all(x);
全零矩阵: m23d = cv::Matx23d::zeros();
元素全是1的矩阵: m16f = cv::Matx16f::ones();
单位矩阵: m33f = cv::Matx33f::eye();
均匀分布矩阵: m33f = cv::Matx33f::randu(min,max);
正态分布矩阵: m33f = cv::Matx33f::nrandu(mean,variance);
成员访问: m(i,j), m(i);
矩阵能正常的进行加减乘除:
点积: float x = m1.dot(m2);
双精度点积: double x = m1.ddot(m2);
变换操作符: m44f = (Matx44f) m44d;
提取(i, j)处的2*2子矩阵: m44f.get_minor<2,2>(i, j);
提取第i行或者j列: m14f = m44f.row(i),m41f = m44f.col(j);
提取矩阵的对角线: m41f = m44f.diag();
计算矩阵转置: n44f = m44f.t();
计算矩阵的逆: n44f = m44f.inv(method);
每个元素的乘法: m1.mul(m2);
3.6、Vec类:cv::Vec()
6.1 Vec类支持的操作:
默认构造函数: cv::Vec2s v2s;
复制构造函数: cv::Vec3f u3f(v3f);
值构造函数: cv::Vec2f v2f(x0,x1);
成员访问: v4f[i]; v3w(j); //[] 和()都是可以的
向量叉乘: v3f, cross(u3f);
3.7、Range类:cv::Range()
cv::Range类:用于确定一个连续的整数序列。
函数说明:cv::Range(int start,int end)
输入参数:
start 起始点
end 终止点
备注:左闭右开,与Python的range()类似。
举例:cv::Range rng(0, 4) 包含[0,1,2,3],但是不包含4。
4、随机数:cv::RNG
(1)
cv::RNG rng(int seed);
:使用随机数种子seed产生一个64位的随机整数,默认-1。计算机产生的随机数都是伪随机数,是根据种子点seed和特定算法计算出来的。所以,只要种子数一定,算法一定,则每次产生的随机数也是一定的。用系统时间做种子点(2)
cv::RNG rng((unsigned)time(NULL));
用系统时间作为种子点,需添加头文件#include <time.h>
。
4.1、生成一个随机数:cv::RNG::uniform() + cv::RNG::gaussian()
基于随机数的3种生成方法:
cv::RNG rng(int seed) 使用随机数种子seed产生一个64位随机整数,默认-1。
(1)cv::RNG::uniform(a, b): 返回一个[a,b)范围的均匀分布的随机数。a, b的数据类型要一致,而且必须是int、float、double中的一种,默认是int。
(2)cv::RNG::gaussian(σ): 返回一个均值为0,标准差为σ的随机数。若要产生均值为λ,标准差为σ的随机数:λ+RNG::gaussian(σ)
#include<opencv2\opencv.hpp>
#include <string>
//using namespace cv;
//using namespace std;
int main(int argc,char* argv[])
{
cv::RNG rng(-1);
int randomNumber1 = rng;
double randomNumber2 = rng.uniform(0,99);
double randomNumber3 = rng.gaussian(2);
std::cout << "randomNumber1=" << randomNumber1 << std::endl; //randomNumber=130063606
std::cout << "randomNumber2=" << randomNumber2 << std::endl; //randomNumber=14
std::cout << "randomNumber3=" << randomNumber3 << std::endl; //randomNumber=-1.40186
return 0;
}
4.2、获取下一个随机数:next + operator
(1) cv::RNG:: next 返回下一个64位随机整数。
(2) cv::RNG:: operator 返回下一个指定类型的随机数。
#include<opencv2\opencv.hpp>
#include <string>
//using namespace cv;
//using namespace std;
int main(int argc,char* argv[])
{
cv::RNG rng(-1);
int randomNumber = rng.next(); //返回下一个随机整数,即N1.next();
//返回下一个指定类型的随机数
int randomNumber1 = rng.operator uchar(); //返回下一个无符号字符数
int randomNumber2 = rng.operator schar(); //返回下一个有符号字符数
int randomNumber3 = rng.operator ushort(); //返回下一个无符号短型
int randomNumber4 = rng.operator short int(); //返回下一个短整型数
int randomNumber5 = rng.operator int(); //返回下一个整型数
int randomNumber6 = rng.operator unsigned int(); //返回下一个无符号整型数
int randomNumber7 = rng.operator float(); //返回下一个浮点数
int randomNumber8 = rng.operator double(); //返回下一个double型数
int randomNumber9 = rng.operator ()(); //和rng.next( )等价
int randomNumber10 = rng.operator ()(100); //返回[0,100)范围内的随机数
std::cout << "randomNumber=" << randomNumber << std::endl; //randomNumber=130063605
std::cout << "randomNumber1=" << randomNumber1 << std::endl; //randomNumber=156
std::cout << "randomNumber2=" << randomNumber2 << std::endl; //randomNumber=-116
std::cout << "randomNumber3=" << randomNumber3 << std::endl; //randomNumber=24389
std::cout << "randomNumber4=" << randomNumber4 << std::endl; //randomNumber=31943
std::cout << "randomNumber5=" << randomNumber5 << std::endl; //randomNumber=-1348951784
std::cout << "randomNumber6=" << randomNumber6 << std::endl; //randomNumber=94037301
std::cout << "randomNumber7=" << randomNumber7 << std::endl; //randomNumber=0
std::cout << "randomNumber8=" << randomNumber8 << std::endl; //randomNumber=0
std::cout << "randomNumber9=" << randomNumber9 << std::endl; //randomNumber=776868985
std::cout << "randomNumber10=" << randomNumber10 << std::endl; //randomNumber=94
return 0;
}
4.3、用随机数填充矩阵:cv::RNG::fill()
函数说明:void fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false );
输入参数:
(1)mat 输入矩阵。2D或N维矩阵,最多支持4通道,超过4通道先用reshape()改变结构。
(2)distType 分布类型。
cv::RNG::UNIFORM 均匀分布。
cv::RNG::NORMAL 高斯分布。
(3)a 第一分布参数。均匀分布时表示一个下边界(闭区间),正态分布时表示平均值。
(4)b 第二分布参数。均匀分布时表示一个上边界(开区间),正态分布时表示标准差。
(5)saturateRange=false 只针对均匀分布有效。当为真的时候,会先把产生随机数的范围变换到数据类型的范围,再产生随机数。如果为假,会先产生随机数,再进行截断到数据类型的有效区间。
#include<opencv2\opencv.hpp>
#include <string>
//using namespace cv;
//using namespace std;
int main(int argc,char* argv[])
{
cv::RNG rng(-1);
cv::Mat_<int>fillM1(3, 3); //新建3x3矩阵,int类型
cv::Mat_<double>fillM2(3, 3); //新建3x3矩阵,double类型
rng.fill(fillM1, cv::RNG::UNIFORM, 1, 100); //随机生成[1,100)均匀分布的int数,并填充fillM。
rng.fill(fillM2, cv::RNG::NORMAL, 1, 3); //随机生成均值为1,标准差为3的double数,并填fillN。
std::cout << "filM = " << fillM1 << std::endl;
std::cout << "filN = " << fillM2 << std::endl;
return 0;
}
5、计算消费时间函数
5.1、耗时:cv::getTickCount()
函数说明:int64 cv::getTickCount();
函数作用:通过读取函数调用前后的时间刻度,来计算执行该函数所损耗的时间。
5.2、频率:cv::getTickFrequency()
函数说明:double cv::getTickFrequency();
函数作用:将损耗时间除以该函数(频率)以进行单位转换,返回时间的刻度数单位:秒。
5.3、实战案例
#include <opencv2/opencv.hpp>
//using namespace cv;
//using namespace std;
int main(int argc, const char* argv[])
{
double count1 = cv::getTickCount();
//处理
//处理
//处理
double count2 = cv::getTickCount();
double time_consume = (count2 - count1) / cv::getTickFrequency();
std::cout << "耗时:" << time_consume << std::endl;
}
6、鼠标与轨迹条操作
【OpenCV】OpenCV基础教程(11)—— HighGUI图形用户界面文章来源:https://www.toymoban.com/news/detail-779956.html
6.1、鼠标事件的回调函数:cv::MouseCallback
采用回调函数来处理鼠标事件。首先创建一个回调函数,并输入回调函数触发事件以及触发位置。函数还需要被告知用户是否在触发鼠标事件的同时触发了Shift或者Alt等键。文章来源地址https://www.toymoban.com/news/detail-779956.html
#include <opencv2/highgui.hpp>
函数说明:typedef void(* cv::MouseCallback) (int event, int x, int y, int flags, void *userdata)
输入参数:
11、event:鼠标事件
事件名称 数值 说明
CV_EVENT_MOUSEMOVE 0 指示鼠标指针已在窗口上移动。
CV_EVENT_LBUTTONDOWN 1 表示按下了鼠标左键。
CV_EVENT_RBUTTONDOWN 2 表示按下了鼠标右键。
CV_EVENT_MBUTTONDOWN 3 表示按下了鼠标中键。
CV_EVENT_LBUTTONUP 4 表示释放了鼠标左键。
CV_EVENT_RBUTTONUP 5 表示释放了鼠标右键。
CV_EVENT_MBUTTONUP 6 表示释放了鼠标中键。
CV_EVENT_LBUTTONDBLCLK 7 表示双击鼠标左键。
CV_EVENT_RBUTTONDBLCLK 8 表示双击鼠标右键。
CV_EVENT_MBUTTONDBLCLK 9 表示双击鼠标中键。
22、(x, y):触发鼠标事件的坐标位置
33、flags:鼠标状态
标志名称 数值 说明
CV_EVENT_FLAG_LBUTTON 1 表示鼠标左键已按下。
CV_EVENT_FLAG_RBUTTON 2 表示鼠标右键已按下。
CV_EVENT_FLAG_MBUTTON 4 表示鼠标中键已按下。
CV_EVENT_FLAG_CTRLKEY 8 表示按下了Ctrl键(8~15)。
CV_EVENT_FLAG_SHIFTKEY 16 表示按下了Shift键(16~31)。
CV_EVENT_FLAG_ALTKEY 32 表示按下了Alt键(32~39)。
44、param:(可选参数)可以以任何结构方式传递额外的参数信息。
6.2、设置鼠标事件的回调函数:cv::setMouseCallback()
#include <opencv2/highgui.hpp>
函数说明:void cv::setMouseCallback(const String &winname, MouseCallback onMouse, void *userdata = (void *)0)
输入参数:
winname 窗口的名称。
onMouse 鼠标事件的回调函数。
userdata 传递给回调的可选参数(默认0)。
6.3、创建轨迹条,并将其附加到指定的窗口:cv::createTrackbar()
- 创建一个具有指定名称和范围的轨迹条(滑块或范围控件),分配一个变量值作为与轨迹条同步的位置,并指定在轨迹条位置更改时调用的回调函数onChange。
- 创建的轨迹条将显示在指定的窗口winname中。
#include <opencv2/highgui.hpp>
函数说明:int cv::createTrackbar( const String &trackbarname, const String &winname, int *value, int count, TrackbarCallback onChange = 0, void *userdata = 0 )
输入参数:
trackbarname 创建轨迹条的名称。
winname 作为轨迹条的父窗口的名称。
value 指向整数变量的可选指针,该变量的值反映滑块的位置。创建时,滑块位置由该变量定义。
count 滑块的最大位置。最小位置始终为0。
onChange 指向每当滑块改变位置时要调用的函数的指针(默认0)。这个函数应该被原型化为void Foo(int,void*),其中第一个参数是轨迹条位置,第二个参数是用户数据(见下一个参数)。如果回调是NULL指针,则不会调用回调,但只更新值。
userdata 按原样传递给回调的用户数据(默认0)。它可以在不使用全局变量的情况下用于处理轨迹条事件。
6.4、获取指定轨迹条的当前位置:cv::getTrackbarPos()
#include <opencv2/highgui.hpp>
函数说明:int cv::getTrackbarPos(const String &trackbarname, const String &winname)
输入参数:
trackbarname 轨迹条的名称。
winname 作为轨迹条的父窗口的名称。
备注:如果轨迹条连接到控制面板,winname可以为空。
6.5、设置指定轨迹条在指定窗口中的位置:cv::setTrackbarPos()
#include <opencv2/highgui.hpp>
函数说明:void cv::setTrackbarPos( const String &trackbarname, const String &winname, int pos )
输入参数:
trackbarname 轨迹条的名称。
winname 作为轨迹条的父窗口的名称。
pos 新的位置。
备注:如果轨迹条连接到控制面板,winname可以为空。
到了这里,关于Opencv C++图像处理:矩阵Mat + 随机数RNG + 计算耗时 + 鼠标事件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!