##圖像的基本操作
一直沒有一個系統的時間來深入學習OpenCV,鑒于項目需要,記錄一下一些要點以供日后查閱。?
OpenCV是一個基于(開源)發行的跨平臺計算機視覺庫,可以運行在Linux、Windows和Mac OS操作系統上。其1.0版本于2006年面世,而在2009年又發布了重要的版本:OpenCV2,帶來了新的C++接口;現在,OpenCV3也發布了,據說其Python接口大大增強、并且加入了Python 3.x的支持,還帶來了許多新的提升,不過這并不在這里的討論范圍之內。這里使用的是:OpenCV2.4.9+Qt5.3.2。?
OpenCV庫自2.2版本起就被劃分成多個模塊,在進行開發之前,需要將這些模塊編譯成庫文件,然后在lib文件夾中找到這些模塊:?
opencv_core模塊:其中包含OpenCV基本數據結構、動態數據結構、繪圖與數組操作的相關函數、輔助功能與系統函數、基本的算法函數等核心功能。?
opencv_improc模塊:包含圖像處理函數,主要包含圖像濾波、圖像的幾何變換、直方圖、特征檢測、目標跟蹤等內容。?
opencv_highgui模塊:高層GUI圖形用戶界面,包含媒體的I/O輸入輸出函數,讀寫圖像及視頻的函數,以及操作圖形用戶界面函數。?
opencv_features2d模塊:即2D功能框架,包含興趣點檢測子,描述子以及興趣點匹配框架。?
opencv_calib3d模塊:Calibration(校準)加3D這兩個詞的組合縮寫。這個模塊主要是相機校準和三維重建相關的內容,包含相機標定,雙目幾何估計,物體姿態估計以及立體視覺等函數。?
opencv_video模塊:包含運動估算,特征跟蹤以及前景提取函數與相關的類。?
opencv_objdetect模塊:主要由級聯分類(Cascade Classification)和Latent SVM這兩個部分。其中包括物體檢測函數,如臉部和行人檢測。?
opencv_stitching模塊:OpenCV2.4.0新增的模塊,其主要功能是實現圖像拼接。?
lopencv_superres模塊:即SuperResolution,利用多種算法實現超分辨率技術的相關功能模塊。?
opencv_ml模塊:機器學習模塊,主要包括統計模型 (Statistical Models)、一般貝葉斯分類器 (Normal Bayes Classifier)、K-近鄰 (K-NearestNeighbors)、支持向量機 (Support Vector Machines)、決策樹 (Decision Trees)、提升(Boosting)、梯度提高樹(Gradient Boosted Trees)、隨機樹 (Random Trees)、超隨機樹 (Extremely randomized trees)、期望最大化 (Expectation Maximization)、神經網絡 (Neural Networks)等內容。?
opencv_flann模塊:高維的近似近鄰快速搜索算法庫, 主要由兩個部分組成:快速近似最近鄰搜索和聚類。?
opencv_contrib模塊:第三方代碼,包括一些新添加的不太穩定的可選功能,如新型的人臉識別、立體匹配、人工視網膜模型等技術。?
opencv_nonfree模塊:包含一些擁有專利的算法,如SIFT、SURF函數源碼。?
這些模塊都對有一個單獨的頭文件(位于include文件夾)。在Qt中推薦的聲明方式如下:
~~~
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
~~~
而在Qt中,為了使程序能通過編譯,必須指定OpenCV的庫文件和頭文件的路徑。因此在創建Qt工程之后,需要在后綴為.pro的項目文件中添加用于構建OpenCV的應用信息:
~~~
INCLUDEPATH+=C:\OpenCV\install\include\opencv\
C:\OpenCV\install\include\opencv2\
C:\OpenCV\install\include
LIBS+=C:\OpenCV\lib\libopencv_calib3d249.dll.a\
C:\OpenCV\lib\libopencv_contrib249.dll.a\
C:\OpenCV\lib\libopencv_core249.dll.a\
C:\OpenCV\lib\libopencv_features2d249.dll.a\
C:\OpenCV\lib\libopencv_flann249.dll.a\
C:\OpenCV\lib\libopencv_gpu249.dll.a\
C:\OpenCV\lib\libopencv_highgui249.dll.a\
C:\OpenCV\lib\libopencv_imgproc249.dll.a\
C:\OpenCV\lib\libopencv_legacy249.dll.a\
C:\OpenCV\lib\libopencv_ml249.dll.a\
C:\OpenCV\lib\libopencv_nonfree249.dll.a\
C:\OpenCV\lib\libopencv_objdetect249.dll.a\
C:\OpenCV\lib\libopencv_ocl249.dll.a\
C:\OpenCV\lib\libopencv_video249.dll.a\
C:\OpenCV\lib\libopencv_photo249.dll.a\
C:\OpenCV\lib\libopencv_stitching249.dll.a\
C:\OpenCV\lib\libopencv_superres249.dll.a\
C:\OpenCV\lib\libopencv_ts249.a\
C:\OpenCV\lib\libopencv_videostab249.dll.a
~~~
二、讀取、顯示圖片并對圖片進行簡單的閾值分割?
這里使用Qt設計一個簡單窗口,如圖1所示,該窗口包含打開圖像和處理圖像這兩個按鈕,同時在下方顯示原圖像和處理后的圖像。?

