【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的時候,好像沒有人單獨把這一部分拿出來詳細講解。所以我學的時候,真的是一頭霧水。

但現在好啦,我已經有了很好的基礎,看源碼已經不是問題了。所以我要把我的理解講給大家,把我的想法告訴大家,如果你是一個初學者,我不希望你跟我當初一樣,自己一個人辛苦摸索很久。我希望,你能通過的博客,打好基礎,在以後的學習和實戰中,能夠有個清晰地認識,看別人的代碼也知道,是什麼意思,就算不理解,也能自己看源碼慢慢了解。

計算機視覺領域,我想很多人都跟我一樣是在自學吧!這條路上的苦,或許只有我們自己知道。但是請不要擔心,因爲我會陪你們一直走下去。

就像我們開篇說的,當你再堅持夢想的道路上行走,全世界都是你的幫手!恰好,我就是其中一個!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章