近期,在做一个水印系统,原本只应用opencv处理普通图像就好了,但老师要求还得能处理遥感瓦片数据,接下来就开始了一段痛苦的旅程,当然最后问题都解决了,现在就是做个总结。
1,处理遥感图像,必须要用GDAL!!
(1)如果只是查看8位遥感图像,可以只使用opencv,PIL或者Tiff库直接读取;
(2)如果只是查看16位遥感图像,可以先进行数据拉伸降到8位,然后使用opencv,PIL或者Tiff库直接读取;
def Tiff16to8bit(img_16): if np.max(img_16) - np.min(img_16) != 0: # img_nrm = (img_16 - np.min(img_16)) / (np.max(img_16) - np.min(img_16)) #计算灰度范围,归一化 img_nrm = normalization(img_16) img_8 = np.uint8(255 * img_nrm) return img_8
但这样显示的图像会与使用envi显示的有所差别。(当然是以envi为准!)
注:这是以前的草稿,现在说明一下,16位降8位,理论上正确的做法是找到当前波段的最大值和最小值,以二者之差作为整个区间,计算当前像素所占比例,再以此比例乘以255,但是得到的结果还是会有较大的图像损失。还有的博客提到利用gdal.Translate来实现,我试了下,结果比自己按照原理写的效果要好一些,但这个涉及到很多参数,所以暂时没有深入研究。
(3)如果要对16位遥感图像的像素值进行处理,比如加水印,就一定要用gdal。遥感图像包含图像信息和地理信息,我们单取其图像信息进行一系列操作,最后再与地理信息结合,就得到了处理后的遥感图像。
读操作:
def readTif(fileName): dataset = gdal.Open(fileName) if dataset == None: print(fileName + "文件无法打开") return return dataset # im_width = dataset.RasterXSize #栅格矩阵的列数 # im_height = dataset.RasterYSize #栅格矩阵的行数 # im_bands = dataset.RasterCount #波段数 # im_data = dataset.ReadAsArray(0,0,im_width,im_height)#获取数据 # im_geotrans = dataset.GetGeoTransform()#获取仿射矩阵信息 # im_proj = dataset.GetProjection()#获取投影信息 # im_blueBand = im_data[0,0:im_height,0:im_width]#获取蓝波段 # im_greenBand = im_data[1,0:im_height,0:im_width]#获取绿波段 # im_redBand = im_data[2,0:im_height,0:im_width]#获取红波段 # im_nirBand = im_data[3,0:im_height,0:im_width]#获取近红外波段
想返回哪个值就解开哪个值。
写操作:
def writeTiff(im_data, im_width, im_height, im_bands, im_geotrans, im_proj, path): if 'int8' in im_data.dtype.name: datatype = gdal.GDT_Byte elif 'int16' in im_data.dtype.name: datatype = gdal.GDT_UInt16 else: datatype = gdal.GDT_Float32 if len(im_data.shape) == 3: im_bands, im_height, im_width = im_data.shape else: im_bands, (im_height, im_width) = 1, im_data.shape # 创建文件 driver = gdal.GetDriverByName("GTiff") dataset = driver.Create(path, im_width, im_height, im_bands, datatype) if dataset is not None: dataset.SetGeoTransform(im_geotrans) # 写入仿射变换参数 dataset.SetProjection(im_proj) # 写入投影 for i in range(im_bands): dataset.GetRasterBand(i + 1).WriteArray(im_data[i]) del dataset
要强调的一点是,程序不是死板的,有时候不用函数,而把其分解开,只取所需,会更有利于你实现运行效果,比如,我需要对某个通道进行操作而不是全部,这样在写操作时,明显就是后者的思想更方便。
2,进入正题,xx.py程序在pycharmde 终端中输入python xx.py可以正常运行,但打包后,cmd运行exe就显示缺失模块。
原因在很多博客中都说的很清楚了,就是运行方式不同,导致路径搜索的方式不同。网上关于这个问题的所有帖子我几乎都看过了,方法也几乎都试过了,但对我都没有用。这里将几种方法罗列一下,也许某种对大家有用呢。
方法(1)在源程序中添加搜索路径
import sys import os
curPath = os.path.abspath(os.path.dirname(__file__)) rootPath = os.path.split(curPath)[0] sys.path.append(rootPath)
或
curPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(curPath)
等等类似的。
这里要强调的是,这部分一定要在源程序中,添加到显示缺失的库之前。比如我缺失的是osgeo._gdal,那么‘from osgeo import gdal’ 就一定要在上面这部分程序的下面。
方法(2)修改spec文件。
在spec文件中
在红框部分,添加缺失的库,比如我添加了hiddenimport=['osgeo','gdal','osgeo._gdal'],然后直接pyinstaller xx.spec。当然是有效果的,报错信息不在命令行中显示了,而是弹窗显示。
方法(3)在F:\Anaconda3\Lib\site-packages中,自己添加xx.pth文件。
这个方法与导入自定义库的方法一致。先新建一个.txt文件,里面的内容存放缺失的库的绝对路径
然后另存的时候,类型选择*.*全部文件,只修改后缀为.pth即可。
当然,也是没效果啦。
方法(4)在环境变量中添加PYTHONPTH。
或者设置USE_PATH_FOR_GDAL_PAYTHON=YES
确实 还是不行。
方法(5)当时我已经熬到了晚上快3点,最后索性直接all in。
只要是相关的环境变量路径,添加就完事了。
当时试了之后还是不行,扛不住了就关电脑睡了,第二天打开电脑不信邪 pyinstaller -F xx.py 再一次打包,再一次cmd运行,然后。。。然后就成功了。很玄学了可以说是。
以上全是本人亲身经历。其中滋味,苦不堪言,但成功的那一刻,还是开心的。极致的痛苦之后方见解脱后的幸福,人造多巴胺了属于是。希望这篇文章能帮助到大家啊~~
对了,多提一句,上述问题是去年遇到的问题,现在建议大家安装库都用anaconda,它会自动帮你找到适应你python版本的库的版本,这样程序中应该就不会出现找不到的库的情形了,至少对我而言,已经没有再遇到过这种问题了。