opencv中Vec类的小白向介绍

大纲

  1. 前置知识:C++中类(class)的相关知识
  2. 前置知识:C++中模板(template)的相关知识
  3. opencv中Vec类的定义及源码
  4. 引申:opencv中的宏定义如CV_8UC3

一、C++中的类
“类”是对对象的抽象,C++中的类实际上就是对一类具有相同特性的对象的交集,类相当于一种新的数据类型,数据类型不占用存储空间,用类型定义一个实体的时候,才会为它分配存储空间。
1.类的定义如下:

class 类名
{
成员类表
};

成员列表是类成员的集合,成员可以无限多,也可以没有;可以是数据,也可以是函数,成员函数如普通函数一样可以进行重载或者带默认参数。如果类成员中有函数的话,有以下两种定义方式:

在类内部定义函数体
class 类名
{
返回类型 函数名(形参列表)
{
函数体
} };
在类外部定义函数体
class 类名
{
返回类型 函数名(形参列表);
};
返回类型 类名 :: 函数名(形参列表)
{
函数体
}

前一种定义方式相当于内联函数,调用速度快,但是会占用额外的内存,后一种方式则为普通的外部函数,建议使用第二种方式定义。
如果使用后一种方式定义的话,需要注意C++中设置默认形参的动作之只能有一次,在类中声明时设置了默认参数,外部定义时就不能再重复设置。

class Test
{
public:
    void Sum(int a=0,int b=0);
};
void Test::Sum(int a,int b)
//void Test::Sum(int a=0,int b=0)  错误
{
    cout<<a+b;
}

2.类成员的访问控制
类的每个成员都有着自己的访问属性,包括“public"、”private"、"protected
“public"通常包含成员函数、”private“通常包括一般的变量。对于外部用户像是全局函数、另一个类的成员函数,只能通过"public"来访问类的成员。如:

#include <iostream>

using namespace std;

class Test
{
public:
	Test() {}
	Test(int x, int y) :a(x), b(y) {}
	void Sum();
private:
	int a, b;
	void minus()
};

void Test::Sum()
{
	cout << a + b;
}
void Test::minus()
{
	cout << b - a;
}
int main()
{
	Test test2(3, 4);
	test2.Sum();  //可以运行
	cout << test2.a;   //无法访问
	test2.minus()   //无法访问
	return 0;
}

从而类的定义扩展为:

class 类名
{
public:
公有的数据成员和成员函数
protected:
保护的数据成员和成员函数
private:
私有的数据成员和成员函数
};

3.类成员的初始化
建立一个类的对象时,需要对对象进行初始化,因为在类里面是不能对成员进行进行初始化的(类是数据类型,不占用存储空间),初始化的过程通过构造函数来实现。构造函数是一个没有返回值、和类同名的成员函数,由于创建对象通常是在外部进行的,所以构造函数申明为”public",例下:

#include <iostream>
 
using namespace std;
 
class Test
{
public:
    Test ();
    Test (int x,int y);
    void Sum();
private:
    int a,b;
};
Test::Test()
{
 
}
Test::Test(int x,int y)
{
    a=x;
    b=y;
}
void Test::Sum()
{
    cout<<a+b;
}
int main()
{
    Test test(3,4);
    test.Sum();
    return 0;
}

第一个“Test()“为默认构造函数也叫无参构造函数,它没有形参,可以在后续过程中赋值而不需要立即初始化。使用方法即:

TEST test

如果类中没有默认构造函数的话会报错,但IDE通常情况下会自动生成默认构造函数(实际上是4种特定情况,参见:自动生成默认构造函数情况),但如果自己定义了一个有参的构造函数就如“TEST (int x int y)",那IDE就不会自动再生成。也就是说,如果类中无默认构造函数,也可以通过”TEST test“初始化,一旦类中只存在有参构造函数,就必须带参初始化。、

构造函数初始化列表
之前介绍的构造函数是利用函数体内赋值的方式完成初始化,但更常用的一种方法其实是构造函数初始化列表,如:

#include <iostream>
 
using namespace std;
 
class Test
{
public:
    Test ();
    Test (int x,int y);
    void Sum();
private:
    int a,b;
};
Test::Test()
{
 
}
Test::Test(int x,int y):a(x),b(y)  //构造初始化列表而非赋值
{
 
}
void Test::Sum()
{
    cout<<a+b;
}
int main()
{
    Test test(3,4);
    test.Sum();
    return 0;
}

也可以更简洁的表述为:


class Test
{
public:
    Test () {}
    Test (int x,int y):a(x),b(y) {}  //直接在类内部完成定义
    void Sum();
private:
    int a,b;
};
 

这种方式较函数内赋值一般情况下没有区别,但如果需要初始化的是类类型的成员,就需要使用构建函数初始化列表的方法了。如:

