读懂opencv第一天(图像存储、模板类、文件格式、头文件、命名空间、数据类型,循环for,像素点操作,point,Mat与Mat_)

这篇文章先不去讨论opencv的一堆模块,二郎认为,能点进来的同学们也只是看到opencv的程序有点懵,而不知道如何下手,至于想要编写和如何调用都是后话。因此,没必要先去记块的名字和用法,应该先记住它的语言规则,如何排列以及函数之间的关系,这些都能帮助大家看懂程序,看懂才有机会自己写。

因为二郎学opencv主要做图像的,先谈一下图像的储存
opencv和matlab的储存方式不同,是按照字节储存的,用三个字节存储彩色图像三通道,顺序为——BGR
储存单位为无符号8位整形(CV_8U),表示亮度(一般后面加上C3,表示三通道,C几表示几通道)

Mat M(4,5, CV_8UC3, Scalar(155,0,0));//一个3维矩阵,同时也是一个蓝色的图像(RGB),4行5列
cout << "M = " << endl << " " << M << endl;//输出M矩阵
M=
[155,0,0,155,0,0,155,0,0,155,0,0,155,0,0;
155,0,0,155,0,0,155,0,0,155,0,0,155,0,0;
155,0,0,155,0,0,155,0,0,155,0,0,155,0,0;
155,0,0,155,0,0,155,0,0,155,0,0,155,0,0]

还有一点需要注意,在每个参数出现时,都必须有类型定义或者必须进行类型定义,所以大家会看到很多下面的情况

Mat canvasPart = canvas(Rect(w * 0, 0, w, h));    //Mat
for( int j = 0; j < image.cols; ++j )   //for

在参数赋值的时候,两个变量的类型不一样需要添加强制转换

int* aa;                // 指向整型数的指针
float* bb;            // 指向浮点数的指针
aa = bb;            // 直接赋值会导致编译错误
aa = (int*)bb;      // 强制类型转换后进行赋值

很多情况下会看到,前面也定义类型,后面也定义类型,也属于这种强制转换的情况

double d1 = (double) ((i+j)%255);

以及指针

int myArray[3] = { 9, 10, 11 };   // 定义一个数组
int* myIndex = myArray;          // 将数组的起始地址赋值给指针pIndex
cout<<"指针指向的地址:"<<myIndex<<endl;       // 指针指向的地址
cout<<"指针所指向的数据的值:"<<*myIndex<<endl; // 指向的地址存储的数值

myIndex++;   // 对指针进行加运算,使其指向数组中的下一个值
cout<<"指针指向的地址:"<<myIndex<<endl;        // 指针指向的地址
cout<<"指针所指向的数据的值:"<<*myIndex<<endl;  // 指向的地址存储的数值

输出结果

指针指向的地址:0012FA44
指针所指向的数据的值:9
指针指向的地址:0012FA48    //地址加了4,因为数据类型是 int-4字节,double-16字节,char-1字节。
指针所指向的数据的值:10

这里比较绕的是 myindex = 地址, *myindex = 地址位置存储的数值。

而比较好记的是,我们定义的指针是myindex,它里面的内容是地址,同时我们完全可以认为myindex是另一个变量,
内部存储数值,而只需要注意myindex和
myindex互相影响,对myindex操作,*myindex值会变。

这里有时我们会看到

int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][],所以数组指针也称指向一维数组的指针,亦称行指针。

不太对,刚才上面还说了,赋值给P的应该是地址,*P应该是数值——这里把一个数组赋给P,其实就相当于将数组的首地址赋给了P。

模板类

这个东西比较好玩,在很多情况下我们会见到不是我们之前的int c……之类的定意形式了,而是Mat c(当然其包含的内容也不一样了)
point模板类

Point point;//创建一个2D点对象
point.x = 3;//初始化x坐标值
point.y = 2;//初始化y坐标值

以及 Mat模板类

Mat R = Mat(3, 2, CV_8UC3);

这个和我在python博文中提到的class类似
Mat R————R属于Mat模板类,因此它含有该类中的统一属性
例如:
R.rows代表了R矩阵的行数;R.cols代表R矩阵的列数。

文件格式

cpp、obj、lib、exe的关系
lib 静态数据连接库,将obj转成库文件,用户无法直接看到foo文件,实现了代码的加密。
在这里插入图片描述

1.头文件

一堆的包含,其实只要不发生冲突,你把所有的头都引进来也行,一般不要这么做,这样程序在函数搜索时会变慢。

#include <iostream>  
#include <opencv2/opencv_modules.hpp>
#include <opencv2/opencv.hpp>  

需要哪个包含进去哪个,这个在很多程序中会出现问题
……文件不存在,……丢失。
头包含处出现问题:检查包含格式是否正确,去文档中找一下有无类似文件。
文中函数不存在:这个函数所在的模块没有在头包含时加进来,这时需要在网上找该模块来自哪个模块,将模块进行头包含。
头文件的用途知道这些就可以了。

2.命名空间

在opencv中你时常会看到**::**,这个是在强调命名空间,强调后面的函数在前面的函数的命名空间中。在opencv编程中命名空间有两种形式
①在程序开始声明
该方式避免了后面使用函数时需要考虑函数时c++函数还是opencv的函数。
一般这种方法在程序中函数不会混淆时使用

#include <opencv2/opencv.hpp>  

using namespace std;
using namespace cv;

