GDAL 做影像校正, 支持RPC和GCP

由於公司基礎軟件一直缺乏影像校正能力,遂基於GDAL寫了一個小程序,可以做小數據的影像校正, 網上搜到的都是驟點校正,速度太慢,本程序使用全內存模式, 校正個幾個G的數據 的效率比原生GDAL提供的gdalWarp.exe還要快,主要代碼如下:

主要函數聲明:

	int ImageWarpRCPorGCP2(GDALDatasetH hSrcDS,
		const char * pszDstFile,
		RasterWarpInfo & info,
		int blockw = 256,
		int blockh = 256,
		int transtype = 0, /*0 RPC, 1 GCP*/
		int iOrder = 0,
		double dResX = 0.0,
		double dResY = 0.0,
		GDALRIOResampleAlg eResampleMethod = GRIORA_NearestNeighbour,
		const char * pszFormat = "GTiff");

結構體聲明:

///\brief 柵格類基本信息
struct  RasterWarpInfo
{

	///\brief src X,Y維度範圍
	double			XYDomain[4] = {0};
	///\brief dst X,Y維度範圍
	double			dstXYDomain[4] = { 0 };

	///\brief src X,Y維度範圍
	double			Transform[6] = { 0 };
	///\brief dst X,Y維度範圍
	double			dstTransform[6] = { 0 };

	void*	pGDALRPCorGCPTransform = 0;
	///\brief 像素寬度
	int				Width =0;
	///\brief 像素高度
	int				Height = 0;

	///\brief 像素寬度
	int				dstWidth = 0;
	///\brief 像素高度
	int				dstHeight = 0;

	///\brief 數據存儲塊寬度大小
	int				BlockWidth = 0;
	///\brief 數據存儲塊高度大小
	int				BlockHeight =0;

	///\brief 波段數據類型
	GDALDataType DataType = GDT_Byte;

	///\brief 波段的名稱
	std::vector<GDALColorInterp> BandTypes = {};;
	//空間參考
	char * srcRef = 0;;
	char * dstRef = 0;;

	int nPixelSize = 0;

	std::vector<int>	m_vBand = {};
};

 

主要實現:

