【opencv4.3.0教程】06之基础结构3之Scalar_结构详解

目录

一、前言

二、温故知新——Rect_

1、定义与成员变量

2、构造函数

3、常用方法

三、Scalar_

1、定义

1.构造函数

2.基本操作

3.使用时的数据类型

2、常用方法

1.加法及加赋值运算

2.减法及减赋值运算

3.乘法及乘赋值运算

4.除法及除赋值运算

5.等于与不等于

6.负号

3、代码实战——构建纯色图像

说在后面的话


一、前言

上一篇文章,我们讲了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.赋值操作;

还有很多常用操作,因为内容比较多,就不再在这里说明啦!大家可以打开上一篇博客去详细查看哦:

Rect_类常用方法

三、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的时候,好像没有人单独把这一部分拿出来详细讲解。所以我学的时候,真的是一头雾水。

但现在好啦,我已经有了很好的基础,看源码已经不是问题了。所以我要把我的理解讲给大家,把我的想法告诉大家,如果你是一个初学者,我不希望你跟我当初一样,自己一个人辛苦摸索很久。我希望,你能通过的博客,打好基础,在以后的学习和实战中,能够有个清晰地认识,看别人的代码也知道,是什么意思,就算不理解,也能自己看源码慢慢了解。

计算机视觉领域,我想很多人都跟我一样是在自学吧!这条路上的苦,或许只有我们自己知道。但是请不要担心,因为我会陪你们一直走下去。

就像我们开篇说的,当你再坚持梦想的道路上行走,全世界都是你的帮手!恰好,我就是其中一个!

猜你喜欢

转载自blog.csdn.net/shuiyixin/article/details/106111460