首先創建一個類,主要實現圖像的閾值分割:
colordetector.h:
~~~
#ifndef COLORDETECTOR_H
#define COLORDETECTOR_H
#include <QFileDialog>
#include <QMainWindow>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
class ColorDetector
{
public:
// 最小可接受距離
int minDist;
void setTargetColor(unsigned char red, unsigned char green, unsigned char blue);
cv::Mat process(const cv::Mat &image);
private:
// 目標顏色
cv::Vec3b target;
// 輸出結果圖像
cv::Mat result;
void setTargetColor(cv::Vec3b color);
void colorDetector(const cv::Mat &image, cv::Mat &result);
void setColorDistanceThreshold(int distance);
int getColorDistanceThreshold();
int getDistance(const cv::Vec3b& color);
cv::Vec3b getTargetColor() const;
};
#endif // COLORDETECTOR_H
~~~
colordetector.cpp:
~~~
#include "colordetector.h"
int ColorDetector::getDistance(const cv::Vec3b& color)
{
return abs(color[0] - target[0])+ abs(color[1] - target[1]) + abs(color[1] - target[1]);
}
void ColorDetector::colorDetector(const cv::Mat &image, cv::Mat &result)
{
cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>();
cv::Mat_<uchar>::iterator itout = result.begin<uchar>();
// 對每個像素進行處理,計算每個像素距離目標顏色的距離
for(; it<itend; ++it,++itout)
{
if(getDistance(*it)<minDist) // 判斷距離是否小于最小可接受距離
{
*itout = 0;
}
else *itout = 255;
}
}
void ColorDetector::setColorDistanceThreshold(int distance)
{ // 設置色彩距離閾值,閾值必須為正
if(distance < 0)
distance = 0;
minDist = distance;
}
int ColorDetector::getColorDistanceThreshold(){ // 獲取色彩距離的閾值
return minDist;
}
void ColorDetector::setTargetColor(unsigned char red, unsigned char green, unsigned char blue)
{
// BGR順序
target[2] = red;
target[1] = green;
target[0] = blue;
}
void ColorDetector::setTargetColor(cv::Vec3b color)
{ // 設置需檢測的顏色
target = color;
}
cv::Vec3b ColorDetector::getTargetColor() const
{ // 獲取需檢測的顏色
return target;
}
// Process閾值分割方法的定義
cv::Mat ColorDetector::process(const cv::Mat &image)
{
result.create(image.rows, image.cols, CV_8U);
colorDetector(image, result);
return result;
}
~~~
cv::Mat image表示輸入圖像,result為輸出圖像,使用迭代器遍歷圖像的像素點,每個迭代計算當前像素顏色與設定的目標顏色的距離(用函數getDistance()實現),判斷是否在minDist所定義的容忍度之內。判斷為真,則將當前像素賦值為0(黑色),否則為255(白色)。當然,計算像素點距離的方法有很多,如計算RGB三個分量的歐拉距離。?
在這里,提供兩種設置目標顏色的方法。分別是設置RGB圖像的三個顏色分量,和使用cv::Vec3b來保存顏色值,在函數setTargetColor()中可以看出。
第二步,進入Qt的ui設計模式拖入兩個Push Button和兩個label,調整布局并修改按鈕和label名稱,分別右擊”Open Image”和”Process”轉到槽,編寫槽函數:
~~~
void MainWindow::on_pushButton_clicked() // Open Image槽函數
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), ".",
tr("Image Files(*.png *.jpg *.jpeg *.bmp)"));
image = cv::imread(fileName.toLatin1().data());
if(image.data){
//cv::namedWindow("Original Image");
//cv::imshow("Original Image", image);
cv::Mat imageclone = image.clone(); // 由于要改變顏色通道順序,要避免原始圖像被改變,使用深拷貝
cv::cvtColor(imageclone, imageclone, CV_BGR2RGB); // 改變顏色通道的順序
QImage img1 = QImage((unsigned char*)(imageclone.data), image.cols,
image.rows, QImage::Format_RGB888);
// 顯示在label中
ui -> label ->setPixmap(QPixmap::fromImage(img1));
// 改變label尺寸以自適應圖像
ui -> label ->resize(ui->label->pixmap()->size());
}
else
qDebug() << tr("沒有輸入圖像!");
}
~~~
~~~
void MainWindow::on_pushButton_2_clicked()
{
if(image.data)
{
ColorDetector detect;
// 設置輸入參數
detect.minDist = 100; // 最小可接受距離
detect.setTargetColor(130,190,130); // 選定的閾值
// cv::namedWindow("result");
// cv::imshow("result", detect.process(image));
image = detect.process(image); // 對圖像做閾值分割
cv::cvtColor(image, image, CV_GRAY2RGB); // 改變顏色通道的順序
QImage img = QImage((unsigned char*)(image.data), image.cols, image.rows, QImage::Format_RGB888);
// 顯示在label中
ui -> label_2 ->setPixmap(QPixmap::fromImage(img));
// 改變label尺寸以自適應圖像
ui -> label_2 ->resize(ui->label_2->pixmap()->size());
}
else
qDebug() << tr("無輸入圖像,無法進行閾值分割");
}
~~~
opencv2中用于存儲圖像數據為Mat類型,而在opencv1中使用的是IplImage,其優點在于Mat是一個類,定義的類類型可以自動分配和釋放內存空間,而IplImage需手動為其分配和釋放內存空間,當圖像較多時,可能會造成內存泄露。?
在判斷圖片是否加載成功時使用了image.data,這是一個指向已分配的內存塊的指針,當圖片沒有加載進來為NULL。
最后對main函數進行簡單修改即可:
~~~
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.setWindowTitle("簡單的閾值分割");
w.show();
return a.exec();
}
~~~
結果:?

