OpenCV实现了很多图像处理中的算法,极大的降低了我们研究中码代码的工作量,可以把更多的精力放在算法设计上。
Mat是OpenCV中最常见的数据格式,可以直接用imread函数将图像数据读取并存储为Mat格式。Mat型图像矩阵可以直接应用于OpenCV的大多数算法,例如图像形态学操作、行列号访问像素、仿射变换等。
最近在研究中,我需要读取一些多波段的tiff影像,OpenCV读取tiff的时候,有个波段数≤4的限制,这个在面对多光谱数据的时候,有点不方便。一开始我是将多波段的tiff用ENVI分成多个4波段的tiff文件导出,再多次读取,也将就着完成了研究需求。不过实现方法着实麻烦,也很不优雅。
在完成了研究所需后,再返回来看这个问题本身。我需要的是将多波段的tiff读取为Mat格式。OpenCV不能读取多波段tiff,但是GDAL可以。那我可不可以写个函数,就是用GDAL读取多波段tiff,然后输出给Mat呢?
想法有了,下面就是面向搜索引擎的编程环节了。
网上看了一圈,大概思路是GDAL循环读取tiff的每个波段,然后每个波段存储为一个单波段的Mat,然后存入vector,最后返回一个成员数量与tiff波段数相同的Mat型vector。
这边有个大佬写的特别好,也很详细,不过我还没细啃,我的代码与大佬的相比,相当于从跑车上获得灵感然后造了个自行车。
下面是我的代码,定义了一个函数,并读取一张landsat8影像作为测试:
#include #include #include #include using namespace std;using namespace cv;//gdal读取tiff,转为Mat型Vector,并获取图像左上角坐标与分辨率vector ReadTiffAsMatVector(const char* in_tiff_img, double& map_tie_point_x , double& map_tie_point_y, double& map_pixel_size){ //读取数据千万记得查看数据类型 /***********************************************************/ // GCDataType:GDAL和OpenCV数据类型转换的中间格式 // GC_Byte ======= GDT_Byte ======= CV_8U ======= unsigned char // GC_UInt16 ======= GDT_UInt16 ======= CV_16U ======= unsigned short // GC_Int16 ======= GDT_Int16 ======= CV_16S ======= short int // GC_UInt32 ======= GDT_UInt32 ======= 缺失 ======= unsigned long // GC_Int32 ======= GDT_Int32 ======= CV_32S ======= long // GC_Float32======= GDT_Float32======= CV_32F ======= float // GC_Float64======= GDT_Float64======= CV_64F ======= double /***********************************************************/ //注册驱动 GDALAllRegister(); //读取tiff图像 GDALDataset* in_tiff_dataset = (GDALDataset*)GDALOpen(in_tiff_img, GA_ReadOnly); // GDAL数据集 //获取行列数、图像左上角坐标、分辨率、波段数等基本信息 int col_num = in_tiff_dataset->GetRasterXSize(); // 列 int rol_num = in_tiff_dataset->GetRasterYSize(); // 行 int band_num = in_tiff_dataset->GetRasterCount();//波段数 double* tiff_img_geoTransform = new double[6]; in_tiff_dataset->GetGeoTransform(tiff_img_geoTransform); map_tie_point_x = tiff_img_geoTransform[0]; map_tie_point_y = tiff_img_geoTransform[3]; map_pixel_size = tiff_img_geoTransform[1]; // Mat型容器,每个元素用于存储一个波段的数据 vector img_mat_vector; // 读取每个波段的数据,注意数据类型 unsigned int* tiff_img_band_mat; // 循环读取每个波段 for (int current_band_num = 0; current_band_num < band_num; current_band_num++) { //读取第GDAL波段数从1开始 GDALRasterBand* tiff_img_band_data = in_tiff_dataset->GetRasterBand(current_band_num + 1); tiff_img_band_mat = new unsigned int[col_num * rol_num]; //读取数据 tiff_img_band_data->RasterIO(GF_Read, 0, 0, col_num, rol_num, tiff_img_band_mat, col_num, rol_num, GDT_UInt16, 0, 0); //转为Mat Mat band_mat_temp = Mat(rol_num, col_num, CV_16U, tiff_img_band_mat); //存入容器 img_mat_vector.push_back(band_mat_temp.clone()); delete[]tiff_img_band_data; band_mat_temp.release(); } return img_mat_vector;}int main(){ //新建存放数据的容器 vector cls_img_band_vector; //新建用于接收图像左上角坐标与分辨率的变量 double cls_img_tie_point_x, cls_img_tie_point_y, cls_img_pixel_size; //调用函数读取数据 cls_img_band_vector = ReadTiffAsMatVector("landsat8_test.tif", cls_img_tie_point_x, cls_img_tie_point_y, cls_img_pixel_size); cout << "读取的tiff波段数为:" << cls_img_band_vector.size() << endl; cout << "图像左上角坐标为:" << cls_img_tie_point_x < out_img_mat(3); out_img_mat[0] = cls_img_band_vector[2]; out_img_mat[1] = cls_img_band_vector[3]; out_img_mat[2] = cls_img_band_vector[4]; Mat out_img; merge(out_img_mat, out_img); //显示 namedWindow("测试", 0); resizeWindow("测试", 640, 640); imshow("测试", out_img); waitKey(36000); return 0;}
结果如图
#科技萌新成长营##编程##OpenCV#
注意事项:注意GDAL与OpenCV的数据格式对应,以及OpenCV的波段顺序B、G、R。