學習筆記之OpenCV(1):Mat圖像掃描

轉自:http://www.cnblogs.com/tornadomeet/archive/2012/07/22/2604225.html

本文來講講Mat存儲的像素該怎麼對其進行掃描?掃描的效率又如何?

  文章參考opencv自帶的tutiol及其code

 

  實驗功能:

  可以手動選擇打開電腦上的圖片。

  4種對Mat矩陣的掃描方法效率進行比較,這4種掃描方法分別爲:連續內存直接訪問;用迭代器進行訪問;直接像素點進行訪問;用LUT查找表進行訪問;這4種掃描方法可以在菜單欄進行選擇。

  這些訪問完後是將每個訪問到的像素點的像素進行壓縮,壓縮間隔可以在軟件的界面中進行修改。

  軟件的下端顯示每種次掃描方法掃描打開的圖片所用的平均時間(程序中設定爲求100次的平均值)

 

  實驗說明:

  通過本次實驗,下面幾點需要特別注意和學習:

  1.注意MatisContinuous函數的含義,它是指Mat中的像素點在內存中的存儲是否連續,一般情況下如果改Mat只有1行,那當然連續;如果有多行時,那麼每行的end要與下一行的begin連在一起纔算連續。一般方法我們建立的Mat都是連續的,但是如果用Mat::col(),Mat::dialog()等截取建立的Mat是不連續的。如果Mat連續,那麼我們訪問時就可以把其當成一個長行即可。

  2.Mat自帶的MatIterator_和普通的迭代器類似,都有相應的操作。

  3.Mat爲多通道時,如果我們將其內容輸出到終端,則可以看出其列數爲Mat::colsn倍,當然nMat的通道數。雖是如此,但是Mat::cols的數值並沒有隨之改變。

  4.opencv中自帶有LUT函數,當建立好查找表後,直接輸入就可以得到輸出了。

 

  實驗結果:

  手動選擇打開圖片後:

  

  在菜單欄下可以選擇掃描模式。下面是4種掃描結果圖,注意觀察其掃描所用的時間。

 

  模式1(連續內存直接訪問)結果:

  

 

  模式2(用迭代器進行訪問)結果:

  

 

  模式3(直接像素點進行訪問)結果:

  

 

  模式4(LUT查找表進行訪問)結果:

  

 

  大家不要只盯着圖片的lena看,要觀測掃描這幅圖片所用的時間。

 


  實驗主要部分代碼及註釋(附錄有工程code下載鏈接):

複製代碼
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <iostream>

using namespace std;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    mode_num = 1;
    times = 100;
    divide_width = 50;
    ui->textBrowser->setStyleSheet( QString::fromUtf8("background-color:black") );
    ui->textBrowser->setTextColor( Qt::green );
    ui->textBrowser->setFont( QFont("Times New Roman", 11) );
    ui->textBrowser->append( "Scan Mode-----Efficient_way......" );

    for( int i = 0; i <256; ++i )
    {
        table[i] = (i/divide_width)*divide_width;
    }

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_closeButton_clicked()
{
    close();
}


void MainWindow::on_actionLUT_triggered(bool checked)
{
    ui->textBrowser->clear();
    mode_num = 4;
    if( checked )
    {
        ui->textBrowser->append( "Scan Mode-----LUT......" );
        ui->actionIterator->setChecked( 0 );
        ui->actionOn_the_fly->setChecked( 0 );
        ui->actionEfficient_way->setChecked( 0 );
    }
    else
        ui->textBrowser->append( "0" );
}

void MainWindow::on_actionOn_the_fly_triggered(bool checked)
{
    ui->textBrowser->clear();
    mode_num = 3;
    if( checked )
    {
        ui->textBrowser->append( "Scan Mode-----On_the_fly......" );
        ui->actionEfficient_way->setChecked( 0 );
        ui->actionIterator->setChecked( 0 );
        ui->actionLUT->setChecked( 0 );
    }
    else
        ui->textBrowser->append( "0" );
}

void MainWindow::on_actionIterator_triggered(bool checked)
{
    ui->textBrowser->clear();
    mode_num = 2;
    if( checked )
    {
        ui->textBrowser->append( "Scan Mode-----Iterator......" );
        ui->actionEfficient_way->setChecked( 0 );
        ui->actionOn_the_fly->setChecked( 0 );
        ui->actionLUT->setChecked( 0 );
    }
    else
        ui->textBrowser->append( "0" );
}