#include <iostream>
 
using namespace std;
 
class Test
{
public:
    Test () {}
    Test (int x,int y):a(x),b(y) {}
    void Sum();
private:
    int a,b;
};
 
void Test::Sum()
{
    cout<<a+b;
}
class AnotherTest
{
public:
    AnotherTest(int i,int j):test(i,j) {test.Sum();}
private:
    Test test;
};
int main()
{
    AnotherTest test(3,4);
    return 0;
}

这里Anothertest类中含有一个数据类型为Test的成员,因此需要使用
初始化列表,注意因为在AnotherTest的构造函数里调用了Test类中的Sum成员函数,所以会有输出”7“,如果直接写test.test.Sum()是不会有输出值的,第二个test属于private类型,无法访问。

复制构造函数
复制构造函数顾名思义,通过一个已经存在的对象对另一个对象赋值初始化,常用形式为:

类名 (const 类名& obj)
{
函数体
}

例如:

#include <iostream>
 
using namespace std;
 
class Test
{
public:
    Test () {}
    Test (int x ,int y):a(x),b(y) {}
    Test (const Test& t):a(t.a),b(t.b) {}
    void Sum();
private:
    int a,b;
};
 
void Test::Sum()
{
    cout<<a+b;
}
 
int main()
{
    Test test1(3,4);
    Test test2 = test1;
    test2.Sum();
    return 0;
}

如果不定义复制构造函数,以上对象也可以这样进行初始化,原因就是系统也会自己生成一个复制构造函数。

类的继承
类的继承允许在已有类的基础上建立新类,继承方式,c++提供了三中继承方式。
public(公有继承):基类中的公有和保护成员保持原属性,私有成员为基类私有。
private(私有继承):基类中的所有成员在派生类中都是私有的。
protected(保护继承):基类的公有成员和保护成员在派生类中成了保护成员,私有成员仍为基类私有。
继承的写法如下:

class 子类名:public/ptivate/prtected 父类名
{
成员
}

例矩形对平行四边形的继承:

#include <iostream>
 
using namespace std;
 
class Parallelogram
{
public:
    Parallelogram(int a,int b):length(a),width(b) {}
    int getLength(){return length;}
    int getWidth() {return width;}
private:
    int length,width;
};
class Rectangle : public Parallelogram  //公有继承
{
public:
    Rectangle(int a,int b):Parallelogram(a,b) {}  //先对基类中的数据成员进行初始化
    void Area()     //计算面积
    {
        cout<<getLength()*getWidth();
    }
};
int main()
{
    Rectangle r(3,4);
    r.Area();
    return 0;
}

至此,“类”相关知识介绍完毕。
二、C++中的模板
如果说类是对对象的抽象,那么模板就是对类的抽象,模板相当于具有相同特性的类的交集,它声明了一种类的模板,并且提供了一个或多个的虚拟类型参数,从而给出不同数据类型的同一特性类。

模板的定义
常用形式为:

template<class/typename 虚拟类型1, class/typename 虚拟类型2…>
class 类名
{
成员
}

以下给出了一个适用于float和int型比较的类模板。

template <class numtype> //声明一个模板,虚拟类型名为numtype
class Compare //类模板名为Compare
{
public :
   Compare(numtype a,numtype b)
   {
      x=a;y=b;
   }
   numtype max( )
   {
      return (x>y)?x:y;
   }
   numtype min( )
   {
      return (x<y)?x:y;
   }
private :
   numtype x,y;
};

需要注意这里的成员函数是在类里面定义的,如果是在类外定义的话不能使用一般类成员函数的定义方法

numtype Compare::max( ) {…}

而应该先声明类模板:

template <class numtype>
numtype Compare<numtype>::max( )
{
    return (x>y)?x:y;
}

模板的初始化
使用模板定义对象时的形式为:
类模板名<实际类型名> 对象名;
类模板名<实际类型名> 对象名(实参表列);
如:Compare<int> cmp; Compare<int> cmp(3,7);
至此,所需“模板”知识介绍完毕。

三、opencv中的 Vec类
opencv中经常使用Vec+数字+字母来定义变量,像是Vec4b,Vec3s,其实这就是一个向量模板类,首先它在任何时刻都是一个一维矩阵(即列向量),其次它的元素数据类型可变。这一模板类是对与Mat类的继承,其源码如下;

//【1】下面是OpenCv中定义---定义---向量模板类----的源代码
//【2】一个拥有非类型模板形参的---类模板-----向量模板类
 template<typename _Tp, int cn> 
 class Vec : public Matx<_Tp, cn, 1>
{
	public:
		typedef _Tp value_type;
		enum { depth = DataDepth<_Tp>::value, channels = cn, 
					type = CV_MAKETYPE(depth, channels) };
					
