本文主要讲讲怎样对Mat矩阵进行mask操作,其实也就是对Mat矩阵的滤波操作,俗称卷积,参考文献为opencv自带的tutiol及其code.
开发环境:ubuntu12.04+opencv2.4.2+Qt4.8.2+QtCreator2.5.
实验功能:
单击Open image按钮,手动选择所需滤波的原图片。
单击Setting按钮,弹出对话框,选择滤波所用的模式,这里有2种模式,分别为自己实现滤波功能和采用opencv自带的滤波函数filter2D实现。
单击Process按钮,实现图片滤波过程,并将滤波结果显示在图片窗口中。
滤波完成后,图片下方法显示滤波所采用的模式及其花耗的时间。可以方便对2种模式的效率进行对比。
实验说明:
这次实验图片的显示与前几篇博客中采用的方法不同,前几次是将图片显示在QLabel中,这次是显示在QTextBrowser中。当在QLabel中显示时,直接SetPixmap将图片内容显示上去;而在QTextBrowser中,是加载图片路径名,然后直接挂载的(不需要使用opencv中的imread函数)。
opencv中的saturate_cast函数,其实就是一个类型转换函数,将圆弧括号中的类型转换成尖括号中的类型。
opencv中的setTo函数是将指定的元素设置为指定的值,可以使用mask矩阵掩码。
在Qt中,如果第1个窗口类要调用第2个窗口类,且我们需要在进行第2个窗口操作时改变第1个窗口类中某些变量,这时我们不能直接改变,因为是第1个类调用第2个类,所以不能由第2个类反过来改变第1个类的值,当遇到这种情况时,我们只需要将第2个类中改变的值保存在其public中即可。然后当第1个类的某个变量需要改变时,直接用第2个类中对应的public变量赋值即可,具体可见下面给出的代码。
在类的构造函数中建立的变量,该变量的生命周期是整个类的生命周期,所以该类中其它所有函数都可以使用该变量。
实验结果:
打开原始图片后:
模式设置对话框:
自己实现mask结果:
用opencv自带filter2D实现结果:
实验主要部分代码及注释(附录有工程code下载链接):
mainwindow.cpp:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QtGui> #include <QTextDocument> #include <iostream> using namespace std; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); mask_mode = 1; ui->textBrowser->setStyleSheet( "background-color:black" );//这种参数更简单 ui->textBrowser->setTextColor( Qt::green ); set = new setting( this );//在构造函数中开辟的内存作用与该类的所有函数 } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_closeButton_clicked() { close(); } void MainWindow::on_openButton_clicked() { QString img_name = QFileDialog::getOpenFileName( this, "Open Image", "../mask/", tr("Image Naems( *.png *.jpeg *.jpg *.bmp )")); img = imread( img_name.toAscii().data() ); img1 = img.clone(); cvtColor( img1, img1, CV_BGR2RGB ); QImage qimg = QImage( (const unsigned char*)img1.data, img1.cols, img1.rows, QImage::Format_RGB888 ); //这是在textBrowser里显示图片的另一种方法。<img src='***'>这些都是固定的. //如果是string后要接arg,则不能直接""后面接,可以采用tr或QString括起来。 // ui->textBrowser->append( tr("<img src='%1'>").arg(img_name ); ui->textBrowser->clear(); ui->textBrowser->append( tr("<img src='%1'>").arg( img_name ) ); ui->textBrowser->append( tr("the mask mode is realize by myself......") ); } int MainWindow::on_settingButton_clicked() { // set = new setting( this );//如果在这个地方进行new的话,则不能保存上次设置的结果,因此应该在构造函数中new set->show(); } void MainWindow::on_processButton_clicked() { mask_mode = set->set_mask_mode; img1 = img.clone(); Mat img2; img2.create( img1.size(), img1.type() ); CV_Assert( img1.depth() == CV_8U ); if( 1 == mask_mode ) { double t = getTickCount(); int rows = img1.rows; int cols = img1.cols; int channels = img1.channels(); for( int i = 1; i < rows-1; i++ ) { uchar *current = img1.ptr<uchar>( i ); uchar *previous = img1.ptr<uchar>( i-1 ); uchar *next = img1.ptr<uchar>( i+1 ); uchar *output = img2.ptr<uchar>( i ); for( int j = channels; j < channels*(cols-1); j++ ) { *output++ = saturate_cast<uchar>( 5*current[j]-current[j-channels]-current[j+channels] -previous[j]-next[j]); } } img2.row(0).setTo(Scalar(0)); img2.row(rows-1).setTo(Scalar(0)); img2.col(0).setTo(Scalar(0)); img2.col(cols-1).setTo(Scalar(0)); t = (getTickCount()-t)/getTickFrequency()*1000; imwrite( "lena1.jpg", img2 ); ui->textBrowser->clear(); ui->textBrowser->append(tr("<img src=./lena1.jpg>")); ui->textBrowser->append( tr("the mask mode is realize by myself......") ); ui->textBrowser->append( tr("the time running cost %1ms").arg(t) ); } else if( 2 == mask_mode ) { double t = getTickCount(); Mat kernel = (Mat_<char>(3,3)<<0,-1,0,-1,5,-1,0,-1,0); filter2D( img1, img2, -1, kernel ); t = (getTickCount()-t)/getTickFrequency()*1000; imwrite( "lena2.jpg", img2 ); ui->textBrowser->clear(); ui->textBrowser->append(tr("<img src=./lena2.jpg>")); ui->textBrowser->append( tr("the mask mode is filter2D of the opencv......") ); ui->textBrowser->append( tr("the time running cost %1ms").arg(t) ); } }
setting.cpp:
#include "setting.h" #include "ui_setting.h" setting::setting(QWidget *parent) : QDialog(parent), ui(new Ui::setting) { ui->setupUi(this); set_mask_mode = 1; } setting::~setting() { delete ui; } void setting::on_closeButton_clicked() { close(); ui->~setting(); } void setting::on_realizeButton_clicked() { if( ui->realizeButton->isChecked() ) { set_mask_mode = 1; } } void setting::on_filter2dButton_clicked() { if( ui->filter2dButton->isChecked() ) { set_mask_mode = 2; } }
实验总结:
通过本次实验可以看到,虽然上面2种模式都实现了滤波功能,不过opencv自带的filter2D对lena这张图片才用3ms,而自己实现的函数用了7.5ms,差不多慢了1倍。由此可见,opencv中很多函数是进行了优化的。