/**
* @brief 影像幾何校正
* @param GDALDatasetH hSrcDS,			輸入數據集
* @param pszDstFile			輸出文件路徑
* @param RasterWarpInfo		校正信息
* @param	int blockw ,分塊大小
* @param	int blockh ,分塊大小
* @param	int transtype,RPC或者GCP
* @param	int iOrder,
* @param	double dResX, 分辨率  目前爲原始分辨率
* @param	double dResY 分辨率 目前爲原始分辨率
* @param
* @param eResampleMethod	重採樣方式
* @param @param pszFormat			輸出文件格式
* * @return  返回值,返回true或者false
* */
int CImageCorrectionDlg::ImageWarpRCPorGCP2(GDALDatasetH hSrcDS,
	const char * pszDstFile,
	RasterWarpInfo & info,
	int blockw ,
	int blockh ,
	int transtype,
	int iOrder,
	double dResX,
	double dResY,
	GDALRIOResampleAlg eResampleMethod,
	const char * pszFormat)
{

	CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
	GDALDataType eDataType = info.DataType;
	int nBandCount = info.BandTypes.size();

	// 創建幾何多項式座標轉換關係
	void *hTransform = info.pGDALRPCorGCPTransform;
	if (NULL == hTransform)
	{
		GDALClose(hSrcDS);
		return 0;
	}

	// 計算輸出圖像四至範圍、大小、仿射變換六參數等信息
	double* adfGeoTransform = info.dstTransform;
	double* adfExtent = info.dstXYDomain;
	int    nPixels = info.dstWidth, nLines = info.dstHeight;

	// 以下開始依據用戶指定的分辨率來反算輸出圖像的大小和六參數等信息
	//double dResXSize = dResX;
	//double dResYSize = dResY;

	//dResXSize = (info.dstTransform[1]);
	//dResYSize = (info.dstTransform[5]);


	// 計算輸出圖像的範圍
	//double minX = adfGeoTransform[0];
	//double maxX = adfGeoTransform[0] + adfGeoTransform[1] * nPixels;
	//double maxY = adfGeoTransform[3];
	//double minY = adfGeoTransform[3] + adfGeoTransform[5] * nLines;

	//nPixels = ceil((maxX - minX) / dResXSize);
	//nLines = abs(ceil((minY - maxY) / dResYSize));
	//adfGeoTransform[0] = minX;
	//adfGeoTransform[3] = maxY;
	//adfGeoTransform[1] = dResXSize;
	//adfGeoTransform[5] = dResYSize;

	// 創建輸出圖像
	GDALDriverH hDriver = GDALGetDriverByName(pszFormat);
	if (NULL == hDriver)
	{
		::MessageBox(this->m_hWnd, _T("獲取驅動失敗"), _T("錯誤"),
			MB_ICONERROR);
		return  0;
	}
	GDALDatasetH hDstDS = GDALCreate(hDriver, pszDstFile, nPixels, nLines, nBandCount, eDataType, NULL);
	if (NULL == hDstDS)
	{
		::MessageBox(this->m_hWnd, _T("創建目標文件失敗"), _T("錯誤"),
			MB_ICONERROR);
		return  0;
	}
	GDALDataset* gsDS = (GDALDataset*)hSrcDS;
	char* pszWkt1 = (char*)gsDS->GetProjectionRef();
	if (0 == strlen(pszWkt1))
	{
		pszWkt1 = (char*)gsDS->GetGCPProjection();
		if (0 == strlen(pszWkt1))
		{
			pszWkt1 = "GEOGCS[\"GCS_WGS_1984\", DATUM[\"D_WGS_1984\", SPHEROID[\"WGS_1984\", 6378137, 298.257223563]], PRIMEM[\"Greenwich\", 0], UNIT[\"degree\", 0.0174532925199433]]";
		}
	}
	GDALSetProjection(hDstDS, pszWkt1);
	GDALSetGeoTransform(hDstDS, adfGeoTransform);// info.dstTransform);



	//獲得原始圖像的行數和列數
	int nXsize = GDALGetRasterXSize(hSrcDS);
	int nYsize = GDALGetRasterYSize(hSrcDS);

	//然後是圖像重採樣
	int nFlag = 0;
	float dfValue = 0;
	CPLErr err = CE_Failure;
	//進度
	INIT_RASTERIO_EXTRA_ARG(m_Arg);
	m_Arg.eResampleAlg = GRIORA_NearestNeighbour;
	m_Arg.pfnProgress = RasterIOProgress;
	//m_Arg.pProgressData = pClass;

	long long srcLen = (long long)info.nPixelSize* info.Width * info.Height;
	unsigned char* srcbuff = (unsigned char*)malloc(srcLen);

	long long targetLen = (long long)info.nPixelSize * info.dstWidth* info.dstHeight;
	unsigned char* tgbuff = (unsigned char*)malloc(targetLen);
	memset(tgbuff, 0, sizeof(unsigned char));
	GDALDataset* srcDS = (GDALDataset*)hSrcDS;
	GDALDataset* tgDS = (GDALDataset*)hDstDS;
	for (int i = 0; i < tgDS->GetRasterCount(); i++)
	{
		tgDS->GetRasterBand(i + 1)->SetNoDataValue(0.);
	}
	//直接讀取所有原始數據,  
	 err = srcDS->RasterIO(GF_Read, 0, 0, info.Width, info.Height, srcbuff,
		info.Width, info.Height, info.DataType, info.BandTypes.size(), &info.m_vBand[0],
		info.nPixelSize, info.nPixelSize * info.Width,
		info.nPixelSize / info.m_vBand.size(), &m_Arg);

	//unsigned char* emptyvalue = (unsigned char*)malloc(info.nPixelSize*8);
	//memset(emptyvalue, 0, info.nPixelSize * 8);
	//遍歷目標像素值,給目標像素buff賦值

	 nPixels = info.dstWidth; 
	 nLines = info.dstHeight;
#pragma omp parallel for
	for (int nRow = 0; nRow < nLines; nRow++)
	{
		for (int nCol = 0; nCol < nPixels; nCol++)
		{
			double dbX = adfGeoTransform[0] + nCol * adfGeoTransform[1]
				+ nRow * adfGeoTransform[2];
			double dbY = adfGeoTransform[3] + nCol * adfGeoTransform[4]
				+ nRow * adfGeoTransform[5];

			//由輸出的圖像地理座標系變換到原始的像素座標系

			if (0 == transtype)
				GDALRPCTransform(hTransform, TRUE, 1, &dbX, &dbY, NULL, &nFlag);
			else if (1 == transtype)
				GDALGCPTransform(hTransform, TRUE, 1, &dbX, &dbY, NULL, &nFlag);
			int nXCol = (int)(dbX );
			int nYRow = (int)(dbY);

			//超出範圍的用0填充
			if (nXCol < 0 || nXCol >= nXsize || nYRow < 0 || nYRow >= nYsize)
			{
				int tmp = 0;
			
				tgbuff[nRow * nPixels * info.nPixelSize + nCol* info.nPixelSize]=0;
			}
			else
			{
				 memcpy(&tgbuff[nRow * nPixels * info.nPixelSize + nCol * info.nPixelSize],
					 &srcbuff[nYRow   * nXsize * info.nPixelSize + nXCol * info.nPixelSize],
					 info.nPixelSize);
	
				 //memcpy(&tgbuff[nCol*nLines + nRow], &srcbuff[nXCol  * nXsize + nYRow], sizeof(short)*4);

			}
		}
	}
	//free(emptyvalue);
	//寫好的buff直接IO進文件
	tgDS->RasterIO(GF_Write,0,0,info.dstWidth,info.dstHeight, tgbuff,
		info.dstWidth, info.dstHeight, info.DataType, info.BandTypes.size(), &info.m_vBand[0],
		info.nPixelSize, info.nPixelSize * info.dstWidth,
		info.nPixelSize / info.m_vBand.size(), &m_Arg);

	//err = tgDS->RasterIO(GF_Write, 0, 0, info.Width, info.Height, tgbuff,
	//	info.Width, info.Height, info.DataType, info.BandTypes.size(), &info.m_vBand[0],
	//	0,0,0, &m_Arg);

	if (hTransform != NULL)
	{
		GDALDestroyGCPTransformer(hTransform);
		hTransform = NULL;
	}

	GDALClose(hSrcDS);
	GDALClose(hDstDS);


	return 1;
}

 

 