void MainWindow::on_actionEfficient_way_triggered(bool checked)
{
    ui->textBrowser->clear();
    mode_num = 1;
    if( checked )
    {
        ui->textBrowser->append( "Scan Mode-----Efficient_way......" );
        ui->actionIterator->setChecked( 0 );
        ui->actionOn_the_fly->setChecked( 0 );
        ui->actionLUT->setChecked( 0 );
    }
    else
        ui->textBrowser->append( "0" );
}

void MainWindow::on_openButton_clicked()
{
    //tr函數是用來實現國際化的,即軟件以後翻譯成其它語言時,會自動翻譯成中文,這裏其實是沒有必要的
    QString img_mame = QFileDialog::getOpenFileName( this, "Open img", "../scan_img", tr("Image Files(*.png *.jpg *.bmp *.jpeg)") );
    img = imread( img_mame.toAscii().data() );
    cvtColor( img, img, CV_BGR2RGB );
    QImage qimg = QImage( (const unsigned char*)(img.data), img.cols, img.rows, QImage::Format_RGB888 );
    ui->label->setPixmap( QPixmap::fromImage( qimg ) );
    cvtColor( img, img, CV_RGB2BGR );
}

void MainWindow::on_scanButton_clicked()
{
    ui->label->clear();//該句可以不用,因爲下面的圖片顯示會自動覆蓋

    //連續內存處理模式
    if( 1 == mode_num )
    {
        double t = (double)getTickCount();
        for( int i = 0; i < times; i++ )
        {
            Mat clone_I = img.clone();
            img_scan = MainWindow::efficient_way_scan( clone_I, table );
        }
        t = (double)(((getTickCount()-t)/getTickFrequency())*1000/times);//計算times次的平均時間
        ui->textBrowser->append( tr("the average time of scanning the image is : %1ms").arg( t ) );

        //顯示像素壓縮後圖像
        cvtColor( img_scan, img_scan, CV_BGR2RGB );
        QImage qimg = QImage( (const unsigned char*)(img_scan.data), img_scan.cols, img_scan.rows, QImage::Format_RGB888 );
        ui->label->setPixmap( QPixmap::fromImage( qimg ) );
        cvtColor( img_scan, img_scan, CV_RGB2BGR );
    }

    //迭代器模式
    else if( 2 == mode_num )
    {
        double t = (double)getTickCount();
        for( int i = 0; i < times; i++ )
        {
            Mat clone_I = img.clone();
            img_scan = MainWindow::iterator_scan( clone_I, table );
        }
        t = (double)(((getTickCount()-t)/getTickFrequency())*1000/times);//計算times次的平均時間
        ui->textBrowser->append( tr("the average time of scanning the image is : %1ms").arg( t ) );

        //顯示像素壓縮後圖像
        cvtColor( img_scan, img_scan, CV_BGR2RGB );
        QImage qimg = QImage( (const unsigned char*)(img_scan.data), img_scan.cols, img_scan.rows, QImage::Format_RGB888 );
        ui->label->setPixmap( QPixmap::fromImage( qimg ) );
        cvtColor( img_scan, img_scan, CV_RGB2BGR );
    }

    //單獨掃描模式
    else if( 3 == mode_num )
    {
        double t = (double)getTickCount();
        for( int i = 0; i < times; i++ )
        {
            Mat clone_I = img.clone();
            img_scan = MainWindow::on_the_flay_way_scan( clone_I, table );
        }
        t = (double)(((getTickCount()-t)/getTickFrequency())*1000/times);//計算times次的平均時間
        ui->textBrowser->append( tr("the average time of scanning the image is : %1ms").arg( t ) );

        //顯示像素壓縮後圖像
        cvtColor( img_scan, img_scan, CV_BGR2RGB );
        QImage qimg = QImage( (const unsigned char*)(img_scan.data), img_scan.cols, img_scan.rows, QImage::Format_RGB888 );
        ui->label->setPixmap( QPixmap::fromImage( qimg ) );
        cvtColor( img_scan, img_scan, CV_RGB2BGR );
    }

    //LUT模式
    else if( 4 == mode_num )
    {
        Mat lookup_table( 1, 256, CV_8U );
        uchar *p = lookup_table.data;//即使沒有初始化也是有首地址的
        for( int i = 0; i < 256 ; i++ )
            {
                p[i] = table[i];
            }

        double t = (double)getTickCount();
        for( int j = 0; j < times; j++ )
            {
                LUT( img, lookup_table, img_scan );
            }
        t = (double)(((getTickCount()-t)/getTickFrequency())*1000/times);//計算times次的平均時間
        ui->textBrowser->append( tr("the average time of scanning the image is : %1ms").arg( t ) );

        //顯示像素壓縮後圖像
        cvtColor( img_scan, img_scan, CV_BGR2RGB );
        QImage qimg = QImage( (const unsigned char*)(img_scan.data), img_scan.cols, img_scan.rows, QImage::Format_RGB888 );
        ui->label->setPixmap( QPixmap::fromImage( qimg ) );
        cvtColor( img_scan, img_scan, CV_RGB2BGR );
    }

}

