C++学习笔记——opencv2模块(图像处理)
生活随笔
收集整理的這篇文章主要介紹了
C++学习笔记——opencv2模块(图像处理)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
C++學(xué)習(xí)筆記——opencv2模塊(圖像處理)
- 1. 使用方式
- 2. 注意點(diǎn)
- 3. 圖片讀寫與屬性
- 3.1 圖片基本操作
- 3.2 遍歷像素點(diǎn)
- 3.3 提取指定像素值
- 3.4 初始化為0的矩陣
- 3.5 計(jì)算某個(gè)灰度切片區(qū)域的像素和
- 4. 切片,淺拷貝與深拷貝
- 5. 圖片操作函數(shù)
- 5.1 圖片翻轉(zhuǎn)
- 5.2 二值化
- 5.3 輪廓檢測(cè)
- 5.4 計(jì)算均值與標(biāo)準(zhǔn)差
- 6. 一些代碼樣例
- 6.1 去畸變代碼
- 6.2 ORB特征匹配代碼
- 6.3 將特征數(shù)據(jù)寫入磁盤/從磁盤讀取特征數(shù)據(jù)并匹配輸出
- 7. cv::Mat與Eigen::Matrix互相轉(zhuǎn)換
- 8. 在窗口中顯示放大后的圖像
用于計(jì)算圖像處理的opencv2,只不過這次用的不是python的版本,而是C++的版本。
參考書籍:《視覺SLAM十四講-從理論到實(shí)踐》——高翔
1. 使用方式
CMakeLists.txt寫法樣例:
# 添加c++11標(biāo)準(zhǔn)支持 set(CMAKE_CXX_FLAGS "-std=c++11")# cmake最低版本需求 cmake_minimum_required(VERSION 2.8)# 創(chuàng)建項(xiàng)目 project( test )# 創(chuàng)建執(zhí)行程序 add_executable( cv2_test cv2_test.cpp )# 尋找opencv find_package( OpenCV REQUIRED ) # 添加頭文件 include_directories(${OpenCV_INCLUDE_DIRS}) # 鏈接opencv庫(kù) target_link_libraries( cv2_test ${OpenCV_LIBS} )2. 注意點(diǎn)
讀取數(shù)據(jù)的數(shù)據(jù)類型使用unsigned char:因?yàn)閕nt類型在不同操作系統(tǒng)平臺(tái)下長(zhǎng)度不同,而uchar類型在所有平臺(tái)上長(zhǎng)度都是一樣的。
3. 圖片讀寫與屬性
3.1 圖片基本操作
#include <iostream>// 導(dǎo)入cv2模塊 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp>// 在不考慮細(xì)致實(shí)現(xiàn)的情況下,粗暴導(dǎo)入所有cv2模塊 // #include <opencv2/opencv.hpp>using namespace std; using namespace cv;int main(int argc, char **argv) {// 圖片路徑,可以寫字符串,也可以用argv從啟動(dòng)參數(shù)導(dǎo)入// 注意在IDE里面編譯時(shí)需要考慮到當(dāng)前運(yùn)行路徑在build文件夾內(nèi)string img_path = "../test.jpeg";// 創(chuàng)建圖片變量cv::Mat image;// 讀圖片image = cv::imread(img_path);// 驗(yàn)證圖片是否正確讀取,如果data為空指針說明讀取失敗if (image.data == nullptr) {cerr << "圖片" << img_path << "讀取失敗。" << endl;return -1;}// 打開一個(gè)新窗口,顯示讀取成功的圖片cv::imshow("窗口的標(biāo)題", image);// 保持窗口打開等待關(guān)閉cv::waitKey(0);// 輸出圖片類型,直接輸出會(huì)是一個(gè)整數(shù)cout << "圖片類型為:" << image.type() << endl;// 但是作為哈希值可以直接用來判斷圖片類型// CV_8UC3表示8bit,unsigned int,圖像3通道,灰度圖就是1通道// float 32位,double64位// S(signed int),U(unsigned int),F(float)if (image.type() == CV_8UC3) {cout << "這是彩色8比特圖片。" << endl;}else {cout << "這不是彩色8比特圖片。" << endl;}// 獲取圖片尺寸信息cout << "寬度:" << image.cols;cout << ",高度:" << image.rows;cout << ",頻道數(shù):" << image.channels() << endl;// 創(chuàng)建新圖像[以灰度圖為例]// int rows = image.rows, cols = image.cols;// cv::Mat image_new = cv::Mat(rows, cols, CV_8UC1);// 遍歷圖像與指針for (size_t y = 0; y < image.rows; y++) {// 行指針類型cv::Mat::ptr// 使用每一行的頭部指針作為后續(xù)遍歷基礎(chǔ)unsigned char *row_ptr = image.ptr<unsigned char>(y);// 然后對(duì)每一行進(jìn)行遍歷for (size_t x = 0; x < image.cols; x++) {// 指向一個(gè)具體的像素點(diǎn)的指針// 按照【像素?cái)?shù)*通道屬性】向后移動(dòng)unsigned char *data_ptr = &row_ptr[x*image.channels()];// 遍歷每一個(gè)通道for (int c = 0; c != image.channels(); c++) {unsigned char data = data_ptr[c];// 顯示時(shí)需要做個(gè)類型轉(zhuǎn)換修改為int類型cout << (int)data << " ";}// 以每一行一個(gè)像素點(diǎn)的方式顯示// 例如:217 215 221cout << endl;}}// 直接取出指定像素// 此處彩色圖片,灰色單通道圖片則使用image.at<uchar>// 第0行第0列的像素cv::Vec3b pixel = image.at<cv::Vec3b>(0, 0);cout << "第一個(gè)像素點(diǎn)是:" << pixel << endl;cout << "第一個(gè)像素點(diǎn)的第一個(gè)通道是:" << (int)pixel[0] << endl;// 保存圖片cv::imwrite( "../test2.jpeg", image);}運(yùn)行得到:
【圖就不放了】
3.2 遍歷像素點(diǎn)
較慢的遍歷圖片中每個(gè)像素點(diǎn)(灰度)
for (auto p = img.begin<uchar>(); p != img.end<uchar>(); ++p){uchar value = uchar((*p));}更快的像素點(diǎn)指針遍歷法(灰度):
for (int i = 0; i <img.cols * img.rows; i++){uchar value = img.data[i];}3.3 提取指定像素值
用at的方法按照行列提取像素值(慢)
// 遍歷行for (int row = 0; row < grayImg.rows; row++){// 遍歷列for (int col = 0; col < grayImg.cols; col++){ point_value = int(grayImg.at<uchar>(row, col));}}用指針按照行列提取像素值(快)
point_value = float(grayImg.data[row * grayImg.cols + col]);3.4 初始化為0的矩陣
cv::Mat temp_img; // 初始化為0 temp_img = cv::Mat::zeros(rows, cols, CV_8UC1);3.5 計(jì)算某個(gè)灰度切片區(qū)域的像素和
float sum_bright = cv::sum(GrayImg(cv::Rect(col_min,row_min,w,h)))[0]4. 切片,淺拷貝與深拷貝
#include <iostream>// 導(dǎo)入cv2模塊 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp>// 在不考慮細(xì)致實(shí)現(xiàn)的情況下,粗暴導(dǎo)入所有cv2模塊 // #include <opencv2/opencv.hpp>using namespace std; using namespace cv;int main(int argc, char **argv) {// 圖片路徑,可以寫字符串,也可以用argv從啟動(dòng)參數(shù)導(dǎo)入// 注意在IDE里面編譯時(shí)需要考慮到當(dāng)前運(yùn)行路徑在build文件夾內(nèi)string img_path = "../test.jpeg";// 創(chuàng)建圖片變量cv::Mat image;// 讀圖片image = cv::imread(img_path);// 驗(yàn)證圖片是否正確讀取,如果data為空指針說明讀取失敗if (image.data == nullptr) {cerr << "圖片" << img_path << "讀取失敗。" << endl;return -1;}// 淺拷貝,并不會(huì)復(fù)制內(nèi)存數(shù)據(jù)cv::Mat image_shallow_copy = image;// “切片”,將左上角100*100的塊置零image_shallow_copy( cv::Rect(0, 0, 100, 100)).setTo(0);// 顯示原圖片的像素,發(fā)現(xiàn)被改變了cout << "淺拷貝圖片的第一個(gè)像素:" << image_shallow_copy.at<cv::Vec3b>(0, 0) << endl;cout << "原始圖片的第一個(gè)像素:" << image.at<cv::Vec3b>(0, 0) << endl;// 深拷貝,會(huì)復(fù)制內(nèi)存數(shù)據(jù)cv::Mat image_deep_copy = image.clone();// “切片”,將左上角100*100的塊置255image_deep_copy( cv::Rect(0, 0, 100, 100)).setTo(255);// 顯示原圖片的像素,發(fā)現(xiàn)被改變了cout << "深拷貝圖片的第一個(gè)像素:" << image_deep_copy.at<cv::Vec3b>(0, 0) << endl;cout << "原始圖片的第一個(gè)像素:" << image.at<cv::Vec3b>(0, 0) << endl; }運(yùn)行得到:
淺拷貝圖片的第一個(gè)像素:?0, 0, 0? 原始圖片的第一個(gè)像素:?0, 0, 0? 深拷貝圖片的第一個(gè)像素:?255, 255, 255? 原始圖片的第一個(gè)像素:?0, 0, 0?5. 圖片操作函數(shù)
5.1 圖片翻轉(zhuǎn)
// 翻轉(zhuǎn)圖片 // 水平翻轉(zhuǎn) cv::flip(image_input, image_output, 1); // 垂直翻轉(zhuǎn) cv::flip(image_input, image_output, 0); // 水平垂直翻轉(zhuǎn) cv::flip(image_input, image_output, -1);5.2 二值化
// 固定閾值 cv::threshold(img, binary, Threshold, 255, cv::THRESH_BINARY);// 大津法 int Threshold = cv::threshold(img, binary, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);5.3 輪廓檢測(cè)
// 對(duì)二值化圖像進(jìn)行輪廓檢測(cè) std::vector< std::vector< cv::Point> > contours; cv::findContours(bianry, contours, cv::noArray(),cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);5.4 計(jì)算均值與標(biāo)準(zhǔn)差
cv::meanStdDev()6. 一些代碼樣例
6.1 去畸變代碼
來自參考書籍:《視覺SLAM十四講-從理論到實(shí)踐》——高翔
#include <opencv2/opencv.hpp> #include <string> using namespace std;// 也可以直接通過庫(kù)函數(shù)cv::Undisort()實(shí)現(xiàn)// 圖片路徑 string image_file = "../distorted.png";int main(int argc, char **argv) {// 配置畸變參數(shù)double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;// 配置內(nèi)部參數(shù)double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;// CV_8UC1灰度圖cv::Mat image = cv::imread(image_file, 0);// 定義去畸變后的新圖像int rows = image.rows, cols = image.cols;cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1);// 計(jì)算去畸變后的圖像for (int v=0; v<rows; v++) {for (int u=0; u<cols; u++) {// 將當(dāng)前的(u, v)對(duì)應(yīng)到畸變圖像中的(u_distorted, v_distorted)double x = (u-cx) / fx, y = (v-cy) / fy;double r = sqrt(x * x + y * y);double x_distorted = x * (1+ k1*r*r + k2*r*r*r*r) + 2*p1*x*y + p2*(r*r+2*x*x);double y_distorted = y * (1+ k1*r*r + k2*r*r*r*r) + 2*p2*x*y + p1*(r*r+2*y*y);double u_distorted = fx * x_distorted + cx;double v_distorted = fy * y_distorted + cy;// 使用最近鄰插值進(jìn)行賦值if (u_distorted >= 0 && v_distorted >=0 && u_distorted < cols && v_distorted < rows) {image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);}else {image_undistort.at<uchar>(v, u) = 0;}}}// 顯示去畸變圖cv::imshow("distored", image);cv::imshow("undistored", image_undistort);cv::waitKey();return 0; }6.2 ORB特征匹配代碼
來自參考書籍:《視覺SLAM十四講-從理論到實(shí)踐》——高翔
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp> #include <chrono>using namespace std; using namespace cv;// 使用opencv實(shí)現(xiàn)ORB特征匹配int main(int argc, char **argv) {// 為了在ide里面方便運(yùn)行,暫時(shí)改為寫死的固定路徑,注釋掉原作者的這部分代碼// if (argc != 3) {// cout << "usage: feature_extraction img1 img2" << endl;// return 1;// }// 讀取圖像,修改為從固定路徑讀取// 因?yàn)樵赽uild文件夾內(nèi),因此進(jìn)入上級(jí)目錄Mat img_1 = imread("../1.png", CV_LOAD_IMAGE_COLOR);Mat img_2 = imread("../2.png", CV_LOAD_IMAGE_COLOR);// 用斷言確保兩張圖片都已經(jīng)成功被讀取assert(img_1.data != nullptr && img_2.data != nullptr);// 創(chuàng)建兩組關(guān)鍵點(diǎn)的vectorstd::vector<KeyPoint> keypoints_1, keypoints_2;// 創(chuàng)建兩個(gè)矩陣Mat descriptors_1, descriptors_2;// 初始化// 提取FAST關(guān)鍵點(diǎn)用的工具Ptr<FeatureDetector> detector = ORB::create();// 提取BRIEF描述子用的工具Ptr<DescriptorExtractor> descriptor = ORB::create();// 使用漢明距離作為匹配標(biāo)準(zhǔn)Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");// 第一步:檢測(cè) Oriented FAST 角點(diǎn)位置// 檢測(cè)半徑為3的圓上是否有足夠多的點(diǎn)和當(dāng)前點(diǎn)比起來亮度差異超過一定比例// 基于圖像金字塔的上下層來構(gòu)建尺度不變性// 基于灰度質(zhì)心法構(gòu)建旋轉(zhuǎn)不變性chrono::steady_clock::time_point t1 = chrono::steady_clock::now();detector->detect(img_1, keypoints_1);detector->detect(img_2, keypoints_2);// 第二步:根據(jù)FAST角點(diǎn)位置計(jì)算 BRIEF 描述子descriptor->compute(img_1, keypoints_1, descriptors_1);descriptor->compute(img_2, keypoints_2, descriptors_2);// 結(jié)束檢測(cè)部分,計(jì)算消耗時(shí)間chrono::steady_clock::time_point t2 = chrono::steady_clock::now();chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);cout << "extract ORB cost = " << time_used.count() << " seconds. " << endl;Mat outimg1;// 繪制并顯示特征圖drawKeypoints(img_1, keypoints_1, outimg1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);imshow("ORB features", outimg1);// 第三步:對(duì)兩幅圖像中的BRIEF描述子進(jìn)行匹配,使用 Hamming 距離作為匹配標(biāo)準(zhǔn)vector<DMatch> matches;t1 = chrono::steady_clock::now();// 匹配2張圖像的描述子matcher->match(descriptors_1, descriptors_2, matches);// 再次計(jì)算消耗時(shí)間t2 = chrono::steady_clock::now();time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);cout << "match ORB cost = " << time_used.count() << " seconds. " << endl;// 第四步:匹配點(diǎn)對(duì)篩選// 計(jì)算vector<DMatch> matches中的最小距離和最大距離// 此處使用了C++中的lambda函數(shù)[](){}auto min_max = minmax_element(matches.begin(), matches.end(), [](const DMatch &m1, const DMatch &m2) { return m1.distance < m2.distance; });double min_dist = min_max.first->distance;double max_dist = min_max.second->distance;printf("-- Max dist : %f \n", max_dist);printf("-- Min dist : %f \n", min_dist);//當(dāng)描述子之間的距離大于兩倍的最小距離時(shí),即認(rèn)為匹配有誤.但有時(shí)候最小距離會(huì)非常小,設(shè)置一個(gè)經(jīng)驗(yàn)值30作為下限.// 將所有符合條件的匹配放入了一個(gè)新的vector容器內(nèi)std::vector<DMatch> good_matches;for (int i = 0; i < descriptors_1.rows; i++){if (matches[i].distance <= max(2 * min_dist, 30.0)){good_matches.push_back(matches[i]);}}// 第五步:繪制匹配結(jié)果Mat img_match;Mat img_goodmatch;// 繪制所有匹配drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_match);// 繪制篩選后的匹配drawMatches(img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch);// 顯示圖片imshow("all matches", img_match);imshow("good matches", img_goodmatch);waitKey(0);return 0; }6.3 將特征數(shù)據(jù)寫入磁盤/從磁盤讀取特征數(shù)據(jù)并匹配輸出
自己寫的代碼,記錄一下
將圖片與std::vector<cv::Point2f>類型的特征數(shù)據(jù)存儲(chǔ)到本地:
從磁盤讀取圖片與float型的特征點(diǎn)坐標(biāo),用ORB進(jìn)行匹配并輸出匹配連接圖像:
#include <iostream> #include <string> #include <fstream> #include <vector>#include <opencv2/opencv.hpp>using namespace std; using namespace cv;int main(int argc, char **argv) {string save_path = "存儲(chǔ)目錄";// 從磁盤上讀圖片cv::Mat cur_img = cv::imread(save_path + "cur_img.jpg", 0);cv::Mat forw_img = cv::imread(save_path + "forw_img.jpg", 0);// 從磁盤上讀取特征點(diǎn),并以vector<cv::KeyPoint >的形式儲(chǔ)存string temp; int pos;float x;float y;ifstream cur_pts_file(save_path + "cur_pts.txt"); // 以vector<cv::KeyPoint >的形式儲(chǔ)存vector<cv::KeyPoint > cur_pts;if (!cur_pts_file.is_open()) { cout << "未成功打開文件cur_pts.txt" << endl; } while(getline(cur_pts_file, temp)) { if (temp.length() != 0) {// 獲得字符串// 找到用于分割的空格位置pos = temp.find(" "); // x坐標(biāo)x = stof(temp.substr (0, pos));// y坐標(biāo)y = stof(temp.substr (pos+1, temp.length()-pos-1));// 將<cv::Point2f >轉(zhuǎn)換為<cv::KeyPoint >KeyPoint temp_keypoint;temp_keypoint.pt = Point2f(x, y);// 將<cv::KeyPoint >放入vectorcur_pts.push_back(temp_keypoint);}} cur_pts_file.close(); ifstream forw_pts_file(save_path + "forw_pts.txt"); vector<cv::KeyPoint > forw_pts;if (!forw_pts_file.is_open()) { cout << "未成功打開文件forw_pts.txt" << endl; } while(getline(forw_pts_file, temp)) { if (temp.length() != 0) {// 獲得字符串// 找到分割的空格位置pos = temp.find(" "); // x坐標(biāo)x = stof(temp.substr (0, pos));// y坐標(biāo)y = stof(temp.substr (pos+1, temp.length()-pos-1));// 將<cv::Point2f >轉(zhuǎn)換為<cv::KeyPoint >KeyPoint temp_keypoint;temp_keypoint.pt = Point2f(x, y);// 將<cv::KeyPoint >放入vectorforw_pts.push_back(temp_keypoint);}} forw_pts_file.close(); // // 打印行數(shù)進(jìn)行驗(yàn)證是否都加載正常// cout << cur_pts.size() << endl;// cout << forw_pts.size() << endl;// 創(chuàng)建匹配vector<DMatch> matches;BFMatcher bfMatcher(NORM_L2);//計(jì)算特征點(diǎn)描述子,特征向量提取Mat dst1, dst2;// 使用SIFT會(huì)產(chǎn)生double free or corruption (!prev) 報(bào)錯(cuò)// Ptr<SiftDescriptorExtractor> descriptor = SiftDescriptorExtractor::create();Ptr<DescriptorExtractor> descriptor = ORB::create();// 計(jì)算描述子descriptor->compute(cur_img, cur_pts, dst1);descriptor->compute(forw_img, forw_pts, dst2);// 進(jìn)行匹配bfMatcher.match(dst1, dst2, matches);// 用于輸出的圖像cv::Mat out_image;// 繪制輸出圖像drawMatches(cur_img, cur_pts, forw_img, forw_pts, matches, out_image);// 顯示輸出圖像imshow("連線圖像", out_image);waitKey(0);return 0; }7. cv::Mat與Eigen::Matrix互相轉(zhuǎn)換
需要導(dǎo)入額外庫(kù)文件
#include <opencv2/core/eigen.hpp>互相轉(zhuǎn)換
// 將cv::Mat轉(zhuǎn)換為Eigen::Matrix cv::cv2eigen(mat_cv, matrix_eigen);// 將Eigen::Matrix轉(zhuǎn)換為cv::Mat cv::eigen2cv(matrix_eigen, mat_cv);8. 在窗口中顯示放大后的圖像
cv::namedWindow("show", 0); cv::resizeWindow("show", cv::Size(1920, 1080)); cv::imshow("show", img);總結(jié)
以上是生活随笔為你收集整理的C++学习笔记——opencv2模块(图像处理)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux账号前有个base,安装 ac
- 下一篇: c++ 中——fatal error: