• GDAL算法进度条使用说明


        在调用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.  
        上面的意思大概就是说,参数pfnProgress 是一个回调函数指针,回调函数定义参考 GDALProgressFunc(),参数pProgressArg 是回调函数pfnProgress 的第三个参数。关于GDALProgressFunc()的定义如下:

      int (*GDALProgressFunc)(double dfComplete, const char *pszMessage, void *pProgressArg);

        该回调函数参数说明分别是:

      dfComplete  进度值,取值范围是0.0~1.0,0.0表示开始,1.0表示完成。
      pszMessage  可选项,用来显示的字符串信息。通常用NULL即可。
      pProgressArg  应用程序回调函数参数,是一个自定义的类(或结构体)指针。
        下面以GDAL自带的进度函数GDALTermProgress为例,进行说明。GDALTermProgress的源代码在GDALSrc\gcore\gdal_misc.cpp,762行左右(GDAL1.8.1版本),代码如下:

    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;
    	}
    
    //后面省略部分代码
        终于写完了。好长啊~~~

  • 相关阅读:
    CF1648D Serious Business
    [学习笔记]后缀相关算法
    ARC163E Noncoprime DAG
    [学习笔记\练习记录]特殊动态规划 及 动态规划的一些优化技巧
    ZR省选十连测
    [练习记录]字符串
    MySQL配置文件查找路径
    MySQL最优配置模板( 5.6&5.7)
    mongo
    elasticsearch中max_result_window默认10000限制修改
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6314032.html
Copyright © 2020-2023  润新知