本文主要介紹怎樣使用opencv來對圖片進行傅里葉變換,其核心函數是opencv自帶的dft()。DFT這個技術手段是將空間域映射到頻率域中去,在圖像處理有着舉足輕重的地位。這里我們只是得到其變換的結果並看看貧域圖有什么特點。
開發環境:opencv2.4.2+Qt4.8.2+ubuntu12.04+QtCreator2.5
實驗功能:
-
單擊Open Image按鈕,手動選擇打開一副圖片,不管其是否是彩色圖片,這里統一將其轉換成灰度圖像顯示,因為需要加快DFT運算速度。
-
將鼠標移動到圖片顯示區域,單擊圖片,這時會顯示DFT中間結果圖,也就是沒有將低頻域的點移動到圖片的中心而已。顯示的圖片有文字提示。
-
繼續單擊圖片,會顯示最后重新布局后的DFT結果圖,當然也有相應的文字提示。
-
繼續單擊圖片時,則原圖,dft未調整圖,dft調整圖這3幅圖片會進行輪流切換顯示。
-
如果沒有打開圖片就在圖片顯示區域單擊,則會在圖片顯示區域顯示相應的提示文字,提示需先打開圖片。
實驗說明:
-
1維的離散序列的DFT變換公式為:
2維的離散矩陣的DFT變換公式為:
-
QTextBrowser中怎樣相應鼠標單擊事件呢?
用QTextBrowser來顯示圖片(不用顯示文本),當圖片顯示好了后想單擊該圖片轉到某一個圖像處理過程函數中去(這里就是進行離散傅里葉變換),但是在QTextBrowser的singal/slot中找不到其對應的鼠標單擊的信號。因此只能自己寫該信號函數了,可是對應捕捉鼠標單擊的函數庫中應該有,那該怎么寫呢?網上有篇不錯的文章:http://www.embedu.org/Column/Column421.htm
仿照上面的文章,自己重新寫一個類MyTextBrowser,從基類QTextBrowser中繼承而來,然后重寫該類的mousePressEvent函數,其實類似這樣的函數是系統提供的,有點類似MFC中的消息響應機制,在該函數中發出信號即可。這樣當鼠標按下時,就發出了一個clicked()的信號,然后在主界面中響應該信號對應的槽函數就ok了。
當要使用新建的MyTextBrowser類代替原先的QTextBrowser時,只需打開.ui設計文件,在Object欄中找到對應的object,然后右鍵選擇進入Promoted Widgets,add一個MyTextBrwoser即可。
-
int getOptimalDFTSize(int vecsize)
該函數是為了獲得進行DFT計算的最佳尺寸。因為在進行DFT時,如果需要被計算的數字序列長度vecsize為2的n次冪的話,那么其運行速度是非常快的。如果不是2的n次冪,但能夠分解成2,3,5的乘積,則運算速度也非常快。這里的getOptimalDFTSize()函數就是為了獲得滿足分解成2,3,5的最小整數尺寸。很顯然,如果是多維矩陣需要進行DFT,則每一維單獨用這個函數獲得最佳DFT尺寸。
-
void copyMakeBorder(InuptArray src, OutputArray dst, int top , int bottom, int left, int right, int borderType, const Scalar& value=Scalar())
該函數是用來擴展一個圖像的邊界的,第3~6個參數分別為原始圖像的上下左右各擴展的像素點的個數,第7個參數表示邊界的類型,如果其為BORDER_CONSTANT,則擴充的邊界像素值則用第8個參數來初始化。將src圖像擴充邊界后的結果保存在dst圖像中。
-
merge()函數是把多個但通道數組連接成1個多通道數組,而split()函數則相反,把1個多通道函數分解成多個但通道函數。
-
Void magnitude(InputArray x, InputArray y, OutPutArray magnitude)
該函數是計算輸入矩陣x和y對應該的每個像素平方求和后開根號保存在輸出矩陣magnitude中。
-
函數log(InputArray src, OutputArray dst)是對輸入矩陣src中每個像素點求log,保存在輸出矩陣dst的相對應的位置上。
-
因為Qt中顯示圖片,如果圖片類型為亮度圖像,即其像素值為0~1之間,則顯示出來的是全黑的圖像。所以為了似圖片顯示正常,必須全部擴大255被,使之在0~255之間。當然了,如果是opencv自帶的imshow()函數,對於這樣的值在0~1之間圖像它會自動識別,並且有個術語稱之為亮度圖像。
實驗結果:
打開一張圖片,並灰度顯示如下:
DFT中間結果圖,未重新排列的效果:
DFT最終排列后的效果圖:
如果沒有打開圖片單擊圖片顯示區域,提示如下:
實驗主要部分代碼及其注釋(附錄有工程code下載鏈接地址):
MainWindo.cpp:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <iostream> #include <QFileDialog> using namespace std; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->textBrowser->setFont( QFont("Times New Roman", 30) ); display_num = 0; QObject::connect( ui->textBrowser, SIGNAL(clicked()), this, SLOT(on_mytextBrowser_clicked()) ); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_openButton_clicked() { /*讀入圖片並顯示,將圖片以灰度形式讀入*/ QString img_name = QFileDialog::getOpenFileName( this, "Open Image", "../dft", tr("Image Files(*.png *.jpg *.bmp *.jpeg)") ); src = imread( img_name.toAscii().data(), CV_LOAD_IMAGE_GRAYSCALE ); src.copyTo( src_1 ); // putText( src_1, "The original image:", Point(0, 50), 2, 1, Scalar::all(0), 2 ); imwrite( "../dft/src_1.jpg", src_1 ); ui->textBrowser->setFixedHeight( src.rows ); ui->textBrowser->setFixedWidth( src.cols ); ui->textBrowser->clear(); ui->textBrowser->append( "<img src=../dft/src_1.jpg>" ); display_num = 1; } void MainWindow::on_closeButton_clicked() { close(); } void MainWindow::on_mytextBrowser_clicked() { display_num ++; //擴展成為DFT最佳運算尺寸; Mat padded; int opw = getOptimalDFTSize( src.cols ); int oph = getOptimalDFTSize( src.rows ); copyMakeBorder( src, padded, 0, oph-src.rows, 0, opw-src.cols, BORDER_CONSTANT, Scalar::all(0) ); //為計算出的虛數增加1個通道 Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)}; Mat complexI; merge( planes, 2, complexI ); dft( complexI, complexI );//支持圖像原地計算,調用opencv的dtf函數進行DFT計算 //得到賦值圖像,取了對數 split( complexI, planes ); magnitude( planes[0], planes[1], planes[0] ); Mat log_img = planes[0]; log_img += Scalar::all(1); cv::log( log_img, log_img ); log_img = log_img(Rect(0, 0, log_img.cols & -2, log_img.rows & -2 )); //DFT結果 dst = log_img.clone(); cv::normalize( dst, dst, 0, 1, CV_MINMAX ); dst = 255.*dst; if( 1 == display_num ) { if( src.empty() ) { ui->textBrowser->setTextColor( Qt::red ); ui->textBrowser->append("Please open one image first!"); } else { ui->textBrowser->clear(); ui->textBrowser->append( "<img src =../dft/src_1.jpg>"); } } else if( 2 == display_num ) { /*7. 因為Qt中顯示圖片,如果圖片類型為亮度圖像,即其像素值為0~1之間,則顯示出來的是全黑的圖像。 *所以為了似圖片顯示正常,必須全部擴大255被,使之在0~255之間。當然了,如果是opencv自帶的imshow()函數, *對於這樣的值在0~1之間圖像它會自動識別,並且有個術語稱之為亮度圖像。*/ dst.copyTo( dst_1 ); putText( dst_1, "DFT without rearranged image:", Point(0, 50), 3, 0.8, Scalar::all(0), 2 ); imwrite( "../dft/dst_1.jpg", dst_1 ); ui->textBrowser->clear(); ui->textBrowser->append( "<img src=../dft/dst_1.jpg>" ); } else if( 3 == display_num ) { display_num = 0;//原圖,dft未調整圖,dft調整圖3幅圖片進行輪流切換。 dst.copyTo( dst_2 ); int cx = dst_2.cols/2; int cy = dst_2.rows/2; Mat q0( dst_2, Rect(0, 0, cx, cy) ); Mat q1( dst_2, Rect(cx, 0, cx, cy) ); Mat q2( dst_2, Rect(0, cy, cx, cy) ); Mat q3( dst_2, Rect(cx, cy, cx, cy) ); Mat temp; //因為是共用數據的,所以下面的q0,q1,q2,q3的改變也會導致dst_2的改變。 q0.copyTo( temp ); q3.copyTo( q0 ); temp.copyTo( q3 ); q1.copyTo( temp ); q2.copyTo( q1 ); temp.copyTo( q2 ); putText( dst_2, "DFT with rearranged image", Point(0, 50), 1, 2, Scalar::all(0), 2, 8 ); imwrite( "../dft/dst_2.jpg", dst_2 ); ui->textBrowser->clear(); ui->textBrowser->append( "<img src=../dft/dst_2.jpg>" ); } }
mytextbrowser.cpp:
#include "mytextbrowser.h" MyTextBrowser::MyTextBrowser(QWidget *parent) : QTextBrowser(parent) { this->setMouseTracking( true ); } void MyTextBrowser::mousePressEvent(QMouseEvent *ev) { // this->setCursor( Qt::PointingHandCursor );//設置光標的形狀為手型。但感覺貌似不起作用 emit this->clicked(); } void MyTextBrowser::mouseMoveEvent(QMouseEvent *ev) { // this->setCursor( Qt::SizeAllCursor );//設置光標的形狀為手型。 }
實驗總結:
通過本次實驗,對Qt中的信號與槽機制有了更深入的認識。另外對圖像的DFT計算有了進一步的理解。