目錄
一、前言
上一篇文章,我們講了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的時候,好像沒有人單獨把這一部分拿出來詳細講解。所以我學的時候,真的是一頭霧水。
但現在好啦,我已經有了很好的基礎,看源碼已經不是問題了。所以我要把我的理解講給大家,把我的想法告訴大家,如果你是一個初學者,我不希望你跟我當初一樣,自己一個人辛苦摸索很久。我希望,你能通過的博客,打好基礎,在以後的學習和實戰中,能夠有個清晰地認識,看別人的代碼也知道,是什麼意思,就算不理解,也能自己看源碼慢慢了解。
計算機視覺領域,我想很多人都跟我一樣是在自學吧!這條路上的苦,或許只有我們自己知道。但是請不要擔心,因爲我會陪你們一直走下去。
就像我們開篇說的,當你再堅持夢想的道路上行走,全世界都是你的幫手!恰好,我就是其中一個!