目录
一、前言
上一篇文章,我们讲了OpenCV中的一个基础结构,Rect_结构,再加上之前讲的两个结构,我们已经讲过三个基本架构啦!
我们也看到了我们讲的结构没有涉及到颜色,而在计算机视觉中,颜色是非常重要的一部分,所以我们今天要讲的Scala_类,就是讲解颜色的,这也是我们要讲的最后一个基础结构了。
其实我们知道,还有很多基础结构,但其他的其实并不常用。有的可能是用于某个特定的领域,比如用于三维空间的点的Point3_,用于特征检测的KeyPoint等,如果我们在后面用到了,我们再进行详细说明吧!
二、温故知新——Rect_
1、定义与成员变量
上一节课,我们讲到了Rect_,大家还记得,我们说的,该怎么理解这个结构吗?
对,就是下面这个:
一个明确位置且横平竖直的矩形。
怎么理解这句话呢?
什么是明确位置,就是需要一个定位点;
什么是横平竖直,就是只通过长和宽就能唯一确定一个矩形。
所以,我们需要四个成员变量:
表示横纵坐标位置的两个成员变量:x,y
表示长宽的两个成员变量:height,width
_Tp x; //!< x coordinate of the top-left corner
_Tp y; //!< y coordinate of the top-left corner
_Tp width; //!< width of the rectangle
_Tp height; //!< height of the rectangle
2、构造函数
所以构造过程无非就是给这四个变量赋值,我们有如下几种方式:
第一种方式是使用一个点的坐标和长宽。
(1)对于这种方式,我们既可以给四个参数:横坐标、纵坐标、长、宽:
(2)也可以使用我们昨天提到的两个类型:Point_和Size_。
第二种方式是使用两个对角线顶点。
第三种方式是使用另外的矩形为该矩形赋值。
Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);
Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz);
Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2);
Rect_(const Rect_& r);
Rect_(Rect_&& r) CV_NOEXCEPT;
3、常用方法
常用方法内容比较多,主要有如下几个方面:
1.定位点的移位操作;
2.矩形尺寸变化操作;
3.求公共矩阵操作;
4.求最小包围矩阵操作;
5.获取矩形基本信息(定位点、长宽、尺寸、面积、左上角与右下角坐标)操作;
6.判断矩阵是否相同操作;
7.计算点是否在矩形内操作;
8.赋值操作;
还有很多常用操作,因为内容比较多,就不再在这里说明啦!大家可以打开上一篇博客去详细查看哦:
三、Scalar_
接下来就让我们走进最后一个最常用的基本结构Scalar_吧!如果你没有接触过色彩相关内容,那可能对这些内容不太清楚,首先,我们先来看一下三原色:
三原色指色彩中不能再分解的三种基本颜色,我们通常说的三原色,是色彩三原色以及光学三原色。
三原色主要有如下两种类型:
1.颜料三原色(CMYK):品红、黄、青(天蓝)。色彩三原色可以混合出所有颜料的颜色,同时相加为黑色,黑白灰属于无色系。
2.光学三原色(RGB):红、绿、蓝(靛蓝)。光学三原色混合后,组成显示屏显示颜色,三原色同时相加为白色,白色属于无色系(黑白灰)中的一种。
在我们计算机视觉中用到的颜色是光学颜色,所有,我们这里要用到的像素都是基于光学三原色的。接下来让我们从定义走近它——Scalar_。
1、定义
首先让我们看一下定义介绍:
/** @brief Template class for a 4-element vector derived from Vec.
Being derived from Vec\<_Tp, 4\> , Scalar\_ and Scalar can be used just as typical 4-element
vectors. In addition, they can be converted to/from CvScalar . The type Scalar is widely used in
OpenCV to pass pixel values.
*/
这里面说,Scalar其实是一个从Vec派生得到的四元向量的模板类,在OpenCV中广泛用于传递像素值。
我们接着看一下定义:
template<typename _Tp> class Scalar_ : public Vec<_Tp, 4>
{
public:
//! default constructor
Scalar_();
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
Scalar_(_Tp v0);
Scalar_(const Scalar_& s);
Scalar_(Scalar_&& s) CV_NOEXCEPT;
Scalar_& operator=(const Scalar_& s);
Scalar_& operator=(Scalar_&& s) CV_NOEXCEPT;
template<typename _Tp2, int cn>
Scalar_(const Vec<_Tp2, cn>& v);
//! returns a scalar with all elements set to v0
static Scalar_<_Tp> all(_Tp v0);
//! conversion to another data type
template<typename T2> operator Scalar_<T2>() const;
//! per-element product
Scalar_<_Tp> mul(const Scalar_<_Tp>& a, double scale=1 ) const;
//! returns (v0, -v1, -v2, -v3)
Scalar_<_Tp> conj() const;
//! returns true iff v1 == v2 == v3 == 0
bool isReal() const;
};
typedef Scalar_<double> Scalar;
template<typename _Tp> class DataType< Scalar_<_Tp> >
{
public:
typedef Scalar_<_Tp> value_type;
typedef Scalar_<typename DataType<_Tp>::work_type> work_type;
typedef _Tp channel_type;
enum { generic_type = 0,
channels = 4,
fmt = traits::SafeFmt<channel_type>::fmt + ((channels - 1) << 8)
#ifdef OPENCV_TRAITS_ENABLE_DEPRECATED
,depth = DataType<channel_type>::depth
,type = CV_MAKETYPE(depth, channels)
#endif
};
typedef Vec<channel_type, channels> vec_type;
};
namespace traits {
template<typename _Tp>
struct Depth< Scalar_<_Tp> > { enum { value = Depth<_Tp>::value }; };
template<typename _Tp>
struct Type< Scalar_<_Tp> > { enum { value = CV_MAKETYPE(Depth<_Tp>::value, 4) }; };
} // namespace
这里面最基本的定义主要包括如下三部分:
1.构造函数
几个构造函数,用于生成Scalar对象:
//! default constructor
Scalar_();
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
Scalar_(_Tp v0);
Scalar_(const Scalar_& s);
Scalar_(Scalar_&& s) CV_NOEXCEPT;
第一个构造函数,我们已经见怪不怪了,几乎有所的类中的默认构造函数都是无参,并且赋值为0或空。
第二个就是我们最常用的,但是一般情况,我们只赋值前三个,第一个参数表示蓝色,第二个参数表示绿色,第三个参数表示红色,也就是BGR:
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
Scalar(0, 0, 255); // 红色
Scalar(0, 255, 0); // 绿色
Scalar(255, 0, 0); // 蓝色
第三个只设置第一个参数,是因为后面的每个都默认设置为0了:
template<typename _Tp> inline
Scalar_<_Tp>::Scalar_(_Tp v0)
{
this->val[0] = v0;
this->val[1] = this->val[2] = this->val[3] = 0;
}
后两个就是用别的Scalar赋值。
2.基本操作
在Scalar_中有如下基本操作:
//! 将所有元素都设为v0
static Scalar_<_Tp> all(_Tp v0);
//! 转换为其他类型
template<typename T2> operator Scalar_<T2>() const;
//! 乘积
Scalar_<_Tp> mul(const Scalar_<_Tp>& a, double scale=1 ) const;
//! 返回 (v0, -v1, -v2, -v3)
Scalar_<_Tp> conj() const;
//! 返回真 true 如果 v1 == v2 == v3 == 0
bool isReal() const;
第一个,将所有元素都设为v0:
实现代码如下:
template<typename _Tp> inline
Scalar_<_Tp> Scalar_<_Tp>::all(_Tp v0)
{
return Scalar_<_Tp>(v0, v0, v0, v0);
}
实验代码如下:
Scalar color = Scalar();
color = color.all(120);
cout << color << endl;
输出结果如下:
第二个,计算两个Scalar的乘积:
实现代码如下,其中scale是一个标量参数,saturate_cast是将数据范围控制在0-255之间,但是我们要注意,在输出的时候,还是原来的数值,但是在以颜色输出就会控制在0-255之间了:
template<typename _Tp> inline
Scalar_<_Tp> Scalar_<_Tp>::mul(const Scalar_<_Tp>& a, double scale ) const
{
return Scalar_<_Tp>(saturate_cast<_Tp>(this->val[0] * a.val[0] * scale),
saturate_cast<_Tp>(this->val[1] * a.val[1] * scale),
saturate_cast<_Tp>(this->val[2] * a.val[2] * scale),
saturate_cast<_Tp>(this->val[3] * a.val[3] * scale));
}
实验代码如下:
Scalar color1 = Scalar(12, 255, 14);
Scalar color2 = Scalar(122, 14, 145);
Scalar color3 = color1.mul(color2);
cout << color3 << endl;
Mat img = Mat(200, 200, CV_8UC3, color3);
imshow("test2", img);
输出结果如下:
当三个都是255的时候,是纯白色图像。
第三个,返回 (v0, -v1, -v2, -v3):
实现代码如下:
template<typename _Tp> inline
Scalar_<_Tp> Scalar_<_Tp>::conj() const
{
return Scalar_<_Tp>(saturate_cast<_Tp>( this->val[0]),
saturate_cast<_Tp>(-this->val[1]),
saturate_cast<_Tp>(-this->val[2]),
saturate_cast<_Tp>(-this->val[3]));
}
实验代码如下:
Scalar color2 = Scalar(122, 14, 145);
Scalar color4 = color2.conj();
cout << color4 << endl;
执行结果如下:
第四个,如果前三个都是0,那么返回true:
实现代码如下:
template<typename _Tp> inline
bool Scalar_<_Tp>::isReal() const
{
return this->val[1] == 0 && this->val[2] == 0 && this->val[3] == 0;
}
实验代码如下:
Scalar color5 = Scalar();
if (color5.isReal()) {
cout << color5 << " is real" << endl;
}
执行结果如下:
3.使用时的数据类型
当我们定义了Scalar_类之后,跟上面一样,我们要定义具体的使用过程中的类型:
typedef Scalar_<double> Scalar;
2、常用方法
接下来我们要说的就是方法,颜色没有那么多花花肠子,只涉及到最基本的数值运算 。
template<typename _Tp> static inline
Scalar_<_Tp>& operator += (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a.val[0] += b.val[0];
a.val[1] += b.val[1];
a.val[2] += b.val[2];
a.val[3] += b.val[3];
return a;
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator -= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a.val[0] -= b.val[0];
a.val[1] -= b.val[1];
a.val[2] -= b.val[2];
a.val[3] -= b.val[3];
return a;
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator *= ( Scalar_<_Tp>& a, _Tp v )
{
a.val[0] *= v;
a.val[1] *= v;
a.val[2] *= v;
a.val[3] *= v;
return a;
}
template<typename _Tp> static inline
bool operator == ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b )
{
return a.val[0] == b.val[0] && a.val[1] == b.val[1] &&
a.val[2] == b.val[2] && a.val[3] == b.val[3];
}
template<typename _Tp> static inline
bool operator != ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b )
{
return a.val[0] != b.val[0] || a.val[1] != b.val[1] ||
a.val[2] != b.val[2] || a.val[3] != b.val[3];
}
template<typename _Tp> static inline
Scalar_<_Tp> operator + (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return Scalar_<_Tp>(a.val[0] + b.val[0],
a.val[1] + b.val[1],
a.val[2] + b.val[2],
a.val[3] + b.val[3]);
}
template<typename _Tp> static inline
Scalar_<_Tp> operator - (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return Scalar_<_Tp>(saturate_cast<_Tp>(a.val[0] - b.val[0]),
saturate_cast<_Tp>(a.val[1] - b.val[1]),
saturate_cast<_Tp>(a.val[2] - b.val[2]),
saturate_cast<_Tp>(a.val[3] - b.val[3]));
}
template<typename _Tp> static inline
Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, _Tp alpha)
{
return Scalar_<_Tp>(a.val[0] * alpha,
a.val[1] * alpha,
a.val[2] * alpha,
a.val[3] * alpha);
}
template<typename _Tp> static inline
Scalar_<_Tp> operator * (_Tp alpha, const Scalar_<_Tp>& a)
{
return a*alpha;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator - (const Scalar_<_Tp>& a)
{
return Scalar_<_Tp>(saturate_cast<_Tp>(-a.val[0]),
saturate_cast<_Tp>(-a.val[1]),
saturate_cast<_Tp>(-a.val[2]),
saturate_cast<_Tp>(-a.val[3]));
}
template<typename _Tp> static inline
Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return Scalar_<_Tp>(saturate_cast<_Tp>(a[0]*b[0] - a[1]*b[1] - a[2]*b[2] - a[3]*b[3]),
saturate_cast<_Tp>(a[0]*b[1] + a[1]*b[0] + a[2]*b[3] - a[3]*b[2]),
saturate_cast<_Tp>(a[0]*b[2] - a[1]*b[3] + a[2]*b[0] + a[3]*b[1]),
saturate_cast<_Tp>(a[0]*b[3] + a[1]*b[2] - a[2]*b[1] + a[3]*b[0]));
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator *= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a = a * b;
return a;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, _Tp alpha)
{
return Scalar_<_Tp>(a.val[0] / alpha,
a.val[1] / alpha,
a.val[2] / alpha,
a.val[3] / alpha);
}
template<typename _Tp> static inline
Scalar_<float> operator / (const Scalar_<float>& a, float alpha)
{
float s = 1 / alpha;
return Scalar_<float>(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s);
}
template<typename _Tp> static inline
Scalar_<double> operator / (const Scalar_<double>& a, double alpha)
{
double s = 1 / alpha;
return Scalar_<double>(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s);
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, _Tp alpha)
{
a = a / alpha;
return a;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator / (_Tp a, const Scalar_<_Tp>& b)
{
_Tp s = a / (b[0]*b[0] + b[1]*b[1] + b[2]*b[2] + b[3]*b[3]);
return b.conj() * s;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return a * ((_Tp)1 / b);
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a = a / b;
return a;
}
template<typename _Tp> static inline
Scalar operator * (const Matx<_Tp, 4, 4>& a, const Scalar& b)
{
Matx<double, 4, 1> c((Matx<double, 4, 4>)a, b, Matx_MatMulOp());
return reinterpret_cast<const Scalar&>(c);
}
template<> inline
Scalar operator * (const Matx<double, 4, 4>& a, const Scalar& b)
{
Matx<double, 4, 1> c(a, b, Matx_MatMulOp());
return reinterpret_cast<const Scalar&>(c);
}
1.加法及加赋值运算
我们先从最简单的加法及加赋值说起,有如下几个:
template<typename _Tp> static inline
Scalar_<_Tp> operator + (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return Scalar_<_Tp>(a.val[0] + b.val[0],
a.val[1] + b.val[1],
a.val[2] + b.val[2],
a.val[3] + b.val[3]);
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator += (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a.val[0] += b.val[0];
a.val[1] += b.val[1];
a.val[2] += b.val[2];
a.val[3] += b.val[3];
return a;
}
注:个人见解
如果要用到像素中,上面的代码是有问题的,因为加法可能超出255的范围,也有可能输入是负值。所以如果是要用到像素中,要修改像素的范围区间的。后面的类似。
加法和加赋值无疑是最简单的,就是两个类型的对应数值分别相加,我们看个简单示例:
Scalar color6 = Scalar(152, 100, 4);
Scalar color7 = Scalar(2, 120, 105);
cout << "color6 + color7 = " << color6 + color7 << endl;
color6 += color7;
cout << "color6 += color7 " << color6 << endl;
执行结果如下:
2.减法及减赋值运算
减法及减赋值和加法及加赋值是类似的,有如下几个:
template<typename _Tp> static inline
Scalar_<_Tp> operator - (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return Scalar_<_Tp>(saturate_cast<_Tp>(a.val[0] - b.val[0]),
saturate_cast<_Tp>(a.val[1] - b.val[1]),
saturate_cast<_Tp>(a.val[2] - b.val[2]),
saturate_cast<_Tp>(a.val[3] - b.val[3]));
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator -= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a.val[0] -= b.val[0];
a.val[1] -= b.val[1];
a.val[2] -= b.val[2];
a.val[3] -= b.val[3];
return a;
}
我们看个简单示例:
Scalar color6 = Scalar(152, 100, 4);
Scalar color7 = Scalar(2, 120, 105);
cout << "color6 = " << color6 << endl;
cout << "color7 = " << color7 << endl;
cout << "color6 + color7 = " << color6 + color7 << endl;
color6 += color7;
cout << "color6 += color7 " << color6 << endl;
执行结果如下:
3.乘法及乘赋值运算
乘法就比较多啦,有如下几个:
template<typename _Tp> static inline
Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, _Tp alpha)
{
return Scalar_<_Tp>(a.val[0] * alpha,
a.val[1] * alpha,
a.val[2] * alpha,
a.val[3] * alpha);
}
template<typename _Tp> static inline
Scalar_<_Tp> operator * (_Tp alpha, const Scalar_<_Tp>& a)
{
return a*alpha;
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator *= ( Scalar_<_Tp>& a, _Tp v )
{
a.val[0] *= v;
a.val[1] *= v;
a.val[2] *= v;
a.val[3] *= v;
return a;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator * (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return Scalar_<_Tp>(saturate_cast<_Tp>(a[0]*b[0] - a[1]*b[1] - a[2]*b[2] - a[3]*b[3]),
saturate_cast<_Tp>(a[0]*b[1] + a[1]*b[0] + a[2]*b[3] - a[3]*b[2]),
saturate_cast<_Tp>(a[0]*b[2] - a[1]*b[3] + a[2]*b[0] + a[3]*b[1]),
saturate_cast<_Tp>(a[0]*b[3] + a[1]*b[2] - a[2]*b[1] + a[3]*b[0]));
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator *= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a = a * b;
return a;
}
template<typename _Tp> static inline
Scalar operator * (const Matx<_Tp, 4, 4>& a, const Scalar& b)
{
Matx<double, 4, 1> c((Matx<double, 4, 4>)a, b, Matx_MatMulOp());
return reinterpret_cast<const Scalar&>(c);
}
template<> inline
Scalar operator * (const Matx<double, 4, 4>& a, const Scalar& b)
{
Matx<double, 4, 1> c(a, b, Matx_MatMulOp());
return reinterpret_cast<const Scalar&>(c);
}
我们发现这个时候,就不仅仅是两个向量之间的运算了,还包括向量和标量的运算,我们看简单示例:
Scalar color6 = Scalar(152, 100, 4);
Scalar color7 = Scalar(2, 120, 105);
Mat img = Mat(200, 600, CV_8UC3, color6);
imshow("color6", img);
img = Mat(200, 600, CV_8UC3, color7);
imshow("color7", img);
cout << "color6 * 5 = " << color6 * 5 << endl;
cout << "5 * color6 = " << 5 * color6 << endl;
color6 *= 5;
cout << "color6 *= 5 " << color6 << endl;
color6 = Scalar(152, 100, 4);
color7 = Scalar(2, 120, 105);
cout << "color6 = " << color6 << endl;
cout << "color7 = " << color7 << endl;
cout << "color6 * color7 = " << color6 * color7 << endl;
color6 *= color7;
cout << "color6 *= color7 " << color6 << endl;
img = Mat(200, 600, CV_8UC3, color6);
imshow("color6 * color7", img);
执行结果如下:
4.除法及除赋值运算
减法及减赋值和加法及加赋值是类似的,有如下几个:
template<typename _Tp> static inline
Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, _Tp alpha)
{
return Scalar_<_Tp>(a.val[0] / alpha,
a.val[1] / alpha,
a.val[2] / alpha,
a.val[3] / alpha);
}
template<typename _Tp> static inline
Scalar_<float> operator / (const Scalar_<float>& a, float alpha)
{
float s = 1 / alpha;
return Scalar_<float>(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s);
}
template<typename _Tp> static inline
Scalar_<double> operator / (const Scalar_<double>& a, double alpha)
{
double s = 1 / alpha;
return Scalar_<double>(a.val[0] * s, a.val[1] * s, a.val[2] * s, a.val[3] * s);
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, _Tp alpha)
{
a = a / alpha;
return a;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator / (_Tp a, const Scalar_<_Tp>& b)
{
_Tp s = a / (b[0]*b[0] + b[1]*b[1] + b[2]*b[2] + b[3]*b[3]);
return b.conj() * s;
}
template<typename _Tp> static inline
Scalar_<_Tp> operator / (const Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
return a * ((_Tp)1 / b);
}
template<typename _Tp> static inline
Scalar_<_Tp>& operator /= (Scalar_<_Tp>& a, const Scalar_<_Tp>& b)
{
a = a / b;
return a;
}
我们看个简单示例:
Scalar color6 = Scalar(152, 100, 4);
Scalar color7 = Scalar(2, 120, 105);
cout << "color6 / 5 = " << color6 / 5 << endl;
cout << "5.0 / color6 = " << 5.0 / color6 << endl; //注意不能用: 5 / color6
cout << "color6 / color7 = " << color6 / color7 << endl;
执行结果如下:
5.等于与不等于
等于不等于比较简单,实现如下:
template<typename _Tp> static inline
bool operator == ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b )
{
return a.val[0] == b.val[0] && a.val[1] == b.val[1] &&
a.val[2] == b.val[2] && a.val[3] == b.val[3];
}
template<typename _Tp> static inline
bool operator != ( const Scalar_<_Tp>& a, const Scalar_<_Tp>& b )
{
return a.val[0] != b.val[0] || a.val[1] != b.val[1] ||
a.val[2] != b.val[2] || a.val[3] != b.val[3];
}
6.负号
负号运算符如下:
template<typename _Tp> static inline
Scalar_<_Tp> operator - (const Scalar_<_Tp>& a)
{
return Scalar_<_Tp>(saturate_cast<_Tp>(-a.val[0]),
saturate_cast<_Tp>(-a.val[1]),
saturate_cast<_Tp>(-a.val[2]),
saturate_cast<_Tp>(-a.val[3]));
}
3、代码实战——构建纯色图像
接下来我们走进实战,为我们以后的使用打好基础吧。
大家还记不记的我们最开始学习Mat构建图像,其中有一个类别是使用Scalar构建彩色图像:
class CV_EXPORTS Mat
{
public:
Mat(int rows, int cols, int type, const Scalar& s);
Mat(Size size, int type, const Scalar& s);
Mat(int ndims, const int* sizes, int type, const Scalar& s);
Mat(const std::vector<int>& sizes, int type, const Scalar& s);
};
我们一般都是需要尺寸+类型+颜色。这里,我们用到类型,主要有如下几种:
//! @name Data types
//! primitive types
//! - schar - signed 1 byte integer
//! - uchar - unsigned 1 byte integer
//! - short - signed 2 byte integer
//! - ushort - unsigned 2 byte integer
//! - int - signed 4 byte integer
//! - uint - unsigned 4 byte integer
//! - int64 - signed 8 byte integer
//! - uint64 - unsigned 8 byte integer
//! @{
#if !defined _MSC_VER && !defined __BORLANDC__
# if defined __cplusplus && __cplusplus >= 201103L && !defined __APPLE__
# include <cstdint>
# ifdef __NEWLIB__
typedef unsigned int uint;
# else
typedef std::uint32_t uint;
# endif
# else
# include <stdint.h>
typedef uint32_t uint;
# endif
#else
typedef unsigned uint;
#endif
typedef signed char schar;
#ifndef __IPL_H__
typedef unsigned char uchar;
typedef unsigned short ushort;
#endif
#if defined _MSC_VER || defined __BORLANDC__
typedef __int64 int64;
typedef unsigned __int64 uint64;
# define CV_BIG_INT(n) n##I64
# define CV_BIG_UINT(n) n##UI64
#else
typedef int64_t int64;
typedef uint64_t uint64;
# define CV_BIG_INT(n) n##LL
# define CV_BIG_UINT(n) n##ULL
#endif
#define CV_USRTYPE1 (void)"CV_USRTYPE1 support has been dropped in OpenCV 4.0"
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE
#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))
#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))
#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))
#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))
#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))
#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))
#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))
#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))
//! @}
我们最常用的是CV_8UC3。
示例如下:
Mat img = Mat(300, 300, CV_8UC3, Scalar(0, 0, 255));
imshow("red", img);
img = Mat(300, 300, CV_8UC3, Scalar(0, 255, 0));
imshow("green", img);
img = Mat(300, 300, CV_8UC3, Scalar(255, 0, 0));
imshow("blue", img);
说在后面的话
到这,我们的基础结构就讲完了,还记不记的我们第一次讲的时候说的,这个很重要,这个是真的很重要,因为在以后的实战中,我们经常会用到这些。我只着重讲了其中四个,因为这四个是我们最常用的四个。
以前自学OpenCV的时候,好像没有人单独把这一部分拿出来详细讲解。所以我学的时候,真的是一头雾水。
但现在好啦,我已经有了很好的基础,看源码已经不是问题了。所以我要把我的理解讲给大家,把我的想法告诉大家,如果你是一个初学者,我不希望你跟我当初一样,自己一个人辛苦摸索很久。我希望,你能通过的博客,打好基础,在以后的学习和实战中,能够有个清晰地认识,看别人的代码也知道,是什么意思,就算不理解,也能自己看源码慢慢了解。
计算机视觉领域,我想很多人都跟我一样是在自学吧!这条路上的苦,或许只有我们自己知道。但是请不要担心,因为我会陪你们一直走下去。
就像我们开篇说的,当你再坚持梦想的道路上行走,全世界都是你的帮手!恰好,我就是其中一个!