两种表示方法如下:
图:坡度的两种表示方式
而坡向的定义是地面任意一点切平面法线在水平面上的投影与正北方向的方向角。坡向的计算公式如下:
由上面两个公式可以看出,都需要求fx和fy。对于这两个分量的计算方法有很多种。具体的计算方法有二阶差分、三阶差分等,更多的计算方法见下图,图中Slope-we对应fx,Slope-sn对应fy。
各个商业软件基本上都是选取上述的算法进行计算,ArcGIS采用的算法二,ERDAS采用的是算法4。本文采用算法作为计算方法。
//投影 const char* pszWkt = poSrcDS->GetProjectionRef(); double dbNres = 0; double dbEres = 0; double dfGeotrans[6]; if (poSrcDS->GetGeoTransform(dfGeotrans) != CE_None) { dbNres = fabs(dfGeotrans[5]); dbEres = fabs(dfGeotrans[1]); } else { OGRSpatialReference* pSrs = (OGRSpatialReference*)OSRNewSpatialReference(pszWkt); if (pSrs != NULL) { if (pSrs->IsGeographic()) { dbNres = fabs(dfGeotrans[5]); dbNres *= 110000; dbEres = fabs(dfGeotrans[1]); dbEres *= 110000; } else if (pSrs->IsGeocentric()) { dbNres = fabs(dfGeotrans[5]); dbEres = fabs(dfGeotrans[1]); } else if (pSrs->IsProjected()) { dbNres = fabs(dfGeotrans[5]); dbEres = fabs(dfGeotrans[1]); } } OSRDestroySpatialReference((OGRSpatialReferenceH)pSrs); } SlopeOption slopeOption; slopeOption.dbEwres = dbEres; slopeOption.dbNsres = dbNres; slopeOption.slopeType = eSlopeType;
为了方便直观表达其意思,其代码如下:
//分块处理 int nSubHeight = 2000; int nYBlockCount = (nYsize+nSubHeight-1)/nSubHeight; //计算分块的数量 for (int i = 0; i < nYBlockCount; i ++) { //实际的块的大小 int nRealWidth = nXsize; int nRealHeight = nSubHeight; //读取数据块的大小 int nReadWidth = nXsize; int nReadHeight = nSubHeight; int nYOffset = i*nSubHeight; if (1 == nYBlockCount) { nRealHeight = nYsize; nReadHeight = nRealHeight; } else { if (i == 0) { nReadHeight += 1; } else if (i > 0 && i < nYBlockCount-1) { nReadHeight += 2; nYOffset -= 1; } else if(i == nYBlockCount-1) { nRealHeight = nYsize-nSubHeight*(nYBlockCount-1); nReadHeight = nRealHeight + 1; nYOffset -= 1; } } //读取数据 float* poData = new float[nReadWidth*nReadHeight]; float* poOutData = new float[nReadWidth*nReadHeight]; poBand->RasterIO(GF_Read,0,nYOffset,nReadWidth,nReadHeight,poData,nReadWidth,nReadHeight,GDT_Float32,0,0); //中间处理过程 //写入数据 int pBandList[] = {1}; if (1 == nYBlockCount) { poDstDS->RasterIO(GF_Write,0,nYOffset,nRealWidth,nRealHeight,poOutData,nRealWidth,nRealHeight, GDT_Float32,1,pBandList,0,0,0); } else { if (i == 0) { poDstDS->RasterIO(GF_Write,0,nYOffset,nRealWidth,nRealHeight,poOutData,nRealWidth,nRealHeight, GDT_Float32,1,pBandList,0,0,0); } else if (i > 0 && i < nYBlockCount-1) { poDstDS->RasterIO(GF_Write,0,nYOffset+1,nRealWidth,nRealHeight,poOutData+nRealWidth,nRealWidth,nRealHeight, GDT_Float32,1,pBandList,0,0,0); } else if(i == nYBlockCount-1) { poDstDS->RasterIO(GF_Write,0,nYOffset+1,nRealWidth,nRealHeight,poOutData+nRealWidth,nRealWidth,nRealHeight, GDT_Float32,1,pBandList,0,0,0); } } if (poData != NULL) { delete []poData; poData = NULL; } if (poOutData != NULL) { delete []poOutData; poOutData = NULL; } }
float SlopeCal (float* afRectData, float fDstNoDataValue,void* pData) { const double radiansToDegrees = 180.0 / M_PI; SlopeOption *psData = (SlopeOption*)pData; double dx =((afRectData[0] + afRectData[3]*2 + afRectData[6]) - (afRectData[2]+ afRectData[5]*2 + afRectData[8])) / (psData->dbEwres*8); double dy =((afRectData[6] + afRectData[7]*2 + afRectData[8]) - (afRectData[0]+ afRectData[1]*2 + afRectData[2])) / (psData->dbNsres*8); double key = (dx *dx + dy * dy); if(psData->slopeType == DEGREE_SLOPE) { return (float)(atan(sqrt(key) ) * radiansToDegrees); //return key; } else if (psData->slopeType == PERCENT_SLOPE) return (float)(100*(sqrt(key) )); return 0; }
在arcgis中也基于同样的数据生成坡度数据,发现其边缘像素的值不一样,这是因为边缘处理的策略不一样导致的。
__kernel void slope_kernel( __global const float *pSrcData, __global float *pDestData,const int nWidth,const int nHeight , struct SlopeOption slopeType) { int j = (int)get_global_id(0); int i = (int)get_global_id(1); if (j >= nWidth || i >= nHeight) return; int nTopTmp = i-1; int nBottomTmp = i+1; int nLeftTep = j-1; int nRightTmp = j+1; //处理边界情况 if (0 == i) { nTopTmp = i; } if (0 == j) { nLeftTep = j; } if (i == nHeight-1) { nBottomTmp = i; } if (j == nWidth-1) { nRightTmp = j; } float dbRectData[9]; dbRectData[0] = pSrcData[nTopTmp*nWidth+nLeftTep]; dbRectData[1] = pSrcData[nTopTmp*nWidth+j]; dbRectData[2] = pSrcData[nTopTmp*nWidth+nRightTmp]; dbRectData[3] = pSrcData[i*nWidth+nLeftTep]; dbRectData[4] = pSrcData[i*nWidth+j]; dbRectData[5] = pSrcData[i*nWidth+nRightTmp]; dbRectData[6] = pSrcData[nBottomTmp*nWidth+nLeftTep]; dbRectData[7] = pSrcData[nBottomTmp*nWidth+j]; dbRectData[8] = pSrcData[nBottomTmp*nWidth+nRightTmp]; double dx = ((dbRectData[0] + dbRectData[3]*2 + dbRectData[6]) - (dbRectData[2]+ dbRectData[5]*2 + dbRectData[8])) / (slopeType.dbEwres*8); double dy =((dbRectData[6] + dbRectData[7]*2 + dbRectData[8]) - (dbRectData[0]+ dbRectData[1]*2 + dbRectData[2])) / (slopeType.dbNsres*8); double fTmp = (dx *dx + dy * dy); //计算坡度 double radiansToDegrees = 180.0/M_PI; double fValue = 0; if(slopeType.slopeType == DEGREE_SLOPE) { fValue = atan(sqrt(fTmp) ) * radiansToDegrees; } else if (slopeType.slopeType == PERCENT_SLOPE) fValue = 100*sqrt(fTmp); pDestData[i*nWidth+j] = fValue; }
//opencl平台搭建 cl_int status = 0; //状态号码 static cl_context cxGPUContext = NULL; // OpenCL context static cl_command_queue cqCommandQueue = NULL;// OpenCL command que static cl_platform_id cpPlatform = NULL; // OpenCL platform static cl_device_id cdDevice = NULL; // OpenCL device static cl_program cpProgram = NULL; // OpenCL program static cl_kernel ckKernel = NULL; // OpenCL kernel static bool bInit = 0; //是否初始化了 if (!bInit) { OpenCLInit(&cpPlatform,&cdDevice,&cxGPUContext); BuildKernel(cpPlatform,cdDevice,cxGPUContext,&cpProgram,&cqCommandQueue); ckKernel = clCreateKernel(cpProgram,"slope_kernel",&status); bInit = 1; } cl_int errNum; cl_mem bufIn = clCreateBuffer(cxGPUContext,CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR, sizeof(float)*nWidth*nHeight,poDataIn,&errNum); cl_mem bufOut = clCreateBuffer(cxGPUContext,CL_MEM_WRITE_ONLY, sizeof(float)*nWidth*nHeight,NULL,&errNum); //设置参数 status = clSetKernelArg(ckKernel,0,sizeof(cl_mem),&bufIn); status = clSetKernelArg(ckKernel,1,sizeof(cl_mem),&bufOut); status = clSetKernelArg(ckKernel,2,sizeof(cl_int),&nWidth); status = clSetKernelArg(ckKernel,3,sizeof(cl_int),&nHeight); SlopeOption slopeOpt; memcpy(&slopeOpt,pSlopeType,sizeof(SlopeOption)); status = clSetKernelArg(ckKernel,4,sizeof(SlopeOption),&slopeOpt); //执行核函数 size_t globalThreads[] = {nWidth,nHeight}; status = clEnqueueNDRangeKernel(cqCommandQueue,ckKernel,2, NULL,globalThreads,NULL,0,NULL,NULL); status = clFinish(cqCommandQueue); status = clEnqueueReadBuffer(cqCommandQueue,bufOut,CL_TRUE,0,sizeof(float)*nWidth*nHeight,poDataOut,0,NULL,NULL);
当然,最后也别忘了释放之前在GPU设备上申请的内存。
数据 CPU时间(毫秒) GPU时间(毫秒) 加速比
6001*6001 5846 1385 4.221