三:圖像復制中的深淺拷貝?
需要注意的一點小問題,在復制圖像時需要考慮是否想更改原圖像,這需要牽涉到淺拷貝和深拷貝的概念:?
淺拷貝的主要方式:?
Mat A;?
A = image ; // 第一種方式?
Mat B(image); // 第二種方式?
這兩種方式之所以稱為淺拷貝,是因為它們雖然有不同的名稱,但是它們指向相同的內存空間。當其中一個圖像矩陣發生變化時,另外一個也會發生變化。?
深拷貝的主要方式:?
Mat A,B;?
A = image.clone(); // 第一種方式?
image.copyTo(B); // 第二種方式?
深拷貝是真正的復制了一個新的圖像矩陣,此時image,A,B三者無論誰發生了改變,其他都不受影響。
最后感謝博客:[http://blog.csdn.net/lu597203933](http://blog.csdn.net/lu597203933)?給出的一些講解。
- 前言
- Win8.1下OpenCV2.4.9+Qt5.3.2開發環境搭建
- OpenCV2學習筆記(一)
- OpenCV2學習筆記(二)
- OpenCV2學習筆記(三)
- OpenCV2學習筆記(四)
- OpenCV2學習筆記(五)
- OpenCV2學習筆記(六)
- OpenCV2學習筆記(七)
- OpenCV2學習筆記(八)
- OpenCV2學習筆記(九)
- OpenCV2學習筆記(十)
- OpenCV2學習筆記(十一)
- OpenCV2學習筆記(十二)
- OpenCV2學習筆記(十三)
- OpenCV2學習筆記(十四)
- OpenCV2學習筆記(十五)
- OpenCV2學習筆記(十六)
- OpenCV2學習筆記(十七)
- OpenCV2學習筆記(十八)
- OpenCV2學習筆記(十九)
- OpenCV2學習筆記(二十)
- OpenCV2學習筆記(二十一)
- OpenCV2學習筆記(二十二)