
由于我所在的實驗室主要是研究無人機智能巡檢,是以大多數的項目涉及到圖像識别。說到圖像識别自然少不了著名的圖像處理庫
OpenCV
。是以想趁着這個史無前例的長假期,好好學習一下
OpenCV
。
首先說一下我的開發環境,作業系統Manjaro Linux,開發語言C++,IDE CLion,編譯器gcc 9.2.0,OpenCV版本4.2,cmake。至于為什麼使用Manjaro Linux而不是用大家經常使用Ubuntu,Ubuntu的軟體包管理工具apt中的大多數軟體的版本都過低, 想要使用高版本的軟體,必須自己下載下傳源代碼手動編譯。記得有一次要編譯一個比較新的版本的庫,這個庫必須要求高版本的gcc才能編譯,害得我不得不編譯更高版本的gcc,是以為了避免少折騰,我使用了Manjaro。
關于在C++中使用OpenCV提示一點,我們在CMakeLists.txt中連結庫和包含OpenCV頭檔案的時候,使用下面的文法:
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(xxx ${OpenCV_LIBS})
既然是學習圖像處理,那就自然少不了圖像,這裡我選擇的圖像是我的女神佟麗娅女士在春晚上的一張圖檔,真是太美了。
那麼接下來就是學習了。
讀取圖檔并顯示:
OpenCV提供了imread方法讀取圖檔,這個方法位于imgcodecs.hpp頭檔案中,提供了imshow函數用于顯示圖檔,這個方法位于highgui頭檔案中。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly.jpg");
cv::imshow("yaya", image);
cv::waitKey(0);
imread方法接收兩個參數,第二個參數預設值為IMREADCOLOR,這個參數表示讀取彩色圖像,IMREAD_GRAYSCALE表示讀取灰階圖像。
- channels()方法用于擷取圖檔的通道數。
- rows屬性表示圖像的高度。
- cols屬性表示圖像的寬度。
- at<type>(row, col)擷取某點的像素值,對于灰階圖像type為uchar,對于彩色圖像來說,type為Vec3b。
- data屬性傳回Mat中存儲的像素值。
- type()使用者擷取Mat中的資料類型。
例如如果是CV_8UC3傳回16,如果是CV_8UC1傳回0。
讀取視訊:OpenCV提供了VideoCapture類用于讀取視訊檔案或者攝像頭視訊流。該類位于videoio.hpp頭檔案中。
cv::VideoCapture capture = cv::VideoCapture(0);
if (!capture.isOpened()) {
return -1;
}
char flag = 0;
while (flag != 'q') {
cv::Mat image = {};
capture.read(image);
cv::imshow("cmy", image);
flag = (char)cv::waitKey(30);
}
擷取圖像的一部分資料: types.hpp 頭檔案中提供了Rect類用于指定要截取的部分,被截取的部分通常被成ROI,然後Mat類重載了函數運算符,其中有一個重載接收Rect類型,然後傳回新的Mat對象,對應原圖像的一部分。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly.jpg", cv::IMREAD_COLOR);
cv::Rect rect = cv::Rect(200, 200, 400, 400);
cv::Mat roi = image(rect);
cv::imshow("roi", roi);
cv::waitKey(0);
core.hpp 頭檔案中提供了split和merge方法分别用于圖像通道的分離和合并操作,在OpenCV中,InputArray通常代表傳入的參數為Mat,InputArrayofArrays表示傳入的參數的Mat的數組,通常用vector存儲。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly.jpg", cv::IMREAD_COLOR);
std::vector<cv::Mat> channels;
cv::split(image, channels);
cv::imshow("roi", image);
cv::imshow("roi1", channels.at(0));
cv::imshow("roi2", channels.at(1));
cv::imshow("roi3", channels.at(2));
cv::Mat mergeImage;
cv::merge(channels, mergeImage);
cv::imshow("roi4", mergeImage);
cv::waitKey(0);
邊界填充: core.hpp 頭檔案中提供了copyMakeBorder方法,這個方法接收一個輸入圖像和一個輸出圖像,上下左右需要填充的像素個數,填充方式。
填充方式有下面五種:
- CV_HAL_BORDER_REPLICATE:複制最邊緣的像素點的像素值。
- CV_HAL_BORDER_REFLECT: 321|1234567|765。
- CV_HAL_BORDERREFLECT101: 432|1234567|654,以邊界對稱。
- CV_HAL_BORDER_CONSTANT:使用常數值0進行填充。
- CV_HAL_BORDER_WRAP: 外包裝法 cdefgh | abcdefgh| abcdefg
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi.jpg", cv::IMREAD_COLOR);
cv::Mat img {};
cv::copyMakeBorder(image, img, 50, 50, 50, 50, CV_HAL_BORDER_CONSTANT);
cv::imshow("border", img);
cv::waitKey(0);
數值計算: Mat類重載了各種數學運算符,比如加法,減法,乘法等。
加減法與數字運算表示圖像的每個像素的藍色分量于該數字進行運算。若想每個分量進行加減法,與cv::Scalar(b, g, r)進行加減,乘法于數字進行運算表示每個通道進行乘法運算,與Mat的加法和減法隻能和自己大小相同的Mat進行運算。Mat的乘法表示矩陣的乘法,dot方法實作點乘,結果傳回一個值。
cv::Mat image = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi.jpg", cv::IMREAD_COLOR);
cv::Mat img = image + image;
cv::imshow("image_image", img);
cv::imwrite("/home/cenmmy/CLionProjects/opencv_learning/assets/double_tly.jpg", img);
cv::waitKey(0);
core.hpp 提供了addWeighted函數用于兩個圖像的合并,圖像合并的前提是兩個圖像的大小必須相同,大小不相同的圖像進行合并會報錯。imgproc.hpp頭檔案提供了resize方法用于重新設定圖像的大小。
cv::Mat image1 = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi.jpg", cv::IMREAD_COLOR);
cv::Mat image2 = cv::imread("/home/cenmmy/CLionProjects/opencv_learning/assets/tly_roi1.jpg", cv::IMREAD_COLOR);
cv::Mat image3 {};
cv::resize(image2, image3, cv::Size(400, 400));
// gamma的值會加載融合之後的每個像素點上
cv::addWeighted(image1, 0.2, image3, 0.8, 0, image3);
cv::imshow("image_image", image3);
cv::waitKey(0);
addWeighted方法類似于R = aX + bY + c。