zoukankan      html  css  js  c++  java
  • qt&gdal

    转自:http://blog.csdn.net/deirjie/article/details/37872743

    使用需要自行配置Qt和GDAL路径。

        近期写了一个高光谱图像光谱曲线匹配的算法,想封装到软件当中方便观察效果,也便于做后期算法改进和实际应用,并且以后的算法可以直接集成上来。于是打算自己写一个基本的框架实现图像浏览的一些基本功能。在网上各种找,利用GDAL进行遥感图像显示的代码很多,但不是有问题就是写的不太清楚,不够简洁,并且大多基于MFC。经过几天的奋战,成功实现了利用Qt框架进行遥感图像显示的基本功能,于是分享出来,对自己是总结,并且希望对别人有所帮助。

    开发环境:VS2010 Qt4.8.4GDAL 1.10

           说明:这里并没有QtGDAL,以及C++的一些基本的东西,这里只关注功能的实现做说明。如果对于QtGDAL有问题的,请参见其他资料。并不高深,高手绕道!

       重要说明:Qt发音叫做“Q特”,不是“Q替”,不要再读错了,官网有专门说明的!不要搞半天还说我发音有问题!!!  还有要写成Qt,不是QT,QT是QuickTime,不要混淆了。

             OK,先说说程序的功能:

    • 能够打开图像,并读取图像元数据信息;
    • 能够实现图像的正确显示,包括灰度图和RGB真彩色图的显示;
    • 支持图像的缩放,漫游;

    计算机生成了可选文字: 困ImgTest口电口}~!画冲口州openViewoperationWindowHelplmageCloseZoomoutZoomInFitWindowNorm以Size人bout日X日XENVIBand3Band41DescriptionMetaInforDataTypeX引zeySizeBandCountprojectionoriginpixelSizeENVI.hdrLab…Ulnt1653505376pROJCS[.UTM…660221,3.696二8.04627·8.0L二…___

     

    计算机生成了可选文字: 困ImgTest}~4县卜口叫openViewoperationWindowHelplmageCloseZoomoutZoomInFitWindowNorm以Size人bout日X1DescriptionMetaInforDataTypeX引zeySizeBandCountprojectionoriginpixelSizeENVIBand3Band4ENVI.hdrLab…Ulnt1653505376pROJCS[.UTM…660221,3.696二8.04627·8.0L二….

        功能非常简单基础,但是这当中很多细节是需要认真参透的。下面开始逐一进行讲解。(PSUI的设计就不讲了。)


            1.程序简单设计

        其实这个程序的设计相对简单,除了显示图像的控件需要自定义,其他的都可以直接在Qt Designer里面拖放就行了。那就来说说图像显示控件吧。先看头文件:

    1. //***********************************************************************  
    2. //Assembly         : ImgTest  
    3. //Author           : Jacory  
    4. //Created          : 07-10-2014  
    5. //  
    6. // LastModified By : Jacory  
    7. // LastModified On : 07-10-2014  
    8. //***********************************************************************  
    9. //<copyright file="MapCanvas.h" company="">  
    10. //     Copyright (c) . All rights reserved.  
    11. //</copyright>  
    12. //<summary>图像显示窗口类,负责图像的读取和显示等功能。</summary>  
    13. //***********************************************************************  
    14. #ifndefMAPCANVAS_H  
    15. #defineMAPCANVAS_H  
    16.    
    17. #include<QtGui/QGraphicsView>  
    18. #include<QStandardItemModel>  
    19. #include<gdal_priv.h>  
    20.    
    21.    
    22. ///<summary>  
    23. /// ClassMapCanvas.  
    24. ///</summary>  
    25. classMapCanvas : public QGraphicsView  
    26. {  
    27.     Q_OBJECT  
    28.      
    29. public:  
    30.     MapCanvas( QWidget *parent = 0 );  
    31.     ~MapCanvas();  
    32.     void ReadImg( const QString imgPath );  
    33.     void CloseCurrentImg();  
    34.     /// <summary>  
    35.     /// 返回图像元数据信息模型.  
    36.     /// </summary>  
    37.     ///<returns>图像元数据信息模型.</returns>  
    38.     QStandardItemModel* ImgMetaModel()  
    39.     {  
    40.         return imgMetaModel;  
    41.     };  
    42.     /// <summary>  
    43.     /// 设置图像元数据信息模型  
    44.     /// </summary>  
    45.     /// <paramname="model">图像元数据信息模型.</param>  
    46.     void SetMetaModel( QStandardItemModel*model )  
    47.     {  
    48.         this->imgMetaModel = model;  
    49.     };  
    50.     /// <summary>  
    51.     /// 返回文件列表数据模型  
    52.     /// </summary>  
    53.     ///<returns>文件列表数据模型.</returns>  
    54.     QStandardItemModel* FileListModel()  
    55.     {  
    56.         return fileListModel;  
    57.     };  
    58.     /// <summary>  
    59.     /// 设置fileListModel图像文件列表数据模型  
    60.     /// </summary>  
    61.     /// <paramname="model">文件列表数据模型.</param>  
    62.     void SetFileListModel( QStandardItemModel*model )  
    63.     {  
    64.         this->fileListModel = model;  
    65.     };  
    66.      
    67.     QSize sizeHint() const;  
    68.      
    69. publicslots:  
    70.     /// <summary>  
    71.     /// 放大图像  
    72.     /// </summary>  
    73.     void ZoomIn()  
    74.     {  
    75.         ScaleImg( 1.2 );  
    76.     };  
    77.     /// <summary>  
    78.     /// 缩小图像  
    79.     /// </summary>  
    80.     void ZoomOut()  
    81.     {  
    82.         ScaleImg( 0.8 );  
    83.     };  
    84.      
    85. protected:  
    86.     void wheelEvent( QWheelEvent *event );  
    87.     void mousePressEvent( QMouseEvent *event );  
    88.     void mouseMoveEvent( QMouseEvent *event );  
    89.     void mouseReleaseEvent( QMouseEvent *event);  
    90.      
    91. private:  
    92.     void ShowBand( GDALRasterBand* band );  
    93.     void ShowImg( QList<GDALRasterBand*>*imgBand );  
    94.     void ShowImgInfor( const QString filename);  
    95.     void ShowFileList( const QString filename);  
    96.     unsigned char* ImgSketch( float* buffer ,GDALRasterBand* currentBand, int size, double noValue );  
    97.     /// <summary>  
    98.     /// 图像缩放  
    99.     /// </summary>  
    100.     /// <paramname="factor">缩放因子</param>  
    101.     void ScaleImg( double factor )  
    102.     {  
    103.         m_scaleFactor *= factor;  
    104.         QMatrix matrix;  
    105.         matrix.scale( m_scaleFactor,m_scaleFactor );  
    106.         this->setMatrix( matrix );  
    107.     };  
    108.      
    109.     /// <summary>  
    110.     /// 图像元数据模型  
    111.     /// </summary>  
    112.     QStandardItemModel *imgMetaModel;  
    113.     /// <summary>  
    114.     /// 图像数据集  
    115.     /// </summary>  
    116.     GDALDataset *poDataset;  
    117.      
    118.     /// <summary>  
    119.     /// 文件列表数据模型  
    120.     /// </summary>  
    121.     QStandardItemModel *fileListModel;  
    122.      
    123.     /// <summary>  
    124.     /// 缩放系数  
    125.     /// </summary>  
    126.     float m_scaleFactor;  
    127.      
    128.     /// <summary>  
    129.     /// 判断是否显示RGB彩色图像  
    130.     /// </summary>  
    131.     bool m_showColor;  
    132.      
    133.     /// <summary>  
    134.     /// 上一个鼠标事件触发时鼠标的位置  
    135.     /// </summary>  
    136.     QPoint lastEventCursorPos;  
    137. };  
    138.    
    139. #endif //MAPCANVAS_H  

            mapView类集成自QGraphicsView类。代码里面对于成员变量和inline函数我已经写了非常详细的注释了,这里再说明一下几个函数的功能。void ReadImg( const QStringimgPath )是读图函数,通过UI来调用,它并不直接读取图像,而是将相应的参数传递给ShowBandShowImgShowImgInforShowFileList来分别实现各自相应的功能。void ShowBand( GDALRasterBand*band )函数是显示单个波段图像,当文件中的波段数不为3个时,没法组成RGB显示,默认就显示单个波段图像,事实上它只是把单个波段加入3次到QList<GDALRasterBand*>,然后把这个波段列表传递给ShowImg进行显示。而void ShowImg( QList<GDALRasterBand*>*imgBand )是显示图像的核心函数,它的内部通过判断m_showColor的真假来决定将要显示的图像的组织方式,并通过unsigned char* ImgSketch(float* buffer , GDALRasterBand* currentBand, int size, double noValue )函数对图像波段进行拉伸,详细说明放在后面,这里只要知道是什么用就行了。void ShowImgInfor( constQString filename )  void ShowFileList( const QString filename )这两个函数,分别是为了读取并显示图像文件的元数据信息和文件结构树,分别位于主界面的两侧。void CloseCurrentImg()这个函数很简单,就是关闭当前图像。void wheelEvent( QWheelEvent*event )void mousePressEvent(QMouseEvent *event )void mouseMoveEvent(QMouseEvent *event )void mouseReleaseEvent(QMouseEvent *event )4个函数都是重写的鼠标事件,具体后面再讨论。

    看完了头文件,已经基本知道这个MapView是一个什么样的对象了,事实上所有与图像显示有关的操作都属于这个类的职责,自然功能函数也就应该写在这个类里。下面我们根据需求来逐步实现这些功能函数。

     

          2.打开图像并读取图像元数据信息;

       这一步先不显示图像,只要求能够正确打开图像并读取出图像的基本信息即可。主要的函数就是ShowImgInforShowFileList这两个函数,来看代码:

    1. ///<summary>  
    2. ///显示图像基本信息  
    3. ///</summary>  
    4. ///<param name="filename">文件名</param>  
    5. voidMapCanvas::ShowImgInfor( const QString filename )  
    6. {  
    7.     if ( filename == "" || poDataset== NULL )  
    8.     {  
    9.         return;  
    10.     }  
    11.     int row = 0; // 用来记录数据模型的行号  
    12.      
    13.     // 图像的格式信息  
    14.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );  
    15.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );  
    16.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );  
    17.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;  
    18.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );  
    19.     imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );  
    20.      
    21.     // 图像的大小和波段个数  
    22.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );  
    23.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );  
    24.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );  
    25.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );  
    26.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );  
    27.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );  
    28.      
    29.     // 图像的投影信息  
    30.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );  
    31.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );  
    32.      
    33.     // 图像的坐标和分辨率信息  
    34.     double adfGeoTransform[6];  
    35.     QString origin = "";  
    36.     QString pixelSize = "";  
    37.     if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )  
    38.     {  
    39.         origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );  
    40.         pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );  
    41.     }  
    42.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );  
    43.     imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );  
    44.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );  
    45.     imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );  
    46. }  
    47.    
    48. ///<summary>  
    49. ///显示文件结构树  
    50. ///</summary>  
    51. ///<param name="filename">文件名</param>  
    52. voidMapCanvas::ShowFileList( const QString filename )  
    53. {  
    54.     if ( filename == "" || poDataset== NULL )  
    55.     {  
    56.         return;  
    57.     }  
    58.     QFileInfo fileInfo( filename );  
    59.     QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );  
    60.     for ( int i = 0; i <poDataset->GetRasterCount(); i++ )  
    61.     {  
    62.         QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );  
    63.         rootItem->setChild( i, childItem );  
    64.     }  
    65.     fileListModel->setItem( 0, rootItem );  
    66. }  

    其实很简单,就是用了QtModel-View模型,显示的控件分别是QTableViewQTreeView,数据模型分别是这里的imgMetaModelfileListModel,只用将模型适当的初始化并绑定到显示控件上就行了。以上初始化比较简单,这里就不啰嗦了,都是一些基本的GDAL数据模型和Qt的东西。

     

            3.实现图像的显示功能

        这一步就是核心了,我打算通过函数调用的顺序来逐步讲解这里牵涉到的功能函数。首先是UI选择文件后,将文件路径传递给ReadImg函数进行文件读取,那我们先来看看ReadImg函数的实现。

    1. ///<summary>  
    2. /// 读取图像文件  
    3. ///</summary>  
    4. ///<param name="imgPath">图像文件</param>  
    5. voidMapCanvas::ReadImg( const QString imgPath )  
    6. {  
    7.     GDALAllRegister();  
    8.     CPLSetConfigOption("GDAL_FILENAME_IS_UTF8""NO" );  
    9.     poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );  
    10.     if ( poDataset == NULL )  
    11.     {  
    12.         QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );  
    13.         return;  
    14.     }  
    15.     ShowFileList( imgPath );  
    16.     ShowImgInfor( imgPath );  
    17.     // 如果图像文件并非三个波段,则默认只显示第一波段灰度图像  
    18.     if ( poDataset->GetRasterCount() != 3 )  
    19.     {  
    20.         m_showColor = false;  
    21.         ShowBand( poDataset->GetRasterBand(1 ) );  
    22.     }  
    23.     // 如果图像正好三个波段,则默认以RGB的顺序显示彩色图  
    24.     else  
    25.     {  
    26.         m_showColor = true;  
    27.         QList<GDALRasterBand*> bandList;  
    28.         bandList.append(poDataset->GetRasterBand( 1 ) );  
    29.         bandList.append(poDataset->GetRasterBand( 2 ) );  
    30.         bandList.append(poDataset->GetRasterBand( 3 ) );  
    31.         ShowImg( &bandList );  
    32.     }  
    33.     GDALClose( poDataset );  
    34. }  

    逻辑很清晰,调用ShowFileListShowImgInfor初始化两个model以供相应的显示View控件来显示。再判断如果图像文件波段数不为3,则默认显示第一波段灰度图像,否则显示彩色图。

    再来看ShowBand函数:

    1. ///<summary>  
    2. ///显示单波段图像  
    3. ///</summary>  
    4. ///<param name="band">图像波段</param>  
    5. voidMapCanvas::ShowBand( GDALRasterBand* band )  
    6. {  
    7.     if ( band == NULL )  
    8.     {  
    9.         return;  
    10.     }  
    11.      
    12.     QList<GDALRasterBand*> myBand;  
    13.     myBand.append( band );  
    14.     myBand.append( band );  
    15.     myBand.append( band );  
    16.      
    17.     ShowImg( &myBand );  
    18.      
    19. }  

    可以看到,它只是把单个波段用列表的方式存储了三次,传递给ShowImg来进行显示。那直接看ShowImg吧。

    1. ///<summary>  
    2. /// 显示图像  
    3. ///</summary>  
    4. ///<param name="imgBand">图像波段</param>  
    5. voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )  
    6. {  
    7.     if ( imgBand->size() != 3 )  
    8.     {  
    9.         return;  
    10.     }  
    11.      
    12.     int imgWidth = imgBand->at( 0)->GetXSize();  
    13.     int imgHeight = imgBand->at( 0)->GetYSize();  
    14.      
    15.     m_scaleFactor = this->height() * 1.0 /imgHeight;  
    16.      
    17.     int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );  
    18.     int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );  
    19.      
    20.     GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();  
    21.      
    22.     // 首先分别读取RGB三个波段  
    23.     float* rBand = new float[iScaleWidth *iScaleHeight];  
    24.     float* gBand = new float[iScaleWidth *iScaleHeight];  
    25.     float* bBand = new float[iScaleWidth *iScaleHeight];  
    26.      
    27.     unsigned char *rBandUC, *gBandUC, *bBandUC;  
    28.      
    29.     // 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取  
    30.     if ( m_showColor == true )  
    31.     {  
    32.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    33.         imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    34.         imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    35.          
    36.         // 分别拉伸每个波段并将Float转换为unsigned char  
    37.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
    38.         gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );  
    39.         bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );  
    40.     }  
    41.     else  
    42.     {  
    43.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    44.          
    45.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
    46.         gBandUC = rBandUC;  
    47.         bBandUC = rBandUC;  
    48.     }  
    49.      
    50.     // 将三个波段组合起来  
    51.     int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;  
    52.     unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];  
    53.     forint h = 0; h < iScaleHeight; h++ )  
    54.     {  
    55.         forint w = 0; w < iScaleWidth; w++)  
    56.         {  
    57.             allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];  
    58.             allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];  
    59.             allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];  
    60.         }  
    61.     }  
    62.      
    63.     // 构造图像并显示  
    64.     QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem(  QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888  ) ) );  
    65.     QGraphicsScene *myScene = newQGraphicsScene();  
    66.     myScene->addItem( imgItem );  
    67.     this->setScene( myScene );  
    68. }  

    这个函数的核心在于构造QGraphicsPixmapItem,也就是这里的imgItem这个对象,之前的代码都是为了正确构造这个对象而做的工作。这里的imgWidthimgHeight分别是图像文件的宽和高,而iScaleWidthiScaleHeight是缩放后的宽和高,缩放的条件正是显示窗口的大小。读取图像时,采用分别读取RGB三个波段,最后再组合起来的方式,读取波段都是采用RasterIO函数,采用float的数组来进行读取避免一些格式问题带来的精度截断,读取之后,通过ImgSketch函数进行图像拉伸,把图像值拉伸到0~255灰度级。

    1. ///<summary>  
    2. /// 图像线性拉伸  
    3. ///</summary>  
    4. ///<param name="buffer">图像缓存</param>  
    5. ///<param name="currentBand">当前波段</param>  
    6. ///<param name="size">The size.</param>  
    7. ///<param name="noValue">图像中的异常值</param>  
    8. ///<returns>经过拉伸的8位图像缓存</returns>  
    9. unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )  
    10. {  
    11.     unsigned char* resBuffer = new unsignedchar[bandSize];  
    12.     double max, min;  
    13.     double minmax[2];  
    14.    
    15.     currentBand->ComputeRasterMinMax( 1,minmax );  
    16.     min = minmax[0];  
    17.     max = minmax[1];  
    18.     if( min <= noValue && noValue<= max )  
    19.     {  
    20.         min = 0;  
    21.     }  
    22.     for ( int i = 0; i < bandSize; i++ )  
    23.     {  
    24.         if ( buffer[i] > max )  
    25.         {  
    26.             resBuffer[i] = 255;  
    27.         }  
    28.         else if ( buffer[i] <= max&& buffer[i] >= min )  
    29.         {  
    30.             resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );  
    31.         }  
    32.         else  
    33.         {  
    34.             resBuffer[i] = 0;  
    35.         }  
    36.     }  
    37.      
    38.     return resBuffer;  
    39. }  

    这里的转换核心为

    resBuffer[i] = static_cast<uchar>( 255 - 255 * ( max -buffer[i] ) / ( max - min ) );

    转换公式也可以变为((buffer[i]-min)/(max-min))*255,是一样的,当然还有其他的拉伸方法,可以自行使用。

    拉伸之后组合波段,需要用到int bytePerLine = (iScaleWidth * 24 + 31 ) / 8;这句,因为要显示的图像为8位的,需要把图像值变换为unsigned char型,这个工作在imgSketch里面已经做了,但是由于要将3个波段组织正确,还需要字节对齐,也就是这里的bytePerLine,这个是byte型表示的这个图像的宽度,当然是包含了RGB三个波段。然后就是将每个波段的值一一对齐放入allBandUC这个数组中,最后根据这个组合后的数组构QImageQPixmap以及QGraphicsPixmapItem,最后把这个Item加入到scene里面用MapView显示就行了。需要注意的是,这个过程看似简单,但里面有很多细节的地方需要认真对待,仔细思考,不然很容易得不到正确的结果。

     

    4.实现图像的漫游功能

    这一部分我在网上看了很多代码,大部分人都是从底层原理开始又来写一遍,但其实对于漫游的操作其实Qt在后台已经支持的非常好了,我们的MapView继承自QGraphicsView,这个父类已经把很多操作封装到位了,我们只要合理组织就好了,没有必要再去写一遍。那就很简单了,来分别看看实现代码吧。

    1. ///<summary>  
    2. ///鼠标滚轮事件,实现图像缩放  
    3. ///</summary>  
    4. ///<param name="event">滚轮事件</param>  
    5. voidMapCanvas::wheelEvent( QWheelEvent *event )  
    6. {  
    7.     // 滚轮向上滑动,放大图像  
    8.     if ( event->delta() > 0 )  
    9.     {  
    10.         ZoomIn();  
    11.     }  
    12.     // 滚轮向下滑动,缩小图像  
    13.     if ( event->delta() < 0 )  
    14.     {  
    15.         ZoomOut();  
    16.     }  
    17. }  
    18.    
    19. ///<summary>  
    20. ///鼠标按键按下事件  
    21. ///</summary>  
    22. ///<param name="event">鼠标事件.</param>  
    23. voidMapCanvas::mousePressEvent( QMouseEvent *event )  
    24. {  
    25.     // 滚轮键按下,平移图像  
    26.     if ( event->button() == Qt::MidButton )  
    27.     {  
    28.         this->setDragMode(QGraphicsView::ScrollHandDrag );  
    29.         this->setInteractive( false );  
    30.         lastEventCursorPos = event->pos();  
    31.     }  
    32. }  
    33.    
    34. ///<summary>  
    35. /// 鼠标移动事件  
    36. ///</summary>  
    37. ///<param name="event">鼠标事件</param>  
    38. voidMapCanvas::mouseMoveEvent( QMouseEvent *event )  
    39. {  
    40.     if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )  
    41.     {  
    42.         QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;  
    43.        this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );  
    44.        this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );  
    45.         this->viewport()->setCursor(Qt::ClosedHandCursor );  
    46.     }  
    47.      
    48. }  
    49.    
    50. ///<summary>  
    51. ///鼠标按键释放事件  
    52. ///</summary>  
    53. ///<param name="event">鼠标事件</param>  
    54. voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )  
    55. {  
    56.     if ( event->button() == Qt::MidButton )  
    57.     {  
    58.         this->setDragMode(QGraphicsView::NoDrag );  
    59.     }  
    60. }  

    重写鼠标事件,这里需要说明的是,漫游的操作Qt采用鼠标左键按下并移动来实现视图平移,但是我这里强制定义为了鼠标中键按下,所以还比较麻烦相对来说,如果你只想用默认的漫游操作,直接把this->setDragMode(QGraphicsView::NoDrag );这句加入到你的构造函数中就行了,非常简单。事实上我这个平移函数有问题,我后面再解释。这里我们先关注放大缩小功能,这部分代码我是通过inline的方式添加到头文件中的。

    1. ///<summary>  
    2.     /// 放大图像  
    3.     /// </summary>  
    4.     void ZoomIn()  
    5.     {  
    6.         ScaleImg( 1.2 );  
    7.     };  
    8.     /// <summary>  
    9.     /// 缩小图像  
    10.     /// </summary>  
    11.     void ZoomOut()  
    12.     {  
    13.         ScaleImg( 0.8 );  
    14.     };  
    15.    
    16. ///<summary>  
    17.     /// 图像缩放  
    18.     /// </summary>  
    19.     /// <paramname="factor">缩放因子</param>  
    20.     void ScaleImg( double factor )  
    21.     {  
    22.         m_scaleFactor *= factor;  
    23.         QMatrix matrix;  
    24.         matrix.scale( m_scaleFactor,m_scaleFactor );  
    25.         this->setMatrix( matrix );  
    26.     };  

    图像缩放也非常简单,让你的QGraphicsView去处理细节,不要自己又去从底层计算然后写一大堆代码,还不一定就比Qt默认的方式效率高。

    做到这里基本就搞定了,我刚刚说的平移函数那里,实际上我在mouseMoveEvent里采用计算当前坐标点和事件发生的坐标点差值的方式,然后去设置水平和竖直滑动条的值来实现平移

    1. QPointdelta = ( event->pos() - lastEventCursorPos ) / 10;  
    2.        this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );  
    3.        this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );  

    我这样写后,我的平移实际上并不是非常流畅,而是有点像浏览网页时按住鼠标中键往下浏览的效果,但实际上很多GIS和遥感软件的平移方式都不是这样的。这里应该是我对Qt还有些不熟悉,也希望大家知道的话告诉我一下,我认为Qt应该是支持的。

    好了,差不多了,其他的功能我也还在完善,先写到这里吧,希望对大家有所帮助。最后还是把MapView的实现文件整体粘上来。VS2010工程文件我放到CSDN的资源里面大家可以下载。

    1. #include"MapCanvas.h"  
    2. #include<QtGui/QMessageBox>  
    3. #include<QtCore/QFileInfo>  
    4. #include<QtGui/QImage>  
    5. #include<QtGui/QPixmap>  
    6. #include<QtGui/QGraphicsPixmapItem>  
    7. #include<QtGui/QMatrix>  
    8. #include<QtGui/QWheelEvent>  
    9. #include<QtGui/QScrollBar>  
    10.    
    11. MapCanvas::MapCanvas(QWidget *parent /*= 0 */ )  
    12.     : QGraphicsView( parent )  
    13. {  
    14.     poDataset = NULL;  
    15.     m_scaleFactor = 1.0;  
    16.     m_showColor = true;  
    17.     imgMetaModel = new QStandardItemModel;  
    18.     imgMetaModel->setColumnCount( 2 );  
    19.     fileListModel = new QStandardItemModel;  
    20.     QSizePolicy policy( QSizePolicy::Preferred,QSizePolicy::Preferred );  
    21.     this->setSizePolicy( policy );  
    22. }  
    23.    
    24. ///<summary>  
    25. ///Finalizes an instance of the <see cref="MapCanvas" /> class.  
    26. ///</summary>  
    27. MapCanvas::~MapCanvas()  
    28. {  
    29.    
    30. }  
    31.    
    32. ///<summary>  
    33. /// 读取图像文件  
    34. ///</summary>  
    35. ///<param name="imgPath">图像文件</param>  
    36. voidMapCanvas::ReadImg( const QString imgPath )  
    37. {  
    38.     GDALAllRegister();  
    39.     CPLSetConfigOption("GDAL_FILENAME_IS_UTF8""NO" );  
    40.     poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );  
    41.     if ( poDataset == NULL )  
    42.     {  
    43.         QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );  
    44.         return;  
    45.     }  
    46.     ShowFileList( imgPath );  
    47.     ShowImgInfor( imgPath );  
    48.     // 如果图像文件并非三个波段,则默认只显示第一波段灰度图像  
    49.     if ( poDataset->GetRasterCount() != 3 )  
    50.     {  
    51.         m_showColor = false;  
    52.         ShowBand( poDataset->GetRasterBand(1 ) );  
    53.     }  
    54.     // 如果图像正好三个波段,则默认以RGB的顺序显示彩色图  
    55.     else  
    56.     {  
    57.         m_showColor = true;  
    58.         QList<GDALRasterBand*> bandList;  
    59.         bandList.append(poDataset->GetRasterBand( 1 ) );  
    60.         bandList.append(poDataset->GetRasterBand( 2 ) );  
    61.         bandList.append(poDataset->GetRasterBand( 3 ) );  
    62.         ShowImg( &bandList );  
    63.     }  
    64.     GDALClose( poDataset );  
    65. }  
    66.    
    67. ///<summary>  
    68. ///关闭当前图像文件  
    69. ///</summary>  
    70. voidMapCanvas::CloseCurrentImg()  
    71. {  
    72.     poDataset = NULL;  
    73.     imgMetaModel->clear();  
    74.     fileListModel->clear();  
    75. }  
    76.    
    77. ///<summary>  
    78. ///显示单波段图像  
    79. ///</summary>  
    80. ///<param name="band">图像波段</param>  
    81. voidMapCanvas::ShowBand( GDALRasterBand* band )  
    82. {  
    83.     if ( band == NULL )  
    84.     {  
    85.         return;  
    86.     }  
    87.      
    88.     QList<GDALRasterBand*> myBand;  
    89.     myBand.append( band );  
    90.     myBand.append( band );  
    91.     myBand.append( band );  
    92.      
    93.     ShowImg( &myBand );  
    94.      
    95. }  
    96.    
    97. ///<summary>  
    98. /// 显示图像  
    99. ///</summary>  
    100. ///<param name="imgBand">图像波段</param>  
    101. voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )  
    102. {  
    103.     if ( imgBand->size() != 3 )  
    104.     {  
    105.         return;  
    106.     }  
    107.      
    108.     int imgWidth = imgBand->at( 0)->GetXSize();  
    109.     int imgHeight = imgBand->at( 0)->GetYSize();  
    110.      
    111.     m_scaleFactor = this->height() * 1.0 /imgHeight;  
    112.      
    113.     int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );  
    114.     int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );  
    115.      
    116.     GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();  
    117.      
    118.     // 首先分别读取RGB三个波段  
    119.     float* rBand = new float[iScaleWidth *iScaleHeight];  
    120.     float* gBand = new float[iScaleWidth *iScaleHeight];  
    121.     float* bBand = new float[iScaleWidth *iScaleHeight];  
    122.      
    123.     unsigned char *rBandUC, *gBandUC, *bBandUC;  
    124.      
    125.     // 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取  
    126.     if ( m_showColor == true )  
    127.     {  
    128.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    129.         imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    130.         imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    131.          
    132.         // 分别拉伸每个波段并将Float转换为unsigned char  
    133.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
    134.         gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );  
    135.         bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );  
    136.     }  
    137.     else  
    138.     {  
    139.         imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );  
    140.          
    141.         rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );  
    142.         gBandUC = rBandUC;  
    143.         bBandUC = rBandUC;  
    144.     }  
    145.      
    146.     // 将三个波段组合起来  
    147.     int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;  
    148.     unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];  
    149.     forint h = 0; h < iScaleHeight; h++ )  
    150.     {  
    151.         forint w = 0; w < iScaleWidth; w++)  
    152.         {  
    153.             allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];  
    154.             allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];  
    155.             allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];  
    156.         }  
    157.     }  
    158.      
    159.     // 构造图像并显示  
    160.     QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem(  QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888  ) ) );  
    161.     QGraphicsScene *myScene = newQGraphicsScene();  
    162.     myScene->addItem( imgItem );  
    163.     this->setScene( myScene );  
    164. }  
    165.    
    166. ///<summary>  
    167. ///显示图像基本信息  
    168. ///</summary>  
    169. ///<param name="filename">文件名</param>  
    170. voidMapCanvas::ShowImgInfor( const QString filename )  
    171. {  
    172.     if ( filename == "" || poDataset== NULL )  
    173.     {  
    174.         return;  
    175.     }  
    176.     int row = 0; // 用来记录数据模型的行号  
    177.      
    178.     // 图像的格式信息  
    179.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );  
    180.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );  
    181.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );  
    182.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;  
    183.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );  
    184.     imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );  
    185.      
    186.     // 图像的大小和波段个数  
    187.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );  
    188.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );  
    189.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );  
    190.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );  
    191.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );  
    192.     imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );  
    193.      
    194.     // 图像的投影信息  
    195.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );  
    196.     imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );  
    197.      
    198.     // 图像的坐标和分辨率信息  
    199.     double adfGeoTransform[6];  
    200.     QString origin = "";  
    201.     QString pixelSize = "";  
    202.     if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )  
    203.     {  
    204.         origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );  
    205.         pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );  
    206.     }  
    207.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );  
    208.     imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );  
    209.     imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );  
    210.     imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );  
    211. }  
    212.    
    213. ///<summary>  
    214. ///显示文件结构树  
    215. ///</summary>  
    216. ///<param name="filename">文件名</param>  
    217. voidMapCanvas::ShowFileList( const QString filename )  
    218. {  
    219.     if ( filename == "" || poDataset== NULL )  
    220.     {  
    221.         return;  
    222.     }  
    223.     QFileInfo fileInfo( filename );  
    224.     QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );  
    225.     for ( int i = 0; i <poDataset->GetRasterCount(); i++ )  
    226.     {  
    227.         QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );  
    228.         rootItem->setChild( i, childItem );  
    229.     }  
    230.     fileListModel->setItem( 0, rootItem );  
    231. }  
    232.    
    233. ///<summary>  
    234. /// 图像线性拉伸  
    235. ///</summary>  
    236. ///<param name="buffer">图像缓存</param>  
    237. ///<param name="currentBand">当前波段</param>  
    238. ///<param name="size">The size.</param>  
    239. ///<param name="noValue">图像中的异常值</param>  
    240. ///<returns>经过拉伸的8位图像缓存</returns>  
    241. unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )  
    242. {  
    243.     unsigned char* resBuffer = new unsignedchar[bandSize];  
    244.     double max, min;  
    245.     double minmax[2];  
    246.      
    247.      
    248.     currentBand->ComputeRasterMinMax( 1,minmax );  
    249.     min = minmax[0];  
    250.     max = minmax[1];  
    251.     if( min <= noValue && noValue<= max )  
    252.     {  
    253.         min = 0;  
    254.     }  
    255.     for ( int i = 0; i < bandSize; i++ )  
    256.     {  
    257.         if ( buffer[i] > max )  
    258.         {  
    259.             resBuffer[i] = 255;  
    260.         }  
    261.         else if ( buffer[i] <= max&& buffer[i] >= min )  
    262.         {  
    263.             resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );  
    264.         }  
    265.         else  
    266.         {  
    267.             resBuffer[i] = 0;  
    268.         }  
    269.     }  
    270.      
    271.     return resBuffer;  
    272. }  
    273.    
    274. ///<summary>  
    275. /// 控件大小  
    276. ///</summary>  
    277. ///<returns>QSize.</returns>  
    278. QSizeMapCanvas::sizeHint() const  
    279. {  
    280.     return QSize( 1024, 768 );  
    281. }  
    282.    
    283. ///<summary>  
    284. ///鼠标滚轮事件,实现图像缩放  
    285. ///</summary>  
    286. ///<param name="event">滚轮事件</param>  
    287. voidMapCanvas::wheelEvent( QWheelEvent *event )  
    288. {  
    289.     // 滚轮向上滑动,放大图像  
    290.     if ( event->delta() > 0 )  
    291.     {  
    292.         ZoomIn();  
    293.     }  
    294.     // 滚轮向下滑动,缩小图像  
    295.     if ( event->delta() < 0 )  
    296.     {  
    297.         ZoomOut();  
    298.     }  
    299. }  
    300.    
    301. ///<summary>  
    302. ///鼠标按键按下事件  
    303. ///</summary>  
    304. ///<param name="event">鼠标事件.</param>  
    305. voidMapCanvas::mousePressEvent( QMouseEvent *event )  
    306. {  
    307.     // 滚轮键按下,平移图像  
    308.     if ( event->button() == Qt::MidButton )  
    309.     {  
    310.         this->setDragMode(QGraphicsView::ScrollHandDrag );  
    311.         this->setInteractive( false );  
    312.         lastEventCursorPos = event->pos();  
    313.     }  
    314. }  
    315.    
    316. ///<summary>  
    317. /// 鼠标移动事件  
    318. ///</summary>  
    319. ///<param name="event">鼠标事件</param>  
    320. voidMapCanvas::mouseMoveEvent( QMouseEvent *event )  
    321. {  
    322.     if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )  
    323.     {  
    324.         QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;  
    325.        this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );  
    326.        this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );  
    327.         this->viewport()->setCursor(Qt::ClosedHandCursor );  
    328.     }  
    329.      
    330. }  
    331.    
    332. ///<summary>  
    333. ///鼠标按键释放事件  
    334. ///</summary>  
    335. ///<param name="event">鼠标事件</param>  
    336. voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )  
    337. {  
    338.     if ( event->button() == Qt::MidButton )  
    339.     {  
    340.         this->setDragMode(QGraphicsView::NoDrag );  
    341.     }  
    342. }  
  • 相关阅读:
    【动态规划】数的划分
    【动态规划】开心的小明
    【动态规划】回文字符串
    【动态规划】skiing_深度搜索_动态规划
    【动态规划】最大子串和
    JDBC中 execute 与 executeUpdate的区别
    poj 2449 Remmarguts' Date 求第k短路 Astar算法
    ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer 最大生成树 lca
    hdu 2586 How far away ? 倍增求LCA
    acm模板生成
  • 原文地址:https://www.cnblogs.com/zfluo/p/5131828.html
Copyright © 2011-2022 走看看