IDL矢量裁剪影像的代码已经有不少人写了,之前的项目中,同事也是参考了ENVI官博中的写法。后来,客户那边反馈裁剪的不准,才发现了问题,就是这些代码用简单的矢量裁剪影像没有问题,但客户那边的是河流的矢量(如下图),中间有些挖空,在转换为ROI的时候,往往不能正确裁剪。
矢量 | 代码裁剪结果 | 正确裁剪结果 |
可以看出,ROI处理的时候把三个多边形都保留了,就导致了裁剪不能去掉中间空心的内容。把同样的数据放到ENVI中裁剪,可以得到正确裁剪结果,看来ENVI是对这种含有内外环的复杂多边形转换ROI时做了处理。话不多说,下面给出解决思路和代码。
写在前面:
1、项目环境为ENVI5.3.1、IDL8.5,测试数据影像为GF2影像、部分河流矢量,投影均为UTM50N
2、项目需要不启动ENVI,执行裁剪,但希望包含进度条,因此代码中有手动创建进度条
3、ENVI5.0前IDL裁剪的主要逻辑就是将矢量转为ROI,然后将ROI生成掩膜,最后将掩膜应用到图像上
4、下面代码是ENVI5.0后的写法,主要调用ENVITASK执行,即VectorMaskRaster,详细信息可以参考ENVI帮助文档
;;;;;;ImgPath为影像路径
;;;;;;vectorPath矢量路径
;;;;;;OutPath裁剪后结果路径
pro ClipRasterByVector,ImgPath,vectorPath,OutPath
COMPILE_OPT idl2
ENVI,/RESTORE_BASE_SAVE_FILES
ENVI_BATCH_INIT
ENVI_REPORT_INIT,base=base,/finish
CATCH, errorStatus
IF (errorStatus NE 0) THEN BEGIN
CATCH, /CANCEL
ENVI_REPORT_INIT,base=base,/finish
rlt = DIALOG_MESSAGE('ClipRasterByVector ERROR:'+!ERROR_STATE.MSG,title='ERROR',/ERROR)
return
ENDIF
; ImgPath='D:\Temp1\GF2\RpcGeoGc\rpcmss.dat'
; vectorPath='D:\Temp\南京Vector\nanjingTest.shp'
; OutPath='D:\Temp2\Clip\Clip4.tif'
result=file_test(ImgPath)
if result NE 1 then begin
dlg=Dialog_Message('Input Imgfile Is Not Found!',title='Error',/Error)
return
endif
result=file_test(vectorPath)
if result NE 1 then begin
dlg=Dialog_Message('Input vectorPath Is Not Found!',title='Error',/Error)
return
endif
;获取文件后缀
filebasename=FILE_BASENAME(OutPath)
fileExtentStr=strmid(filebasename,strpos(filebasename,'.',/REVERSE_SEARCH)+1)
;获取源码路径
proPath = FILE_DIRNAME(ROUTINE_FILEPATH())+PATH_SEP()
;创建临时tmp文件夹,如果有tmp文件夹则先删除
tempFolder = proPath + 'tmp'
if file_test(tempFolder, /directory) EQ 1 then begin
FILE_DELETE,tempFolder,/RECURSIVE
endif
FILE_MKDIR,tempFolder
;打开影像
envi_open_file, ImgPath, r_fid=fid
if (fid eq -1) then begin
dlg=Dialog_Message('Input Imgfile Is Valid!',title='Error',/Error)
envi_batch_exit
endif
;检查interleave值 如果为BIL BIP 则转为BSQ 否则报错
ENVI_FILE_QUERY,fid,dims=f_dims,NB=f_nb,INTERLEAVE=interleave
f_pos=lindgen(f_nb)
if interleave NE 0 then begin
outbsqPath=tempFolder+'\bsq.dat'
ENVI_DOIT,'CONVERT_DOIT', DIMS=f_dims, FID=fid, O_INTERLEAVE=0, OUT_NAME=outbsqPath,POS=f_pos,r_fid=f_fid
envi_file_mng,id=f_fid,/Remove
ImgPath=outbsqPath
endif
envi_file_mng,id=fid,/Remove
;手动进度条
nb=100
ENVI_REPORT_INIT,['Processing Data,please wait...','InputImg File:'+ImgPath,'InputVecor File:'+vectorPath,'Output File:'+OutPath],$
title='ClipRasterByVector',$
base=base,/INTERRUPT
ENVI_REPORT_INC,base,nb
ENVI_REPORT_STAT,base,25,nb
e = ENVI(/headless)
ENVI_REPORT_STAT,base,35,nb
envi_batch_status_window, /on
Vector = e.OpenVector(vectorPath)
ENVI_REPORT_STAT,base,40,nb
vectorPrj = Vector.COORD_SYS
range = vector.Data_Range
Xmin=range[0]
Ymin=range[1]
Xmax=range[2]
Ymax=range[3]
map1x=[xmin,xmax]
map1y=[ymin,ymax]
Raster=e.openRaster(ImgPath)
ENVI_REPORT_STAT,base,45,nb
NS=raster.NCOLUMNS
NL=raster.NROWS
rasterPrj = raster.SPATIALREF
;转为栅格图像坐标
vectorPrj.ConvertMapToMap, Map1X, Map1Y, Map2X, Map2Y, rasterPrj
fid = ENVIRasterToFID(raster)
;转换为文件坐标
ENVI_CONVERT_FILE_COORDINATES, fid, XF, YF, Map2X, Map2Y
xfMin=ROUND(MIN(XF))
yfMin=ROUND(MIN(YF))
xfMax=ROUND(MAX(XF))
yfMax=ROUND(MAX(YF))
xfMin = xfMin >0
xfMax = xfMax < (NS-1)
yfMin = yfMin >0
yfMax = yfMax < (NL-1)
Subset = Raster.Subset(SUB_RECT=[xfMin, yfMin, xfMax, yfMax])
ENVI_REPORT_STAT,base,65,nb
Task = ENVITask('VectorMaskRaster')
Task.DATA_IGNORE_VALUE = 0
Task.INPUT_MASK_VECTOR = Vector
Task.INPUT_RASTER = Subset
if STRUPCASE(fileExtentStr) eq 'TIF' then begin
tmpFile = tempFolder+'\result.dat'
Task.OUTPUT_RASTER_URI = tmpFile
Task.Execute
tempRaster = Task.OUTPUT_RASTER
tempRaster.Export,outPath,'TIFF'
tempRaster.close
endif else begin
Task.OUTPUT_RASTER_URI = outPath
Task.Execute
endelse
ENVI_REPORT_STAT,base,75,nb
Subset.close
Raster.close
e.close
ENVI_REPORT_STAT,base,85,nb
ENVI_REPORT_INIT,base=base,/finish
ENVI_BATCH_EXIT
FILE_DELETE,tempFolder,/RECURSIVE
end
写在后面:
1、上述代码虽然解决了裁剪不准的问题,但混用了ENVI5.0前后两种版本的写法,实在显得不伦不类。其实我倾向于使用ENVI5.0前二次开发方式,可以不启用ENVI,又可以显示函数执行进度条,还可以掌握一些内部写法。但无奈ENVI4.X的写法,也就是ENVI官博中放出的代码,改来改去也无法解决复杂多边形的裁剪问题,如果各位有相关代码或思路,能分享一下感激不尽。(我觉得关键要点在于单一属性记录复杂多边形如何转为正确单一ROI)
2、ENVI5.0后更新了新的二次开发方式,定义了多种ENVITASK,开发来说是方便了很多,更傻瓜化了。只要把输入输出定义好就可以完成各种功能。但也更加黑盒了,尤其是需要执行一段时间的TASK,在不启动ENVI的情况下,也没有进度显示,整个程序都是卡死状态,对用户来说不友好,这也是我在程序中加入手动进度条的原因。
3、调用ENVITASK-VectorMaskRaster 就可以正确裁剪,说明ENVITASK肯定有关于复杂多边形的处理,只不过该如何自己用代码写,却不得而知了。