	//! default constructor
	//【1】向量类的默认构造函数
	Vec();
	//【1】向量类有参构造函数
	Vec(_Tp v0); //!< 1-element vector constructor
	Vec(_Tp v0, _Tp v1); //!< 2-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2); //!< 3-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3); //!< 4-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4); //!< 5-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5); //!< 6-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6); //!< 7-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7); //!< 8-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8); //!< 9-element vector constructor
	Vec(_Tp v0, _Tp v1, _Tp v2, _Tp v3, _Tp v4, _Tp v5, _Tp v6, _Tp v7, _Tp v8, _Tp v9); //!< 10-element vector constructor
	explicit Vec(const _Tp* values);  //保证函数显式调用

	Vec(const Vec<_Tp, cn>& v);

	static Vec all(_Tp alpha);

	//! per-element multiplication
	Vec mul(const Vec<_Tp, cn>& v) const;

	//! conjugation (makes sense for complex numbers and quaternions)
	Vec conj() const;

	/*!
	  cross product of the two 3D vectors.

	  For other dimensionalities the exception is raised
	*/
	Vec cross(const Vec& v) const;
	//! convertion to another data type
	template<typename T2> operator Vec<T2, cn>() const;
	//! conversion to 4-element CvScalar.
	operator CvScalar() const;

	/*! element access */
	const _Tp& operator [](int i) const;
	_Tp& operator[](int i);
	const _Tp& operator ()(int i) const;
	_Tp& operator ()(int i);

	Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_AddOp);
	Vec(const Matx<_Tp, cn, 1>& a, const Matx<_Tp, cn, 1>& b, Matx_SubOp);
	template<typename _T2> Vec(const Matx<_Tp, cn, 1>& a, _T2 alpha, Matx_ScaleOp);
};	
//【3】用typedef关键字给---向量类模板----template<typename _Tp, int cn> class Vec
	//【1】向量模板类Vec的实例化,并且给相应实例的Vec向量模板类实例---指定新的名字
	//【1】Vec2b--这是一个具体的--类类型---这个类类型实例话的类对象表示如下所示:
	//【1】Vec2b---表示每个Vec2b对象中,可以存储2个char(字符型)数据
	typedef Vec<uchar, 2> Vec2b;//【2】Vec3b---表示每一个Vec3b对象中,可以存储3个char(字符型)数据,比如可以用这样的对象,去存储RGB图像中的一个像素点
	typedef Vec<uchar, 3> Vec3b;
	//【3】Vec4b---表示每一个Vec4b对象中,可以存储4个字符型数据,可以用这样的类对象去存储---4通道RGB+Alpha的图像中的像素点
	typedef Vec<uchar, 4> Vec4b;
             
	//【1】Vec2s---表示这个类的每一个类对象,可以存储2个short int(短整型)的数据
	typedef Vec<short, 2> Vec2s;
	typedef Vec<short, 3> Vec3s;
	typedef Vec<short, 4> Vec4s;

	typedef Vec<ushort, 2> Vec2w;
	typedef Vec<ushort, 3> Vec3w;
	typedef Vec<ushort, 4> Vec4w;

	typedef Vec<int, 2> Vec2i;
	typedef Vec<int, 3> Vec3i;
	typedef Vec<int, 4> Vec4i;
	typedef Vec<int, 6> Vec6i;
	typedef Vec<int, 8> Vec8i;

	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;

至此,opencv中的Vec模板类介绍完毕。

四、Opencv中的宏定义
使用Opencv时常常看到的字母加数字缩写除了Vec模板类,还有就是宏定义了,它经常性的用于Mat的初始化,其具体形式及含义如下:

CV+位深+数据类型缩写+U+通道数目

数据类型缩写有:

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

其具体含义也就不难理解,CV16UC4就表示16位无符号整型三通道矩阵。
如果我们要使用到Vec类来对使用这种宏定义初始化的Mat赋值时,需要注意Vec类要和宏定义相互对应,像是:

Mat tempImg1(480,640,CV_8UC3);
Vec3b& test = temp.at<Vec3b>(i, j);

由于此处定义的Mat为8位深3通道,所以对应Vec模板类为可容纳3(3通道
个字符型数据(8位)的Vec3b
再如:

Mat tempImg1(480,640,CV_16UC4);
Vec4w& test = temp.at<Vec4w>(i, j);

由于此处定义的Mat为16位深4通道,所以对应Vec模板类为可容纳4个(4通道)无符号整型数据(16位)的Vec4w

参考文献:
C++中类的介绍
C++template模板使用方法
OpenCV 中的 Scalar 类、Vec类
详解 c++ 关键字 explicit

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值