RasterWrapInfo 的獲取如下:


int CImageCorrectionDlg::ImageCorrection(const char * pszSrcFile, const char * pszDstFile)
{	//數據集。
	GDALDataset* m_pDS = (GDALDataset *)GDALOpen(pszSrcFile, GA_Update);
	if (m_pDS == NULL)
	{
		::MessageBox(this->m_hWnd, _T("打開文件失敗"), _T("錯誤"),
			MB_ICONERROR);
		return  0;
	}
	if (m_pDS == NULL)
		return 0; 
	//獲取原始信息
	m_pDS->GetGeoTransform(m_GeoTransform);
	RasterWarpInfo srcInfo;
	//memset(&srcInfo, 1, sizeof(srcInfo));
	/// \brief 像素寬度
	srcInfo.Width = m_pDS->GetRasterXSize();
	/// \brief 像素高度
	srcInfo.Height = m_pDS->GetRasterYSize();
	for (int i = 0; i < m_pDS->GetRasterCount(); i++)
	{
		srcInfo.m_vBand.emplace_back(i + 1);
		srcInfo.DataType = m_pDS->GetRasterBand(i + 1)->GetRasterDataType();
		srcInfo.BandTypes.emplace_back(m_pDS->GetRasterBand(i + 1)->GetColorInterpretation());
		m_pDS->GetRasterBand(i + 1)->GetBlockSize(&srcInfo.BlockWidth, &srcInfo.BlockHeight);
		srcInfo.nPixelSize += GDALGetDataTypeSize(srcInfo.DataType);
	}
	
	srcInfo.nPixelSize /= 8;

	char ** datalist = m_pDS->GetMetadataDomainList();

	char** papszRPC = m_pDS->GetMetadata("RPC");
	if (papszRPC)
	{
		GDALRPCInfo oInfo;
		GDALExtractRPCInfo(papszRPC, &oInfo);
		//設置RPC模型中所需的DEM路徑
		char** papszTransOption = NULL;
		double adfGeoTransform[6] = { 0 };

		int    nPixels = 0, nLines = 0;
		//使用RPC信息,DEM等構造RPC轉換參數
		m_pGDALRPCorGCPTransform = GDALCreateRPCTransformer(&oInfo, FALSE, 0, papszTransOption);
		if (m_pGDALRPCorGCPTransform != NULL)
		{
			if (GDALSuggestedWarpOutput2(m_pDS, GDALRPCTransform/*GDALRPCTransform*/, m_pGDALRPCorGCPTransform,
				m_RPCorGCPTransform, &m_nPixels, &m_nLines, m_adfExtent, 0) != CE_None)
			{

				GDALClose(m_pGDALRPCorGCPTransform);
				return 0;
			}
			srcInfo.dstHeight = m_nLines;
			srcInfo.dstWidth = m_nPixels;
			memcpy(srcInfo.dstXYDomain, m_adfExtent, sizeof(double) * 4);
			memcpy(srcInfo.dstTransform, m_RPCorGCPTransform, sizeof(double) * 6);
			srcInfo.pGDALRPCorGCPTransform = m_pGDALRPCorGCPTransform;
		}

		int nGCPCount = m_pDS->GetGCPCount();

		const GDAL_GCP *pGCPList = m_pDS->GetGCPs();
		if (nGCPCount > 0)
		{
			m_pGDALRPCorGCPTransform = GDALCreateGCPTransformer(nGCPCount, pGCPList, 0, FALSE);

			if (GDALSuggestedWarpOutput2(m_pDS, GDALGCPTransform, m_pGDALRPCorGCPTransform,
				m_RPCorGCPTransform, &m_nPixels, &m_nLines, m_adfExtent, 0) != CE_None)
			{
				GDALClose(m_pDS);
				return 0;
			}
			srcInfo.dstHeight = m_nLines;
			srcInfo.dstWidth = m_nLines;
			memcpy(srcInfo.dstXYDomain, m_adfExtent, sizeof(double) * 4);
			memcpy(srcInfo.dstTransform, m_RPCorGCPTransform, sizeof(double) * 6);
			srcInfo.pGDALRPCorGCPTransform = m_pGDALRPCorGCPTransform;
		}
	}

	//如果沒有空間參考則讀取空間參考
	const char*pSrsRef = m_pDS->GetProjectionRef();
	char* pszWkt1 = (char*)m_pDS->GetGCPProjection();
	

	//ImageWarpRCPorGCP(m_pDS, pszDstFile, srcInfo);

	return ImageWarpRCPorGCP2(m_pDS, pszDstFile, srcInfo);
}

 

 

此程序未做分塊處理,分塊和這整塊也無太大區別, 因爲是測試程序,所以不做過多代碼優化,源碼下載請到我的資源下載,只不過是個mfc的測試 程序,代碼較亂,請理解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章