引言
OpenCV圖像處理在Qt中編譯記錄。
之前一直是在Python中使用OpenCV,Python中使用某些模塊使用pip工具很容易將對應的模塊安裝在系統中。根據項目需求項目都要轉移在國產化中使用,為了適應國產化需求,將代碼轉移到Qt開發環境中,Qt使用OpenCV需要使用源碼編譯對應的庫文件,在這里做個記錄。
OpenCV網站也有對應在Windows環境中已經編譯好的安裝包,我也下載安裝了,但是在使用過程中還是有問題。根據筆者編譯后的心得,OpenCV編譯后的庫是根編譯器有關的,不知道官網中使用的什么編譯器,也只是編譯好了,如果你系統中的編譯環境與那個一樣,那么也許正好就能用了。筆者這里使用的是安裝Qt時自身的編譯環境MinGW730_64。這個環境使用OpenCV預編譯的就用不成,具體啥原因也還不清楚,這里就不過多的折騰,那么就根據自己的開發環境來編譯一套OpenCV的庫。
筆者的環境是Windows 10,Qt5.13.1,OpenCV4.5.4,編譯工具cmake3.25.2。
在Python中使用筆者使用的是OpenCV4.5.5,所以剛開始也想編譯OpenCV4.5.5,但是一直不成功,看到有網友說是:該版本的protobuf版本為3.19,使用的Qt5.13.1自帶的MinGW編譯會報錯誤編譯不過去。這是由于protobuf這個版本太新了,Qt5.13.1中MinGW的gcc版本還不支持這種C++新語法,所以我再去查看OpenCV 4.5.4版本中protobuf版本發現是3.5.1,這個版本舊沒有用到C++語法的新特性,所以編譯沒有出現問題。因此筆者也編譯了4.5.4。
文件準備
下載Qt:https://download.qt.io/archive/qt/5.13/5.13.1/
下載OpenCV源碼:https://codeload.github.com/opencv/opencv/zip/refs/tags/4.5.4
下載opencv_contrib源碼:https://codeload.github.com/opencv/opencv_contrib/zip/refs/tags/4.5.4
下載cmake:https://cmake.org/download/
下載完成后你將得到以下文件
版本
名稱 | 版本 |
---|---|
OS | Windows 10 |
Qt | 5.13.1 |
Qt Creator | 4.10.0 |
MinGW | 730 |
OpenCV | 4.5.4 |
opencv_contrib | 4.5.4 |
cmake | 3.25.2 |
安裝
注意事項
在使用cmake時會需要聯網下載一些文件,所以你的電腦是需要互聯網的。如果是局域網環境不方便上網,那你可以找一臺可以上網的電腦,根據局域網電腦的軟件環境編譯一套OpenCV庫,再通過別的方式拷貝到局域網點中,編譯完成后就是一些庫文件是可以轉移的,當然轉移后也記得配置環境變量。
環境準備
首先你要安裝Qt,這里安裝過程不做介紹,主要要安裝MinGW編譯器。我想你既然要在Qt中使用OpenCV,那么你對Qt的認識肯定可以跳過這一步,如果你也是新手,那么你可以先看一下其他關于Qt安裝的內容。
上文中下載的cmake是一個綠色版的壓縮包,解壓出來就可以用。
OpenCV和opencv_contrib也是2個源碼壓縮包,準備你的目錄將2個壓縮包解壓出來,筆者是在D盤下創建opencv文件夾,將2個壓縮包解壓在這個文件夾內。
需要注意的是路徑不要有空格等特殊字符
完成上述操作后配置環境變量,這里配置2個Qt的MinGW的和一個cmake的,具體路徑按照你的安裝路徑來。
編譯OpenCV
可以先看一下上文中解壓出來的OpenCV源碼文件夾里面的內容,因為后面我們編譯后編譯的文件會在這里,至少你要知道編譯后你得到了哪些文件。
打開cmake的可視化工具cmake-gui.exe
我們要操作的就是如下幾個位置,在where is the source code選擇上文中解壓的OpenCV源碼的文件夾。where to build the binaries選擇你要將OpenCV編譯到哪個文件夾,筆者這里選擇在源碼文件夾中創建一個build文件夾,在每編譯的時候這個文件夾里面還沒有內容。
點擊configure,選擇你的編譯器
下一步選擇C和C++編譯器,這里選擇你Qt安裝文件夾中Qt自帶的gcc和g++。
點擊finish,cmake進入配置環節,配置環節會出現一些錯誤,筆者看很多人說的都不一樣,有很多其實不用管的,比如這里的紅色警告,筆者就沒有管,當然后面還有的是需要修改的。
等待一會出現configuring done日志后就配置完成,此時cmake會有很多紅色警告,這里不用管。
在這里我們添加opencv_contrib擴展,在search位置輸入OPENCV_EXTRA_MODULES_PATH搜索OpenCV擴展模塊,填入opencv_contrib擴展的modules文件夾路徑,
再搜索WITH_QT,默認是不選中的,這里我們選中它
再搜索WITH_OPENGL,默認是不選中的,我們選中它
再點擊configure,進行配置
等一會出現配置完成。
再次進入配置選項界面,有網友說繼續配置QT_QMAKE_EXECUTABLE屬性,但是筆者這里搜索不出來這個屬性,因此跳過。
繼續搜索Qt5_DIR屬性,有網友說是要配置到Qt/Qtxxx/xxx/mingwxxx/bin/qmake.exe這個qmake文件,但是在選擇的時候這個屬性很明顯是需要一個文件夾,而不是一個文件。因此對這個屬性進行說明,筆者這里找到這個屬性,看了一下默認的配置屬性,主機上是有這個文件夾的,因此沒有修改,就使用默認的配置屬性
再點擊configure,等一會出現配置完成并且cmake中沒有紅色錯誤就說明配置成功(如果你看到配置成功日志,那就多執行幾次configure)。
再次查看build文件夾,里面已經有一些文件了。
點擊generate生成工程文件
再看build文件夾又會多一些新的文件
在cmd命令行中進入到build路徑,執行命令mingw32-make進行編譯,這個命令可以添加參數-j 8,表示開啟8線程同時編譯,這樣會增加電腦CPU的使用率從而加快速度。筆者這里使用的編譯命令就是:
mingw32-make -j 8
編譯需要等一會,過程中會出現一些如下的錯誤日志,都不用管
出現如下日志說明構建完成
構建完成后繼續在build路徑下執行命令mingw32-make install進行安裝,此時就默認安裝在../build/install文件夾中。
等待命令執行完成,在看build文件夾中出現了install文件夾,說明安裝成功,后面就可以使用了。
使用
環境變量
將上文中安裝的路徑配置到環境變量中
Qt5.13.1使用
使用Qt Creator創建一個工程,筆者這里命名OpenCV2。
build system選擇默認的qmake
details可以默認
kits選擇注意要選擇MinGW,因為我們使用的是MinGW編譯的OpenCV,這也是我們編譯的目的。而且要注意版本,比如筆者電腦上裝了一個Qt5.13.1,裝了一個Qt5.4.1,所以這里的kits選項就出現了2個MinGW,因為我們是使用Qt5.13.1中的MinGW進行編譯的,所以這里要選擇Qt5.13.1的MinGW。
剩下的就是默認,我們能看到初始化的代碼,并且程序能運行啟動我們的初始化窗口。
在pro文件中添加OpenCV的庫鏈接。在pro文件的最后添加2行鏈接代碼,這里的路徑填寫你自己電腦上面的路徑地址
INCLUDEPATH += D:\opencv\opencv-4.5.4\build\install\include
LIBS += -L D:\opencv\opencv-4.5.4\build\install\x64\mingw\lib\libopencv_*.a
在主界面中添加使用OpenCV的代碼,這里的代碼是使用OpenCV讀取本地的一張圖像,在Qt主窗口中將這個圖像顯示出來。其中使用到一個Mat2QImage函數,這個函數是因為OpenCV讀取的圖像是Mat對象,需要轉成QImage對象。
#include "widget.h"
#include "ui_widget.h"
#include "QDebug"
#include <QImage>
#include <QLabel>
#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("Qt OpenCV 圖像顯示");
//OpenCV讀取本地圖像
cv::Mat src = cv::imread("D:/dr.jpg");
QImage img = Mat2QImage(src);
img = img.scaled(img.width()/2, img.height()/2);
QPixmap pixmap = QPixmap::fromImage(img);
QLabel *label = new QLabel();
label->setParent(this);
label->setPixmap(pixmap);
this->window()->resize(img.width(), img.height());
}
Widget::~Widget()
{
delete ui;
}
/**
* OpenCV 的Mat類型圖像轉Qt中的QImage
* @brief Widget::Mat2QImage
* @param mat
* @return
*/
QImage Widget::Mat2QImage(cv::Mat mat)
{
const unsigned char* data = mat.data;
int width = mat.cols;
int height = mat.rows;
int bytesPerLine = static_cast<int>(mat.step);
switch(mat.type())
{
//ARGB
case CV_8UC4:
{
QImage image(data, width, height, bytesPerLine, QImage::Format_ARGB32);
return image;
}
//BGR
case CV_8UC3:
{
QImage image(data, width, height, bytesPerLine, QImage::Format_RGB888);
//swap blue and red channel
return image.rgbSwapped();
}
//Gray shale
case CV_8UC1:
{
QImage image(data, width, height, bytesPerLine, QImage::Format_Grayscale8);
return image;
}
//
default:
{
//Unsupported format
qDebug()<<"Unsupported cv::Mat type:"<<mat.type()<<", Empty QImage will be returned!";
return QImage();
}
}
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QImage Mat2QImage(cv::Mat src);
};
#endif // WIDGET_H
上述代碼是將圖像顯示在QT程序窗口中,QT程序就一個窗口時只能顯示一個圖片,在調試過程中我們可能會需要顯示多個圖片進行對比,一個窗口就不夠了,所以可以使用OpenCV提供的窗口進行查看,將Mat轉QImage在label中顯示的代碼注釋掉,使用cv::imshow();函數調用OpenCV顯示圖像的方式可以單獨開一個OpenCV窗口顯示圖像。這個窗口與QT程序窗口是獨立的,這個窗口關閉,程序沒有退出,程序窗口關閉,這個窗口會關閉。
#include "widget.h"
#include "ui_widget.h"
#include "QDebug"
#include <QImage>
#include <QLabel>
#include <opencv2/opencv.hpp>
#include <opencv2/core/mat.hpp>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("Qt OpenCV 圖像顯示");
//OpenCV讀取本地圖像
cv::Mat src = cv::imread("D:/normal.png");
// QImage img = Mat2QImage(src);
// img = img.scaled(img.width() / 2, img.height() / 2);
// QPixmap pixmap = QPixmap::fromImage(img);
// QLabel *label = new QLabel();
// label->setParent(this);
// label->setPixmap(pixmap);
// this->window()->resize(img.width(), img.height());
cv::imshow("OpenCV", src);
}
Widget::~Widget()
{
delete ui;
}
/**
* OpenCV 的Mat類型圖像轉Qt中的QImage
* @brief Widget::Mat2QImage
* @param mat
* @return
*/
QImage Widget::Mat2QImage(cv::Mat mat)
{
const unsigned char* data = mat.data;
int width = mat.cols;
int height = mat.rows;
int bytesPerLine = static_cast<int>(mat.step);
switch (mat.type())
{
//ARGB
case CV_8UC4:
{
QImage image(data, width, height, bytesPerLine, QImage::Format_ARGB32);
return image;
}
//BGR
case CV_8UC3:
{
QImage image(data, width, height, bytesPerLine, QImage::Format_RGB888);
//swap blue and red channel
return image.rgbSwapped();
}
//Gray shale
case CV_8UC1:
{
QImage image(data, width, height, bytesPerLine, QImage::Format_Grayscale8);
return image;
}
//
default:
{
//Unsupported format
qDebug() << "Unsupported cv::Mat type:" << mat.type() << ", Empty QImage will be returned!";
return QImage();
}
}
}