這篇文章先不去討論opencv的一堆模塊,二郎認爲,能點進來的同學們也只是看到opencv的程序有點懵,而不知道如何下手,至於想要編寫和如何調用都是後話。因此,沒必要先去記塊的名字和用法,應該先記住它的語言規則,如何排列以及函數之間的關係,這些都能幫助大家看懂程序,看懂纔有機會自己寫。
因爲二郎學opencv主要做圖像的,先談一下圖像的儲存
opencv和matlab的儲存方式不同,是按照字節儲存的,用三個字節存儲彩色圖像三通道,順序爲——BGR
儲存單位爲無符號8位整形(CV_8U),表示亮度(一般後面加上C3,表示三通道,C幾表示幾通道)
Mat M(4,5, CV_8UC3, Scalar(155,0,0));//一個3維矩陣,同時也是一個藍色的圖像(RGB),4行5列
cout << "M = " << endl << " " << M << endl;//輸出M矩陣
M=
[155,0,0,155,0,0,155,0,0,155,0,0,155,0,0;
155,0,0,155,0,0,155,0,0,155,0,0,155,0,0;
155,0,0,155,0,0,155,0,0,155,0,0,155,0,0;
155,0,0,155,0,0,155,0,0,155,0,0,155,0,0]
還有一點需要注意,在每個參數出現時,都必須有類型定義或者必須進行類型定義,所以大家會看到很多下面的情況
Mat canvasPart = canvas(Rect(w * 0, 0, w, h)); //Mat
for( int j = 0; j < image.cols; ++j ) //for
在參數賦值的時候,兩個變量的類型不一樣需要添加強制轉換
int* aa; // 指向整型數的指針
float* bb; // 指向浮點數的指針
aa = bb; // 直接賦值會導致編譯錯誤
aa = (int*)bb; // 強制類型轉換後進行賦值
很多情況下會看到,前面也定義類型,後面也定義類型,也屬於這種強制轉換的情況
double d1 = (double) ((i+j)%255);
以及指針
int myArray[3] = { 9, 10, 11 }; // 定義一個數組
int* myIndex = myArray; // 將數組的起始地址賦值給指針pIndex
cout<<"指針指向的地址:"<<myIndex<<endl; // 指針指向的地址
cout<<"指針所指向的數據的值:"<<*myIndex<<endl; // 指向的地址存儲的數值
myIndex++; // 對指針進行加運算,使其指向數組中的下一個值
cout<<"指針指向的地址:"<<myIndex<<endl; // 指針指向的地址
cout<<"指針所指向的數據的值:"<<*myIndex<<endl; // 指向的地址存儲的數值
輸出結果
指針指向的地址:0012FA44
指針所指向的數據的值:9
指針指向的地址:0012FA48 //地址加了4,因爲數據類型是 int-4字節,double-16字節,char-1字節。
指針所指向的數據的值:10
這裏比較繞的是 myindex = 地址, *myindex = 地址位置存儲的數值。
而比較好記的是,我們定義的指針是myindex,它裏面的內容是地址,同時我們完全可以認爲myindex是另一個變量,
內部存儲數值,而只需要注意myindex和myindex互相影響,對myindex操作,*myindex值會變。
這裏有時我們會看到
int a[3][4];
int (*p)[4]; //該語句是定義一個數組指針,指向含4個元素的一維數組。
p=a; //將該二維數組的首地址賦給p,也就是a[0]或&a[0][0]
p++; //該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][],所以數組指針也稱指向一維數組的指針,亦稱行指針。
不太對,剛纔上面還說了,賦值給P的應該是地址,*P應該是數值——這裏把一個數組賦給P,其實就相當於將數組的首地址賦給了P。
模板類
這個東西比較好玩,在很多情況下我們會見到不是我們之前的int c……之類的定意形式了,而是Mat c(當然其包含的內容也不一樣了)
point模板類
Point point;//創建一個2D點對象
point.x = 3;//初始化x座標值
point.y = 2;//初始化y座標值
以及 Mat模板類
Mat R = Mat(3, 2, CV_8UC3);
這個和我在python博文中提到的class類似
Mat R————R屬於Mat模板類,因此它含有該類中的統一屬性
例如:
R.rows代表了R矩陣的行數;R.cols代表R矩陣的列數。
文件格式
cpp、obj、lib、exe的關係
lib 靜態數據連接庫,將obj轉成庫文件,用戶無法直接看到foo文件,實現了代碼的加密。
1.頭文件
一堆的包含,其實只要不發生衝突,你把所有的頭都引進來也行,一般不要這麼做,這樣程序在函數搜索時會變慢。
#include <iostream>
#include <opencv2/opencv_modules.hpp>
#include <opencv2/opencv.hpp>
需要哪個包含進去哪個,這個在很多程序中會出現問題
……文件不存在,……丟失。
頭包含處出現問題:檢查包含格式是否正確,去文檔中找一下有無類似文件。
文中函數不存在:這個函數所在的模塊沒有在頭包含時加進來,這時需要在網上找該模塊來自哪個模塊,將模塊進行頭包含。
頭文件的用途知道這些就可以了。
2.命名空間
在opencv中你時常會看到**::**,這個是在強調命名空間,強調後面的函數在前面的函數的命名空間中。在opencv編程中命名空間有兩種形式
①在程序開始聲明
該方式避免了後面使用函數時需要考慮函數時c++函數還是opencv的函數。
一般這種方法在程序中函數不會混淆時使用
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
②在函數前聲明
System::Math::Sqrt() 相當於System.Math.Sqrt()
cv::
3.數據類型
給原始的數據提供描述,描述符號格式爲:CV_{U|S|F}C(<number_of_channels>)
bit_depth—比特數:8bite,16bites,32bites,64bites
–S--代表—signed int—有符號整形
–U--代表–unsigned int–無符號整形
–F--代表–float---------單精度浮點型
C<number_of_channels>----代表—一張圖片的通道數,
–灰度圖片–grayImg----單通道圖像
–RGB彩色圖像----------3通道圖像
–帶Alph通道的RGB圖像–4通道圖像
比特數爲32bites的3元素浮點元→CV_32FC3
4.opencv2提供matlab類型矩陣創建
矩陣創建
Mat Z = Mat::zeros(4,3, CV_8UC1);
Mat O = Mat::ones(4, 3, CV_8UC1);
Mat E = Mat::eye(4, 3, CV_8UC1);
向量定義
向量定義的類型說明
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
向量創建
Vec3b mylovecolor; //用 color 變量描述一種 RGB 顏色
mylovecolor[0]=255; //B 分量
mylovecolor[1]=0; //G 分量
mylovecolor[2]=0; //R 分量
5.循環 for
相信大家對for一定使用相當多,尤其是在矩陣運算時……在matlab中,for其實可以直接改寫爲矩陣運算,速度可以提高十幾倍。
for( int i = 0; i < myimage.rows; ++i) //可以看出for循環和c++類似,
for( int j = 0; j < image.cols; ++j )
grayim.at<uchar>(i,j) = (i+j)%255;
for …………
6.對圖像像素點進行操作
操作就少不了我們需要知道我們的圖像一下基本信息,我們在matlab中用size確定圖像的行和列
opencv用更簡單的:myimage.rows和myimage.cols來反映行列數
①at
myImage.at<uchar>(j, i) //表示的是 j 行 i 列 的這個像素
myImage.at<uchar>(Point(j, i)) //表示的是 座標(j,i)的像素
uchar value = myImage.at<uchar>(i,j);//讀出第 i 行第 j 列像素值
這裏涉及一個矩陣標和座標的關係(好像沒有矩陣標這一概念,二郎自己想的,方便記憶)
矩陣標:按照人類習慣,在討論矩陣時總是先行後列,因此該標誌也是這個規律
座標:先x後y
在這裏其實不會混淆,因爲不是同一概念,一個是矩陣,一個是座標。大家只需要記住,自己在編程時,是在對矩陣進行操作還是對座標進行操作。
這裏有人會問了(i,j)能表示灰度圖,不能表示彩色圖
把每個像素點看成一個整體(僅有x,y),它的性質(GBR)看成另一個整體。
Vec3b pixel;
pixel[0] = i%255; //Blue
pixel[1] = j%255; //Green
pixel[2] = 0; //Red
colorim.at<Vec3b>(i,j) = pixel;//直接將一個三通道的數賦給像素點
②矩陣的一行或者一列, row()或 col();多行多列,Range()
row,col
A.row(j) = A.row(i)*5+3;
Range(需要強調一下,圖像的第一行和第一列的標號都爲0,而Range參數標號都爲1)
//創建一個單位陣
Mat A = Mat::ones(20, 10, CV_32U);
//提取第 1 到 3 列
Mat B = A(Range::all(), Range(1, 3));
//提取第 10 至 15 行
Mat C = A(Range(10, 15), Range::all());
③指針
指針定義了圖像每一行的首地址,而列以及三維數組均可以由數組表示
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat grayim(600, 800, CV_8UC1);
Mat colorim(600, 800, CV_8UC3);
//遍歷所有像素,並設置像素值
for (int i = 0; i < grayim.rows; ++i)
{
//獲取第 i 行首像素指針
uchar * p = grayim.ptr<uchar>(i);
//對第 i 行的每個像素(byte)操作
for (int j = 0; j < grayim.cols; ++j)
p[j] = (i + j) % 255;
}
//遍歷所有像素,並設置像素值
for (int i = 0; i < colorim.rows; ++i)
{
//獲取第 i 行首像素指針
Vec3b * p = colorim.ptr<Vec3b>(i);
for (int j = 0; j < colorim.cols; ++j)
{
p[j][0] = i % 255; //Blue
p[j][1] = j % 255; //Green
p[j][2] = 0; //Red
}
}
//顯示結果
imshow("grayim", grayim);
imshow("colorim", colorim);
waitKey(0);
return 0;
}
7.point點的模板類
2維點模板類Point_和3維點模板類Point3_
typedef Point2i Point;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point3_<int> Point3i;
實例
Point point;//創建二維點對象
point.x = 3;//初始化x座標值
point.y = 4;//初始化y座標值
等價於
Point point = Point(3, 4);
8.Mat和Mat_
我們會發現,在opencv中出現了很多帶有下劃線的數據類型,這裏先寫一點,其他的以後補充
我們在用指針時
uchar * p = M.ptr(i);
提取地址時每次都需要指定數據類型,比較繁瑣
有了**Mat_**以後就不一樣了
Mat_<uchar> M1 = (Mat_<uchar>&)M;//變量M1聲明的同時確定了M1的指針類型
for( int i = 0; i < M1.rows; ++i)
{
uchar * p = M1.ptr(i);//上面已經聲明,下面便不再需要聲明。