void MainWindow::on_spinBox_editingFinished()
{
    divide_width = ui->spinBox->value();//獲取spinBox裏更改過的值
    for( int i = 0; i <256; ++i )
    {
        table[i] = (i/divide_width)*divide_width;//像素壓縮過程
    }

}

//轉換成一個長行後進行掃描,效率較高
 Mat& MainWindow::efficient_way_scan( Mat& I, const int* const table )
 {
     CV_Assert( I.depth() != sizeof( uchar ) );
     int channels = I.channels();
     int nRows = I.rows*channels;
     int nCols = I.cols;

     if( I.isContinuous() )
     {
        nCols *=nRows;//注意先後順序
        nRows = 1;

     }

     uchar *p;
     for( int i = 0; i < nRows; ++i )
     {
        p =  I.ptr<uchar>(i);
        for( int j = 0; j < nCols; ++j )
            {
              p[j] = (uchar)table[p[j]];//像素壓縮後
            }
     }

     return I;
 }

 //用迭代器進行掃描,比較安全
 Mat& MainWindow:: iterator_scan( Mat& I, const int* const table )
 {
     CV_Assert( I.depth() != sizeof(uchar) );
     int channels = I.channels();
     if ( 1 == channels )
         {
            MatIterator_<uchar> it = I.begin<uchar>(), end = I.end<uchar>();
            for( ; it != end; ++it )
                {
                    *it = table[*it];
                }
         }
     else if( 3 == channels )
         {
            MatIterator_<Vec3b>it = I.begin<Vec3b>(), end = I.end<Vec3b>();
            for( ; it != end; ++it )
                {
                    //3個通道時需分開進行,否則會自動跳過
                    //雖然實際的列數爲其3倍(內存中的),但Mat實際上的cols並沒有改變
                    (*it)[0] = table[(*it)[0]];
                    (*it)[1] = table[(*it)[1]];
                    (*it)[2] = table[(*it)[2]];
                }
         }
     return I;
 }


 //每個點進行訪問,速度最慢
Mat& MainWindow:: on_the_flay_way_scan( Mat& I, const int* const table )
{
    CV_Assert( I.depth() != sizeof(uchar) );
    int cols = I.cols;
    int rows = I.rows;
    int channels = I.channels();

    switch( channels )
        {
            case 1:
            {
                for( int i = 0; i < rows; i++ )
                    for( int j = 0; j < cols; j++)
                    {
                          I.at<uchar>(i, j) = table[I.at<uchar>(i, j)];
                    }
                break;
            }
            case 3:
            {
                Mat_<Vec3b> _I = I;//下面的取元素操作可以少輸入一些關鍵字
                for( int i = 0; i < rows; i++ )
                    for( int j = 0; j < cols; j++ )
                    {
                          _I(i, j)[0] = table[_I(i, j)[0]];
                          _I(i, j)[1] = table[_I(i, j)[1]];
                          _I(i, j)[2] = table[_I(i, j)[2]];
                    }
        break;
            }
            break;
        }

    return I;

}
複製代碼

 

  實驗總結:

  可以看出速度最快的是模式4(LUT查找表),最慢的是模式3(直接像素點進行訪問),模式1(當作1個長行進行掃描)效率較高,模式2(用迭代器進行掃描)效率較低,但是該方法操作比較安全。

  Qt Creator菜單欄編程時,關於一組菜單選項下每個時刻只能選擇1個的解決方法還沒掌握,所以本程序中採用的是比較笨的方法。對Qt的界面編程要多多練習。

  

  附:工程code下載

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章