恢弘志士之气,不宜妄自菲薄。这篇文章主要讲述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 | |||
) |
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;
;
【GDAL算法进度条使用说明】该类是一个进度条基类,将一些通用的函数进行实现,比如获取进度值,获取进度信息等之类的,然后将一些需要根据使用方式不同的设置为虚函数,比如设置进度值等。下面给出一个控制台进度条类,用于显示基于控制台程序的进度值显示,参考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();
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 = https://www.songbingjia.com/android/new DT_8U[iXSize];
DT_8U *pDstData = https://www.songbingjia.com/android/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 pProgressArgCProcessBase的指针
*
* @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 pProgressArgCProcessBase的指针
*
* @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;
//后面省略部分代码
终于写完了。好长啊~~~
推荐阅读
- oeasy教您玩转vim - 80 - # 宏macro
- 带你认识7种云化测试武器
- 汇编语言作业
- 图解四种 IO 模型
- 《Java语言程序设计》大作业报告九宫格游戏
- 2022/1/4TCP和websocket通讯
- Python中的图形绘制——3D绘图
- MongoDB常用命令
- Shell脚本练习题 一(if case)