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,欢迎交流!)
  • 相关阅读:
    HDFS被设计成能够在一个大集群中跨机器可靠地存储超大文件
    NameNode实现了三个接口
    MongoDB Connector for Hadoop
    Java Virtual Machine (JVM) objects 虚拟机实例的产生 退出 两种线程
    Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.
    配置hadoop用户SSH无密码登陆 的2种方式 落脚点是 可以ssh免密进入的主机名写入动作发出主机的 known_hosts,而被无密进入主机的authorized_keys文件 免密登录
    Java Virtual Machine Process Status Tool
    Problem binding to [bigdata-server-01:9000] java.net.BindException: Cannot assign requested address;
    a completely rewritten architecture of Hadoop cluster
    curl is a tool to transfer data from or to a server curl POST
  • 原文地址:https://www.cnblogs.com/tornadomeet/p/2604225.html
Copyright © 2011-2022 走看看