②在函数前声明

System::Math::Sqrt() 相当于System.Math.Sqrt()
cv::

3.数据类型

给原始的数据提供描述,描述符号格式为:CV_{U|S|F}C(<number_of_channels>)
bit_depth—比特数:8bite,16bites,32bites,64bites

–S--代表—signed int—有符号整形
–U--代表–unsigned int–无符号整形
–F--代表–float---------单精度浮点型

C<number_of_channels>----代表—一张图片的通道数,
–灰度图片–grayImg----单通道图像
–RGB彩色图像----------3通道图像
–带Alph通道的RGB图像–4通道图像

比特数为32bites的3元素浮点元→CV_32FC3

4.opencv2提供matlab类型矩阵创建

矩阵创建

Mat Z = Mat::zeros(4,3, CV_8UC1);
Mat O = Mat::ones(4, 3, CV_8UC1);
Mat E = Mat::eye(4, 3, CV_8UC1);

向量定义

向量定义的类型说明
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
    
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

向量创建

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

5.循环 for

相信大家对for一定使用相当多,尤其是在矩阵运算时……在matlab中,for其实可以直接改写为矩阵运算,速度可以提高十几倍。

for( int i = 0; i < myimage.rows; ++i) //可以看出for循环和c++类似,
	 for( int j = 0; j < image.cols; ++j )
		 grayim.at<uchar>(i,j) = (i+j)%255;

for …………

6.对图像像素点进行操作

操作就少不了我们需要知道我们的图像一下基本信息,我们在matlab中用size确定图像的行和列
opencv用更简单的:myimage.rows和myimage.cols来反映行列数

①at

myImage.at<uchar>(j, i) //表示的是  j 行 i 列 的这个像素

myImage.at<uchar>(Point(j, i)) //表示的是 坐标(j,i)的像素

uchar value = myImage.at<uchar>(i,j);//读出第 i 行第 j 列像素值

这里涉及一个矩阵标和坐标的关系(好像没有矩阵标这一概念,二郎自己想的,方便记忆)
矩阵标:按照人类习惯,在讨论矩阵时总是先行后列,因此该标志也是这个规律
坐标:先x后y

在这里其实不会混淆,因为不是同一概念,一个是矩阵,一个是坐标。大家只需要记住,自己在编程时,是在对矩阵进行操作还是对坐标进行操作。

这里有人会问了(i,j)能表示灰度图,不能表示彩色图
把每个像素点看成一个整体(仅有x,y),它的性质(GBR)看成另一个整体。

Vec3b pixel;
pixel[0] = i%255; //Blue
pixel[1] = j%255; //Green
pixel[2] = 0; //Red
colorim.at<Vec3b>(i,j) = pixel;//直接将一个三通道的数赋给像素点

②矩阵的一行或者一列, row()或 col();多行多列,Range()
row,col

A.row(j) = A.row(i)*5+3;

Range(需要强调一下,图像的第一行和第一列的标号都为0,而Range参数标号都为1)

//创建一个单位阵
Mat A = Mat::ones(20, 10, CV_32U);
//提取第 1 到 3 列
Mat B = A(Range::all(), Range(1, 3));
//提取第 10 至 15 行
Mat C = A(Range(10, 15), Range::all());

③指针
指针定义了图像每一行的首地址,而列以及三维数组均可以由数组表示

#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;


int main(int argc, char* argv[])
{
	Mat grayim(600, 800, CV_8UC1);
	Mat colorim(600, 800, CV_8UC3);
	//遍历所有像素,并设置像素值
	for (int i = 0; i < grayim.rows; ++i)
	{
		//获取第 i 行首像素指针
		uchar * p = grayim.ptr<uchar>(i);
		//对第 i 行的每个像素(byte)操作
		for (int j = 0; j < grayim.cols; ++j)
			p[j] = (i + j) % 255;
	}
	//遍历所有像素,并设置像素值
	for (int i = 0; i < colorim.rows; ++i)
	{
		//获取第 i 行首像素指针
		Vec3b * p = colorim.ptr<Vec3b>(i);
		for (int j = 0; j < colorim.cols; ++j)
		{
			p[j][0] = i % 255; //Blue
			p[j][1] = j % 255; //Green
			p[j][2] = 0; //Red
		}
	}
	//显示结果
	imshow("grayim", grayim);
	imshow("colorim", colorim);
	waitKey(0);
	return 0;
}

7.point点的模板类

2维点模板类Point_和3维点模板类Point3_

  typedef Point2i Point;
  typedef Point_<float> Point2f;
  typedef Point_<double> Point2d;
  typedef Point3_<int> Point3i;

实例

  Point point;//创建二维点对象
  point.x = 3;//初始化x坐标值
  point.y = 4;//初始化y坐标值
  等价于
  Point point = Point(3, 4);

8.Mat和Mat_

我们会发现,在opencv中出现了很多带有下划线的数据类型,这里先写一点,其他的以后补充
我们在用指针时
uchar * p = M.ptr(i);

提取地址时每次都需要指定数据类型,比较繁琐
有了**Mat_**以后就不一样了

Mat_<uchar> M1 = (Mat_<uchar>&)M;//变量M1声明的同时确定了M1的指针类型
for( int i = 0; i < M1.rows; ++i)
{
uchar * p = M1.ptr(i);//上面已经声明,下面便不再需要声明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值