zoukankan      html  css  js  c++  java
  • 基础学习笔记之opencv(9):Mat图像扫描

      本文来讲讲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下载

    作者:tornadomeet 出处:http://www.cnblogs.com/tornadomeet 欢迎转载或分享,但请务必声明文章出处。 (新浪微博:tornadomeet,欢迎交流!)
  • 相关阅读:
    uva 10491 Cows and Cars
    uva 10910 Marks Distribution
    uva 11029 Leading and Trailing
    手算整数的平方根
    uva 10375 Choose and divide
    uva 10056 What is the Probability?
    uva 11027 Palindromic Permutation
    uva 10023 Square root
    Ural(Timus) 1081. Binary Lexicographic Sequence
    扩展欧几里得(求解线性方程)
  • 原文地址:https://www.cnblogs.com/tornadomeet/p/2604225.html
Copyright © 2011-2022 走看看