在调用GDAL算法的时候,希望能够显示其处理进度信息,其实在GDAL的算法API中,一般最后两个参数就是进度信息的指针。下面分别实现两种进度条信息,一种是在控制台中的进度条,一种是基于QT界面的进度条(你可以参考写一个MFC的)。
对于GDAL来说,本身就实现了一个基于控制台的进度条函数,名字叫GDALTermProgress,其函数说明参考这里 ,调用这个进度函数后,会在控制台中显示一个进度信息,形状大概就是下面的样子:
0...10...20...30...40...50...60...70...80...90...100 - done.每个2.5%就会输出一个点,然后每到10%就会输出数字,在完成结束后会输出done。
GDAL的算法中,一般最后两个参数是用来返回进度信息的,下面以栅格矢量化的接口举例说明,GDAL栅格矢量化的函数接口定义为:
CPLErr GDALPolygonize | ( | GDALRasterBandH | hSrcBand, | |
GDALRasterBandH | hMaskBand, | |||
OGRLayerH | hOutLayer, | |||
int | iPixValField, | |||
char ** | papszOptions, | |||
GDALProgressFunc | pfnProgress, | |||
void * | pProgressArg | |||
) |
算法说明文档参考地址:http://www.gdal.org/gdal__alg_8h.html#a3f522a9035d3512b5d414fb4752671b1。从文档中可以看到,最后两个参数说明是:
pfnProgress | callback for reporting algorithm progress matching the GDALProgressFunc() semantics. May be NULL. | |
pProgressArg | callback argument passed to pfnProgress. |
int (*GDALProgressFunc)(double dfComplete, const char *pszMessage, void *pProgressArg);
该回调函数参数说明分别是:
dfComplete | 进度值,取值范围是0.0~1.0,0.0表示开始,1.0表示完成。 | |
pszMessage | 可选项,用来显示的字符串信息。通常用NULL即可。 | |
pProgressArg | 应用程序回调函数参数,是一个自定义的类(或结构体)指针。 |
int CPL_STDCALL GDALTermProgress( double dfComplete, const char *pszMessage, void * pProgressArg ) { static int nLastTick = -1; int nThisTick = (int) (dfComplete * 40.0); (void) pProgressArg; nThisTick = MIN(40,MAX(0,nThisTick)); // Have we started a new progress run? if( nThisTick < nLastTick && nLastTick >= 39 ) nLastTick = -1; if( nThisTick <= nLastTick ) return TRUE; while( nThisTick > nLastTick ) { nLastTick++; if( nLastTick % 4 == 0 ) fprintf( stdout, "%d", (nLastTick / 4) * 10 ); else fprintf( stdout, "." ); } if( nThisTick == 40 ) fprintf( stdout, " - done.\n" ); else fflush( stdout ); return TRUE; }可以看出,上面的函数中,只用了第一个参数,后面两个参数没用,就是将第一个参数使用printf函数输出在屏幕上。基于上面的分析,可以自己实现一个自己的GDAL进度条类,首先看类声明:
/** * @brief 进度条基类 * * 提供进度条基类接口,来反映当前算法的进度值 */ class IMGALG_API CProcessBase { public: /** * @brief 构造函数 */ CProcessBase() { m_dPosition = 0.0; m_iStepCount = 100; m_iCurStep = 0; m_bIsContinue = true; } /** * @brief 析构函数 */ virtual ~CProcessBase() {} /** * @brief 设置进度信息 * @param pszMsg 进度信息 */ virtual void SetMessage(const char* pszMsg) = 0; /** * @brief 设置进度值 * @param dPosition 进度值 * @return 返回是否取消的状态,true为不取消,false为取消 */ virtual bool SetPosition(double dPosition) = 0; /** * @brief 进度条前进一步,返回true表示继续,false表示取消 * @return 返回是否取消的状态,true为不取消,false为取消 */ virtual bool StepIt() = 0; /** * @brief 设置进度个数 * @param iStepCount 进度个数 */ virtual void SetStepCount(int iStepCount) { ReSetProcess(); m_iStepCount = iStepCount; } /** * @brief 获取进度信息 * @return 返回当前进度信息 */ string GetMessage() { return m_strMessage; } /** * @brief 获取进度值 * @return 返回当前进度值 */ double GetPosition() { return m_dPosition; } /** * @brief 重置进度条 */ void ReSetProcess() { m_dPosition = 0.0; m_iStepCount = 100; m_iCurStep = 0; m_bIsContinue = true; } /*! 进度信息 */ string m_strMessage; /*! 进度值 */ double m_dPosition; /*! 进度个数 */ int m_iStepCount; /*! 进度当前个数 */ int m_iCurStep; /*! 是否取消,值为false时表示计算取消 */ bool m_bIsContinue; };该类是一个进度条基类,将一些通用的函数进行实现,比如获取进度值,获取进度信息等之类的,然后将一些需要根据使用方式不同的设置为虚函数,比如设置进度值等。下面给出一个控制台进度条类,用于显示基于控制台程序的进度值显示,参考GDALTermProgress函数写的:
/** * @brief 控制台进度条类 * * 提供控制台程序的进度条类接口,来反映当前算法的进度值 */ class CConsoleProcess : public CProcessBase { public: /** * @brief 构造函数 */ CConsoleProcess() { m_dPosition = 0.0; m_iStepCount = 100; m_iCurStep = 0; }; /** * @brief 析构函数 */ ~CConsoleProcess() { //remove(m_pszFile); }; /** * @brief 设置进度信息 * @param pszMsg 进度信息 */ void SetMessage(const char* pszMsg) { m_strMessage = pszMsg; printf("%s\n", pszMsg); } /** * @brief 设置进度值 * @param dPosition 进度值 * @return 返回是否取消的状态,true为不取消,false为取消 */ bool SetPosition(double dPosition) { m_dPosition = dPosition; TermProgress(m_dPosition); m_bIsContinue = true; return true; } /** * @brief 进度条前进一步 * @return 返回是否取消的状态,true为不取消,false为取消 */ bool StepIt() { m_iCurStep ++; m_dPosition = m_iCurStep*1.0 / m_iStepCount; TermProgress(m_dPosition); m_bIsContinue = true; return true; } private: void TermProgress(double dfComplete) { static int nLastTick = -1; int nThisTick = (int) (dfComplete * 40.0); nThisTick = MIN(40,MAX(0,nThisTick)); // Have we started a new progress run? if( nThisTick < nLastTick && nLastTick >= 39 ) nLastTick = -1; if( nThisTick <= nLastTick ) return ; while( nThisTick > nLastTick ) { nLastTick++; if( nLastTick % 4 == 0 ) fprintf( stdout, "%d", (nLastTick / 4) * 10 ); else fprintf( stdout, "." ); } if( nThisTick == 40 ) fprintf( stdout, " - done.\n" ); else fflush( stdout ); } };至此,一个控制台的进度条就完成了,使用方式如下(注意:之前的博客中出现的LT_ConsoleProgress就是上面这个类):
void main() { CConsoleProcess *pProgress = new CConsoleProcess(); //参考建立Erdas金字塔的那篇博客:http://blog.csdn.net/liminlu0314/article/details/6127755 int f = CreatePyramids("C://Work//Data//ttttt.img", pProgress); if (f == RE_SUCCESS) printf("计算成功/n"); else printf("计算失败/n"); delete pProgress; }
效果图如下:
下面开始编写一个基于QT界面的进度条类,首先是类定义,基于QT界面的,对于MFC界面的可以参考这个写一个:
/** * @brief 进度条对话框类 * * 提供GUI程序的进度条类接口,来反映当前算法的进度值 */ class CProcessDlg : public QProgressDialog, public CProcessBase { Q_OBJECT public: /** * @brief 构造函数 */ CProcessDlg(QWidget *parent = 0); /** * @brief 析构函数 */ ~CProcessDlg(); /** * @brief 设置进度信息 * @param pszMsg 进度信息 */ void SetMessage(const char* pszMsg); /** * @brief 设置进度值 * @param dPosition 进度值 */ bool SetPosition(double dPosition); /** * @brief 进度条前进一步 */ bool StepIt(); public slots: void updateProgress(int); };接下来是实现部分代码,具体和上面的控制台类一样,只不过是设置进度值都是设置在界面上的相应的控件中:
/** * @brief 构造函数 */ CProcessDlg::CProcessDlg(QWidget *parent) :QProgressDialog(parent) { m_dPosition = 0.0; m_iStepCount = 100; m_iCurStep = 0; setModal(true); setLabelText(tr("处理中...")); setAutoClose(false); setAutoReset(false); setCancelButtonText(tr("取消")); setWindowTitle(tr("进度")); //禁用关闭按钮 setWindowFlags(Qt::WindowTitleHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); }; /** * @brief 析构函数 */ CProcessDlg::~CProcessDlg() { }; /** * @brief 设置进度信息 * @param pszMsg 进度信息 */ void CProcessDlg::SetMessage(const char* pszMsg) { if (pszMsg != NULL) { m_strMessage = pszMsg; setLabelText(QString(pszMsg)); } } /** * @brief 设置进度值 * @param dPosition 进度值 */ bool CProcessDlg::SetPosition(double dPosition) { m_dPosition = dPosition; setValue( std::min( 100u, ( uint )( m_dPosition*100.0 ) ) ); QCoreApplication::instance()->processEvents(); if(this->wasCanceled()) return false; return true; } /** * @brief 进度条前进一步,返回false表示终止操作 */ bool CProcessDlg::StepIt() { m_iCurStep ++; m_dPosition = m_iCurStep*1.0 / m_iStepCount; setValue( std::min( 100u, ( uint )( m_dPosition*100.0 ) ) ); QCoreApplication::instance()->processEvents(); if(this->wasCanceled()) return false; return true; } void CProcessDlg::updateProgress(int step) { this->setValue(step); QCoreApplication::instance()->processEvents(); }至此,QT界面的进度条类已经完成,调用方式和上面控制台的基本类似,示例代码:
//省略部分代码 CProcessDlg *pPro = new CProcessDlg(); pPro->setWindowTitle(tr("正在执行栅格矢量化")); pPro->show(); int iRev = ImagePolygonize(m_strInputName.c_str(), m_strOutputName.c_str(), pszFormat, pPro); delete pPro;
这样,我们基本上就实现了两个进度条,一个控制台,一个QT界面的。执行的效果图如下:
那么在自己的算法中如何使用这个进度条类呢,下面给出一个简单的算法,图像反色算法:
/** * @brief 图像反色 * @param pszSrcFile 输入文件路径 * @param pszDstFile 输出文件路径 * @param pszFormat 输出文件格式,详细参考GDAL支持数据类型 * @param pProcess 进度条指针 * @return 返回值,表示计算过程中出现的各种错误信息 */ int ImageAnticolor(const char* pszSrcFile, const char* pszDstFile, const char* pszFormat, CProcessBase* pProcess = NULL) { if(pProcess != NULL) { pProcess->ReSetProcess(); pProcess->SetMessage("执行图像反色..."); } if(pszSrcFile == NULL || pszDstFile == NULL) return RE_PARAMERROR; GDALAllRegister(); GDALDataset *poSrcDS = (GDALDataset *) GDALOpen( pszSrcFile, GA_ReadOnly ); if( poSrcDS == NULL ) { if(pProcess != NULL) pProcess->SetMessage("输入文件不能打开,请检查文件是否存在!"); return RE_FILENOTEXIST; } GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); if( poDriver == NULL ) { if(pProcess != NULL) pProcess->SetMessage("不能创建制定类型的文件,请检查该文件类型GDAL是否支持创建!"); GDALClose( (GDALDatasetH) poSrcDS ); return RE_FILENOTSUPPORT; } //获取图像宽高和波段数 int iXSize = poSrcDS->GetRasterXSize(); int iYSize = poSrcDS->GetRasterYSize(); int iBandCount = poSrcDS->GetRasterCount(); //假设输入图像也是8U的数据 GDALDataset *poDstDS = poDriver->Create(pszDstFile, iXSize, iYSize, iBandCount, GDT_Byte, NULL); double dGeoTrans[6] = {0}; //设置仿射变换参数 poSrcDS->GetGeoTransform(dGeoTrans); poDstDS->SetGeoTransform(dGeoTrans); //设置图像投影信息 poDstDS->SetProjection(poSrcDS->GetProjectionRef()); DT_8U *pSrcData = new DT_8U[iXSize]; DT_8U *pDstData = new DT_8U[iXSize]; if(m_pProcess != NULL) { m_pProcess->SetMessage("计算图像反色..."); m_pProcess->SetStepCount(iYSize*iBandCount); //设置进度条的总步长 } //循环波段 for(int iBand=1; iBand<=iBandCount; iBand++) { GDALRasterBand *pSrcBand = poSrcDS->GetRasterBand(iBand); GDALRasterBand *pDstBand = poDstDS->GetRasterBand(iBand); for(int i=0; i<iYSize; i++) //循环图像高 { pSrcBand->RasterIO(GF_Read, 0, i, iXSize, 1, pSrcData, iXSize, 1, GDT_Byte, 0, 0); for(int j=0; j<iXSize; j++) //循环图像宽 pDstData[j] = 255 - pSrcData[j]; pDstBand->RasterIO(GF_Write, 0, i, iXSize, 1, pDstData, iXSize, 1, GDT_Byte, 0, 0); if(m_pProcess != NULL) { bool bIsCancel = m_pProcess->StepIt(); if(!bIsCancel) { RELEASE(pSrcData); RELEASE(pDstData); //关闭原始图像和结果图像 GDALClose( (GDALDatasetH) poDstDS ); GDALClose( (GDALDatasetH) poSrcDS ); remove(pszDstFile); //删除结果图像 if(pProcess != NULL) pProcess->SetMessage("图像反色取消!"); return RE_CANCEL; } } } } RELEASE(pSrcData); RELEASE(pDstData); //关闭原始图像和结果图像 GDALClose( (GDALDatasetH) poDstDS ); GDALClose( (GDALDatasetH) poSrcDS ); if(pProcess != NULL) pProcess->SetMessage("图像反色完成!"); return RE_SUCCESS; }如果我要使用GDAL提供的算法,该怎么使用上面的进度条呢,很简单,具体是要实现一个GDALProgressFunc()函数相同的一个使用__stdcall导出的函数即可,函数定义如下:
/** * @brief 导出符号定义 */ #ifndef STD_API #define STD_API __stdcall #endif /** * \brief 调用GDAL进度条接口 * * 该函数用于将GDAL算法中的进度信息导出到CProcessBase基类中,供给界面显示 * * @param dfComplete 完成进度值,其取值为 0.0 到 1.0 之间 * @param pszMessage 进度信息 * @param pProgressArg CProcessBase的指针 * * @return 返回TRUE表示继续计算,否则为取消 */ int STD_API ALGTermProgress( double dfComplete, const char *pszMessage, void * pProgressArg );下面为该函数的实现代码,请重点看第三个参数的使用方式:
/** * \brief 调用GDAL进度条接口 * * 该函数用于将GDAL算法中的进度信息导出到CProcessBase基类中,供给界面显示 * * @param dfComplete 完成进度值,其取值为 0.0 到 1.0 之间 * @param pszMessage 进度信息 * @param pProgressArg CProcessBase的指针 * * @return 返回TRUE表示继续计算,否则为取消 */ int STD_API ALGTermProgress( double dfComplete, const char *pszMessage, void * pProgressArg ) { if(pProgressArg != NULL) { CProcessBase * pProcess = (CProcessBase*) pProgressArg; pProcess->m_bIsContinue = pProcess->SetPosition(dfComplete); if(pProcess->m_bIsContinue) return TRUE; else return FALSE; } else return TRUE; }稍微对上面的函数体进行说明一下,关键是第三个参数,第三个参数其实就是我们上面定义的控制台类获取QT界面类的对象指针,只不过是专为一个void指针,在函数体中,我们需要将其转为基类CProcessBase的指针,然后调用该基类的设置进度值函数即可。对于该函数的使用,下面还是以栅格矢量化为例来进行说明,废话不多说,看代码:
//前面省略很多代码 GDALRasterBandH hSrcBand = (GDALRasterBandH)poSrcDS->GetRasterBand(1); if(GDALPolygonize(hSrcBand, NULL, (OGRLayerH)poLayer, 0, NULL, ALGTermProgress, pProcess)!= CE_None) { GDALClose( (GDALDatasetH) poSrcDS ); if(pProcess != NULL) { if(!pProcess->m_bIsContinue) { pProcess->SetMessage("计算取消!"); return RE_USERCANCEL; } else { pProcess->SetMessage("计算失败!"); return RE_PARAMERROR; } } return RE_PARAMERROR; } //后面省略部分代码终于写完了。好长啊~~~