IDL矢量裁剪影像(解决复杂多边形、空心、岛等裁剪问题)

    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肯定有关于复杂多边形的处理,只不过该如何自己用代码写,却不得而知了。

    
                                      

猜你喜欢

转载自blog.csdn.net/u013471015/article/details/82150172