├── README.md ├── .gitignore ├── DataDensityMap.m ├── pointAreaSubsets.m ├── polyCropGCPs.m ├── interpolate2grid.m ├── readisreg.m ├── loadGCPFile_xyz.m ├── loadGCPFile_is.m ├── DataDensityMask.m ├── addQC2Meta.m ├── batch_mask_rema2a.m ├── batchMergeTileBuffer.m ├── tileMeta.m ├── writeGeotiff.m ├── orderPairs.m ├── entropyMask.m ├── coregister2tile.m ├── edgeMask.m ├── RTFDFilter.m ├── edgeWarp.m ├── readGeotiff.m ├── stripmeta.m ├── readStripInTile.m ├── mask8m.m ├── batchAlignTile.m ├── tileRegMeta.m ├── QCStrips.m ├── edgeFeather.m ├── batch_mask.m ├── coregisterdems.m ├── remaMask2a.m ├── mosaic.m ├── batch_scenes2strips.m ├── mergeTileBuffer.m ├── batch_applyRegistration.m ├── registerDEM2LIDAR.m ├── boundaryAdjust.m ├── enviwrite.m ├── applyRegistration.m ├── registerTile.m ├── compileStripMeta.m ├── registerStrips.m ├── alignTile.m ├── DecimatePoly.m ├── addStrip2Mosaic.m ├── scenes2strips.m ├── QCStripsByTile.m └── strips2tile.m /README.md: -------------------------------------------------------------------------------- 1 | # setsm_postprocessing 2 | functions for filtering, stripping and mosaicing setsm-produced DEMs 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py 2 | *.pyc 3 | *.asv 4 | *.mat 5 | *.sh 6 | includeList* 7 | scenes2strips_single.m 8 | mask_strip.m 9 | selectTileByName.m 10 | compileDatabase.m 11 | batch_tileMeta.m 12 | batch_tileRegMeta.m 13 | batch_batchAlignTile.m 14 | batch_batchMergeTileBuffer.m 15 | mosaicStrips.m 16 | writeTileToTif.m 17 | compileDatabaseRema.m 18 | selectTileByName_Antarctic.m 19 | convertQcDataToTxt.m 20 | loadQcData.m 21 | *.csv 22 | -------------------------------------------------------------------------------- /DataDensityMap.m: -------------------------------------------------------------------------------- 1 | function P = DataDensityMap(varargin) 2 | % DataDensityMap density of data points within an array 3 | % 4 | % P = DataDensityMap(m) returns a size(m) array of the fraction of nodes 5 | % containing ones within an 11 by 11 kernel, where m is a mask array of 0 6 | % for no data and 1 for data. 7 | % 8 | % P = DataDensityMap(m,n) uses an n-sized kernal (default = 11) 9 | 10 | n = 11; 11 | 12 | m = varargin{1}; 13 | if nargin == 2; 14 | n = varargin{2}; 15 | end 16 | 17 | P =conv2(double(m), ones(n),'same'); 18 | P = P./n.^2; -------------------------------------------------------------------------------- /pointAreaSubsets.m: -------------------------------------------------------------------------------- 1 | function [xsub,ysub,zsub]=pointAreaSubsets(xp,yp,x,y,m,dd,N) 2 | 3 | % initialize coordinate subset cells 4 | xsub=cell(length(xp),1); 5 | ysub=cell(length(xp),1); 6 | zsub=cell(length(xp),1); 7 | 8 | % subsetting loop 9 | j=1; 10 | for j=1:length(xp) 11 | minCol=find(x >= xp(j) - dd,1,'first'); 12 | maxCol=find(x <= xp(j) + dd,1,'last'); 13 | 14 | minRow=find(y <= yp(j) + dd,1,'first'); 15 | maxRow=find(y >= yp(j) - dd,1,'last'); 16 | 17 | xsub{j}=x(minCol:maxCol); 18 | ysub{j}=y(minRow:maxRow); 19 | zsub{j}=m.z(minRow:maxRow,minCol:maxCol); 20 | zsub{j}(~N(minRow:maxRow,minCol:maxCol)) = NaN; 21 | end 22 | 23 | -------------------------------------------------------------------------------- /polyCropGCPs.m: -------------------------------------------------------------------------------- 1 | function [n]=polyCropGCPs(gcp,xv,yv,varargin) 2 | 3 | nonRectPoly=true; 4 | if ~isempty(varargin) 5 | switch lower(varargin{1}) 6 | case 'rectangle' 7 | nonRectPoly=false; 8 | end 9 | end 10 | 11 | 12 | % fast rectangle crop gcp data to rectangularized DEM boundarys 13 | n= gcp.x >= min(xv) & gcp.x <=max(xv) &... 14 | gcp.y >= min(yv) & gcp.y <=max(yv); 15 | 16 | 17 | n=find(n); % reduce index to fast crop selection 18 | 19 | if nonRectPoly 20 | % polygon crop THIS IS NOT NEEDED FOR RECTANGULAR TILES 21 | nn = inpolygon(gcp.x(n),gcp.y(n),xv,yv); % polygon crop 22 | 23 | n=n(nn); clear nn % reduce index to fast crop selection 24 | 25 | end 26 | -------------------------------------------------------------------------------- /interpolate2grid.m: -------------------------------------------------------------------------------- 1 | function [z,mt,or] = interpolate2grid(x,y,z,mt,or,xi,yi) 2 | % interpolate2grid interpolate the mask and ortho images to shifted grid 3 | % 4 | % [mi,oi] = interpolate2grid(m,o,x,y,dtrans) 5 | 6 | % interpolate elevation to grid 7 | z = interp2(x,y,z,xi(:)',yi(:),'*linear'); 8 | 9 | % interpolate the mask to the same grid 10 | mt = interp2(x,y,single(mt),xi(:)',yi(:),'*nearest'); 11 | 12 | mt(isnan(mt)) = 0; % convert back to uint8 13 | mt = logical(mt); 14 | 15 | 16 | % interpolate ortho to same grid 17 | or = single(or); 18 | or(or==0) = NaN; % set border to NaN so wont be interpolated 19 | or = interp2(x,y,or,xi(:)',yi(:),'*cubic'); 20 | 21 | or(isnan(or)) = 0; % convert back to int16 22 | or = int16(or); 23 | -------------------------------------------------------------------------------- /readisreg.m: -------------------------------------------------------------------------------- 1 | function [N,mn,md,trans,pctile]=readisreg(f) 2 | 3 | 4 | 5 | fid=fopen(f); 6 | c=textscan(fid,'%s','delimiter','\n'); 7 | fclose(fid); 8 | c=c{1}; 9 | 10 | astr='# GCPs='; 11 | r=~cellfun(@isempty,strfind(c,astr)); 12 | N = str2double(strrep(c{r},astr,'')); 13 | 14 | 15 | astr='Mean Vertical Residual (m)='; 16 | r=~cellfun(@isempty,strfind(c,astr)); 17 | mn = str2double(strrep(c{r},astr,'')); 18 | 19 | 20 | astr='Median Vertical Residual (m)='; 21 | r=~cellfun(@isempty,strfind(c,astr)); 22 | md = str2double(strrep(c{r},astr,'')); 23 | 24 | astr='Translation Vector (dz,dx,dy)(m)='; 25 | r=~cellfun(@isempty,strfind(c,astr)); 26 | trans = str2num(strrep(c{r},astr,'')); 27 | 28 | p=50:5:100; 29 | j=1; 30 | for j=1:length(p) 31 | astr=[num2str(p(j)),'th percentile=']; 32 | r=find(~cellfun(@isempty,strfind(c,astr))); 33 | pctile(1,j)= str2double(strrep(c{r},astr,'')); 34 | end 35 | -------------------------------------------------------------------------------- /loadGCPFile_xyz.m: -------------------------------------------------------------------------------- 1 | function [gcp]=loadGCPFile_xyz(gcpfile) 2 | % loadGCPFile_is load icesat csv file into structure 3 | 4 | [~,name,ext]=fileparts(gcpfile); 5 | fprintf('loading gcp file: %s\n',gcpfile) 6 | switch ext 7 | case '.csv' 8 | fid=fopen(gcpfile); 9 | %-2171380.33353,868114.550919,469.9240 10 | D=textscan(fid,'%f32%f32%f32','delimiter',','); 11 | fclose(fid); 12 | gcp.x=D{1}; 13 | gcp.y=D{2}; 14 | gcp.z=D{3}; 15 | clear D; 16 | 17 | % remove missing data lines 18 | n = isnan(gcp.x) | isnan(gcp.y) | isnan(gcp.z); 19 | gcp = structfun(@(x) ( x(~n) ), gcp, 'UniformOutput', false); 20 | clear n 21 | 22 | % find and remove duplicates 23 | [~,n] = unique([gcp.x,gcp.y],'rows'); 24 | gcp = structfun(@(x) ( x(n) ), gcp, 'UniformOutput', false); 25 | clear n 26 | 27 | gcp.dataset=name; 28 | 29 | otherwise 30 | error('gcp file must be .csv or .mat') 31 | 32 | end 33 | -------------------------------------------------------------------------------- /loadGCPFile_is.m: -------------------------------------------------------------------------------- 1 | function [gcp]=loadGCPFile_is(gcpfile) 2 | % loadGCPFile_is load icesat csv file into structure 3 | 4 | [~,name,ext]=fileparts(gcpfile); 5 | fprintf('loading gcp file: %s\n',gcpfile) 6 | switch ext 7 | case '.csv' 8 | fid=fopen(gcpfile); 9 | %-2171380.33353,868114.550919,469.9240,2003-10-14 18:58:57.794,0.0840 10 | D=textscan(fid,'%f32%f32%f32%u32-%u8-%u8 %u8:%u8:%f32%f32','delimiter',','); 11 | fclose(fid); 12 | gcp.x=D{1}; 13 | gcp.y=D{2}; 14 | gcp.z=D{3}; 15 | gcp.t=datenum([double(D{4}) double(D{5}) double(D{6}) double(D{7}) double(D{8}) double(D{9})]); 16 | gcp.i_gmc=D{10}; 17 | clear D; 18 | 19 | % remove missing data lines 20 | n = isnan(gcp.x) | isnan(gcp.y) | isnan(gcp.z); 21 | gcp = structfun(@(x) ( x(~n) ), gcp, 'UniformOutput', false); 22 | clear n 23 | 24 | % find and remove duplicates 25 | [~,n] = unique([gcp.x,gcp.y],'rows'); 26 | gcp = structfun(@(x) ( x(n) ), gcp, 'UniformOutput', false); 27 | clear n 28 | 29 | gcp.dataset=name; 30 | 31 | otherwise 32 | error('gcp file must be .csv or .mat') 33 | 34 | end 35 | -------------------------------------------------------------------------------- /DataDensityMask.m: -------------------------------------------------------------------------------- 1 | function M = DataDensityMask(varargin) 2 | % edgeMask returns mask for bad edges using the matchag field 3 | % 4 | % m1 = edgeMask(m0) where m0 is the matchtag array returns a binary mask of 5 | % size(m0) designed to filter data bad edges using match point density and 6 | % defualt parameters 7 | % 8 | % m1 = edgeMask(m0,Pmin,Amax) where Pmin is the minimum data density 9 | % threshold for masking and Amax maximum data gap area to leave filled 10 | 11 | m = varargin{1}; 12 | 13 | % Defaults 14 | n =21; % data density kernel 15 | Pmin=.3; % data density threshold for masking 16 | Amin=1000; % minimum data cluster area 17 | Amax=10000; % maximum data gap area to leave filled 18 | 19 | % parse inputs 20 | for i = 2:2:length(varargin) 21 | switch lower(varargin{i}) 22 | case 'n' 23 | n=varargin{i+1}; 24 | case 'pmin' 25 | Pmin=varargin{i+1}; 26 | case 'amin' 27 | Amin=varargin{i+1}; 28 | case 'amax' 29 | Amax=varargin{i+1}; 30 | end 31 | end 32 | 33 | P = DataDensityMap(m,n); % data density map is the fraction of pixels in 34 | % the kernel containing data points. 35 | 36 | % apply match density threshold 37 | M=P>=Pmin; 38 | 39 | if ~any(M(:)); return; end 40 | 41 | % remove small data clusters 42 | M = bwareaopen(M, Amin); % get rid of isolated little clusters of data 43 | 44 | % remove small data gaps 45 | M = ~bwareaopen(~M, Amax); 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /addQC2Meta.m: -------------------------------------------------------------------------------- 1 | function m = addQC2Meta(m,changePath) 2 | 3 | m.baseName = strrep(m.f,'_8m_meta.txt',''); 4 | 5 | uniqueRegionDir=unique(m.region); 6 | i=1; 7 | qc.fileNames=[]; 8 | qc.flag=[]; 9 | qc.x =[]; 10 | qc.y=[]; 11 | for i=1:length(uniqueRegionDir) 12 | 13 | if exist([uniqueRegionDir{i},'/qc.mat'],'file') 14 | 15 | qci=load([uniqueRegionDir{i},'/qc.mat']); 16 | 17 | n = ~cellfun(@iscell, qci.x) | ~cellfun(@iscell, qci.y); 18 | if any(n) 19 | warning('non cell mask entries detected, correcting') 20 | n=find(n); 21 | j=1; 22 | for j=1:length(n) 23 | qci.x(n(j)) = {{qci.x{n(j)}'}}; 24 | qci.y(n(j)) = {{qci.y{n(j)}'}}; 25 | end 26 | 27 | save([uniqueRegionDir{i},'/qc.mat'],'-struct','qci') 28 | 29 | end 30 | 31 | qc.fileNames=[qc.fileNames(:);qci.fileNames(:)]; 32 | qc.flag=[qc.flag(:);qci.flag(:)]; 33 | qc.x=[qc.x(:);qci.x(:)]; 34 | qc.y=[qc.y(:);qci.y(:)]; 35 | 36 | clear qci; 37 | end 38 | 39 | end 40 | 41 | if ~isempty(changePath) 42 | qc.fileNames= strrep(qc.fileNames,'/data4',changePath); 43 | end 44 | 45 | if ~isempty(qc.fileNames) 46 | 47 | [~,IA,IB]=intersect( strrep(m.f,'_meta.txt',''), strrep(qc.fileNames,'_dem_browse.tif','')); 48 | 49 | if isempty(IA) 50 | [~,IA,IB]=intersect( strrep(m.f,'_meta.txt',''), strrep(qc.fileNames,'_dem.tif','')); 51 | end 52 | 53 | m.qc = zeros(size(m.f),'uint8'); 54 | m.maskPolyx = cell(size(m.f)); 55 | m.maskPolyy = cell(size(m.f)); 56 | 57 | m.qc(IA) = qc.flag(IB); 58 | m.maskPolyx(IA) = qc.x(IB); 59 | m.maskPolyy(IA) = qc.y(IB); 60 | 61 | end -------------------------------------------------------------------------------- /batch_mask_rema2a.m: -------------------------------------------------------------------------------- 1 | % batch_mask_rema2a: batch script for priducing edgemask and datamask files 2 | % from DEM scene pairs. 3 | 4 | % Ian Howat, ihowa@gmail.com, Ohio State University 5 | 6 | % DEM directory 7 | demDir='/data3/REMA/region_04_ronne_shelf/tif_results/8m'; 8 | 9 | % start on file number 10 | strt=1; 11 | 12 | % and process every 13 | inc=1; 14 | 15 | 16 | fprintf('processing %s\n',demDir) 17 | fprintf('processing every %d files starting with %d\n',inc,strt); 18 | 19 | % make filename list 20 | demFiles = dir([demDir,'/*dem.tif']); 21 | demFiles = {demFiles.name}; 22 | demFiles = cellfun( @(x) [demDir,'/',x], demFiles, 'uniformoutput', false); 23 | 24 | % processing list 25 | I = strt:inc:length(demFiles); 26 | 27 | i=1; 28 | for i=1:1:length(I) 29 | 30 | demFile = demFiles{I(i)}; 31 | OutMaskName = strrep(demFile,'dem.tif','mask2a.tif'); 32 | 33 | fprintf('processing %d of %d: %s \n',i,length(I),demFile) 34 | 35 | if exist(OutMaskName,'file'); fprintf('mask exists, skipping\n'); continue; end 36 | 37 | m = remaMask2a(demFile); 38 | 39 | if isfield(m.Tinfo,'GeoDoubleParamsTag') 40 | 41 | if m.Tinfo.GeoDoubleParamsTag(1) > 0 42 | projstr='polar stereo north'; 43 | else 44 | projstr='polar stereo south'; 45 | end 46 | 47 | else 48 | 49 | projstr=m.Tinfo.GeoAsciiParamsTag; 50 | a=findstr( projstr, 'Zone'); 51 | b=findstr( projstr, ','); 52 | c = findstr( projstr,'Northern Hemisphere'); 53 | 54 | if ~isempty(c) 55 | projstr=[projstr(a+4:b-1),' North']; 56 | else 57 | projstr=[projstr(a+4:b-1),' South']; 58 | end 59 | end 60 | 61 | writeGeotiff(OutMaskName,m.x,m.y,m.z,1,0,projstr) 62 | 63 | end -------------------------------------------------------------------------------- /batchMergeTileBuffer.m: -------------------------------------------------------------------------------- 1 | function batchMergeTileBuffer(f) 2 | % batchMergeTileBuffer: mergeTileBuffer to all neighboring files 3 | % 4 | % batchMergeTileBuffer(f) where f is a cellstr of files to be merged. 5 | % Filenames must be in the format /rr_cc_*.mat where rr and cc are the 6 | % tile row and column numbers. 7 | 8 | 9 | [~,fname]=cellfun(@fileparts, f, 'UniformOutput',false); 10 | 11 | % tile row/col index from filename 12 | tc = char(fname(:)); 13 | tr= str2num(tc(:,1:2)); 14 | tc= str2num(tc(:,4:5)); 15 | 16 | % get up/down/right/left neighbors 17 | A = [[0 1];[1 0];[-1 0];[0 -1]]; 18 | 19 | % pair counter 20 | c=0; 21 | 22 | % pair index variables 23 | n0=0; 24 | n1=0; 25 | 26 | % loop through each file and see if neighbors exist, only keeping unique 27 | % pairs 28 | i=1; 29 | for i=1:length(f); 30 | 31 | % this row_col string 32 | si=[num2str(tr(i),'%02d'),'_',num2str(tc(i),'%02d')]; 33 | 34 | j=1; 35 | for j=1:4 36 | 37 | % that row_col string 38 | sj=[num2str(tr(i)+A(j,1),'%02d'),'_',num2str(tc(i)+A(j,2),'%02d')]; 39 | 40 | % make filename 41 | fj=strrep(f{i},si,sj); 42 | 43 | % test if it exists in list 44 | n = find(strcmp(fj,f)); 45 | 46 | if ~isempty(n) 47 | 48 | % exists, but is it aleady matched? 49 | [~,ia]=intersect([n i],[n0 n1],'rows'); 50 | 51 | if ~isempty(ia); continue; end 52 | 53 | % new pair, add to count and vectors 54 | 55 | c=c+1; 56 | n0(c,1)=i; 57 | n1(c,1)=n; 58 | 59 | end 60 | 61 | end 62 | end 63 | 64 | % run mergeTileBuffer on each pair in list 65 | for i=1:length(n0); mergeTileBuffer(f{n0(i)},f{n1(i)}); end 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /tileMeta.m: -------------------------------------------------------------------------------- 1 | function tileMeta(f) 2 | % tileMeta: write meta file for ArcticDEM mosaic tile 3 | % 4 | % tileMeta(f) where f is either the filename or matfile handle of the 5 | % tile matfile. 6 | 7 | %% first test if input arg is either a valid filename or mat file handle 8 | if isstr(f) % it's a string, might be a filename 9 | if exist(f,'file') % yup its a file 10 | m = matfile(f); % load it 11 | else 12 | error('File does not exist'); 13 | end 14 | elseif isvalid(f) % not a string, is it a valid file handle? 15 | m = f; % yes, so set m to f and get the filename for f 16 | f = m.Properties.Source; 17 | else error('input arg must be a filename or valid matfile handle') 18 | end 19 | 20 | % make outname 21 | outfile=strrep(f,'.mat','_meta.txt'); 22 | 23 | % test if exists, skip if does 24 | if exist(outfile,'file'); 25 | fprintf('%s exists, skipping\n',outfile); 26 | return; 27 | end 28 | 29 | %% Read and format strip info 30 | strip.name=m.f; 31 | strip.name=strip.name(:); 32 | 33 | % strip out path from file names 34 | for i=1:length(strip.name); 35 | [~,strip.name{i}]=fileparts(strip.name{i}); 36 | end 37 | % remove meta suffix from filenames 38 | strip.name=strrep(strip.name,'_meta',''); 39 | 40 | strip.dtrans=m.dtrans; 41 | strip.dtrans=strip.dtrans'; 42 | 43 | strip.rmse=m.rmse; 44 | strip.rmse=strip.rmse(:); 45 | 46 | % find nan dtrans, meaning strip wasn't used, and remove from lists 47 | n = any(~isnan(strip.dtrans'))'; 48 | 49 | strip = structfun(@(x) ( x(n,:) ), strip, 'UniformOutput', false); 50 | 51 | %% Get tile version 52 | tileVersion='1.0'; 53 | if ~isempty(whos(m,'version')); tileVersion=m.version; end; 54 | 55 | %% Write File 56 | fid=fopen(outfile,'w'); 57 | fprintf(fid,'ArcticDEM Mosaic Tile Metadata \n'); 58 | fprintf(fid,'Creation Date: %s\n',datestr(now)); 59 | fprintf(fid,'Version: %s\n',tileVersion); 60 | fprintf(fid,'\n'); 61 | fprintf(fid,'Mosaicking Alignment Statistics (meters) in rank order\n'); 62 | fprintf(fid,'strip, rmse, dz, dx, dy\n'); 63 | i=1; 64 | for i=1:length(strip.name) 65 | fprintf(fid,'%s %.2f %.4f %.4f %.4f\n',... 66 | strip.name{i},strip.rmse(i),strip.dtrans(i,:)); 67 | end 68 | fclose(fid); 69 | 70 | -------------------------------------------------------------------------------- /writeGeotiff.m: -------------------------------------------------------------------------------- 1 | function writeGeotiff(OutFileName,x,y,z,fmt,nodata,projstr) 2 | % writeGeotiff writes raster to geotiff using GDAL 3 | % 4 | % writeGeotiff(OutFileName,x,y,z,fmt,nodata,projstr) writes the map data in 5 | % x, y, z using the format, nodata and projection tags. 'projstr' can be 6 | % 'polar stereo north', 'polar stereo south' or, for utm, a string the zone 7 | % number and hemisphere (e.g. '12 North'). This function actually writes 8 | % the raster first to a flat binary in envi format using enviwrite and 9 | % then calls gdal to perform the conversion. If not in the system path, 10 | % gdalpath needs to be set below. 11 | % 12 | % See enviwrite help for format codes and other options. 13 | 14 | % Ian Howat, ihowat@gmail.com, Ohio State 15 | 16 | 17 | gdalpath =[]; %set to the path of the gdal binary if not in system path. 18 | if ismac 19 | gdalpath = '/Library/Frameworks/GDAL.framework/Versions/1.11/Programs/'; 20 | end 21 | 22 | %if ismac 23 | % tempfile = [tempname(tempdir),'.envi']; 24 | %else 25 | % tempfile = [tempname('/scratch/tmp'),'.envi']; 26 | %end 27 | 28 | outdir=OutFileName(1:find(OutFileName=='/',1,'last')); 29 | tempfile = [tempname(outdir),'.envi']; 30 | 31 | if strcmpi('polar stereo south',projstr) || strcmpi('polar stereo north',projstr) 32 | enviwrite(tempfile,x,y(:),z,'format',fmt,'proj',projstr); 33 | else 34 | if ~isempty(findstr(projstr,'North')) 35 | zone = str2num(strrep(projstr,'North','')); 36 | hemi = 'north'; 37 | else 38 | zone = str2num(strrep(projstr,'South','')); 39 | hemi = 'south'; 40 | end 41 | enviwrite(tempfile,x,y(:),z,'format',fmt,'proj','UTM','hemi',hemi,'zone',zone); 42 | end 43 | %enviwrite(tempfile,x,y(:),z,'format',fmt,'proj','polar stereo south'); 44 | %enviwrite(tempfile,x,y(:),z,'format',fmt,'proj','UTM','hemi','south','zone',23); 45 | 46 | 47 | % confirm file write 48 | if ~(exist(tempfile,'file') && exist([tempfile,'.hdr'],'file')) 49 | disp('Warning, envi file not written in getImage, stopping') 50 | keyboard 51 | end 52 | 53 | system([gdalpath ,'gdal_translate -co bigtiff=if_safer -co compress=lzw -co tiled=yes -a_nodata ',... 54 | num2str(nodata),' ',tempfile,' ', OutFileName]); 55 | 56 | delete([tempfile,'*']) 57 | -------------------------------------------------------------------------------- /orderPairs.m: -------------------------------------------------------------------------------- 1 | function f1 = orderPairs(demdir,f0) 2 | 3 | % original pair index 4 | n0 = 1:length(f0); 5 | 6 | % pair boundary vertices 7 | xv = zeros(5,length(n0)); 8 | yv = zeros(5,length(n0)); 9 | 10 | % imrect format vertices 11 | R0 = zeros(length(n0),4); 12 | 13 | % ordered pair index 14 | n1 = zeros(1,length(n0)); 15 | 16 | % loop through files and get border information 17 | for i=1:length(f0); 18 | try 19 | d = readGeotiff([demdir,'/',f0{i}],'mapinfoonly'); 20 | catch 21 | error(['error reading map info from ',demdir,'/',f0{i}]) 22 | end 23 | R0(i,:) = [min(d.x),min(d.y),max(d.x)-min(d.x),max(d.y)-min(d.y)]; 24 | xv(:,i) = [min(d.x); min(d.x); max(d.x); max(d.x); min(d.x)]; 25 | yv(:,i) = [min(d.y); max(d.y); max(d.y); min(d.y); min(d.y)]; 26 | clear d 27 | end 28 | 29 | % get aspect ratio of strip 30 | ar = (max(R0(:,1) + R0(:,3)) - min(R0(:,1)))./... 31 | (max(R0(:,2) + R0(:,4)) - min(R0(:,2))); 32 | 33 | % start with bottom left pair 34 | if ar >= 1; 35 | [~,n1(1)] = min(R0(:,1)); 36 | else 37 | [~,n1(1)] = min(R0(:,2)); 38 | end 39 | 40 | % put this starting pair into the ordered index 41 | R1 = R0(n1(1),:); 42 | 43 | n0(n1(1)) =[]; 44 | R0(n1(1),:) =[]; 45 | 46 | % loop through pairs and sequentially add the next pair with the most 47 | % overlap 48 | i=2; 49 | for i=2:length(f0); 50 | 51 | % loop through all scenes and calculate overlap 52 | A=zeros(length(n0),1); 53 | j=1; for j=1:length(A); A(j) = rectint(R1,R0(j,:)); end 54 | 55 | if ~any(A > 0); 56 | disp('Break in overlap detected, returning this segment only') 57 | break 58 | end 59 | 60 | % find pair with max overlap to pair i 61 | [~,mxAn] = max(A); 62 | n1(i) = n0(mxAn); 63 | 64 | % expand the combined footprint to add new pair 65 | R0xmax = R0(mxAn,1)+ R0(mxAn,3); 66 | R0ymax = R0(mxAn,2)+ R0(mxAn,4); 67 | 68 | R1xmax = R1(1)+ R1(3); 69 | R1ymax = R1(2)+ R1(4); 70 | 71 | R1(1) = min([R1(1),R0(mxAn,1)]); 72 | R1(2) = min([R1(2),R0(mxAn,2)]); 73 | R1(3) = max([R1xmax,R0xmax])-R1(1); 74 | R1(4) = max([R1ymax,R0ymax])-R1(2); 75 | 76 | % remove this pair from the list 77 | n0(mxAn) =[]; 78 | R0(mxAn,:) =[]; 79 | end 80 | 81 | % remove files with no overlap 82 | n1(n1==0) = []; 83 | f1 = f0(n1); 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /entropyMask.m: -------------------------------------------------------------------------------- 1 | function M = entropyMask(orthoFile) 2 | % entropyMask classify areas of low entropy in an image such as water 3 | % 4 | % M = entropyMask(orthoFile) returns the low entropy classification mask 5 | % from the geotif image in orthoFile. Also checks whether wvc was applied 6 | % from the metafile. 7 | % 8 | % Ian Howat,ihowat@gmail.com, Ohio State 9 | % 13-Apr-2017 10:41:41 10 | 11 | % parameters 12 | pres = 8; % resample to this resolution for processing for speed and smooth 13 | Jthresh = 0.2; % minimum entropy threshold. 0.2 seems to be good for water 14 | minPix = 1000; % clusters of mask and void pixels in the resampled 15 | % image less than this will be removed 16 | 17 | % Get meta file info 18 | metaFile = strrep(orthoFile,'ortho.tif','meta.txt'); 19 | 20 | % There is some sort of scaling difference between the wv_correct and non 21 | % wv_correct - applied othos that screws up the conversion to uint8 in 22 | % entropyfilt (which uses im2uint8) unless I convert to uint8 first. So 23 | % need to check if wvc applied. 24 | wvcFlag=false; 25 | 26 | % check if meta file exists 27 | if ~exist(metaFile,'file') 28 | warning('no meta file found, assuming no wv_correct applied\n'); 29 | else 30 | % exists, so read meta file 31 | c=textread(metaFile,'%s','delimiter','\n'); 32 | 33 | %find SETSM version 34 | str='Image 1 wv_correct='; 35 | r=find(~cellfun(@isempty,strfind(c,str))); 36 | value=deblank(strrep(c{r(1)},str,'')); 37 | wvcFlag = logical(str2num(value)); 38 | 39 | if wvcFlag 40 | fprintf('wv_correct applied\n') 41 | else 42 | fprintf('wv_correct not applied\n'); 43 | end 44 | end 45 | 46 | % read ortho 47 | or=readGeotiff(orthoFile); 48 | res = or.x(2) - or.x(1); 49 | or = or.z; 50 | sz = size(or); 51 | 52 | bg = or == 0; % image background mask 53 | 54 | % resize ortho to pres 55 | if res ~= pres 56 | or = imresize(or,res./pres); 57 | end 58 | 59 | % subtraction image 60 | or_subtraction = movmax(or,5) - movmin(or,5); 61 | 62 | if ~wvcFlag; or_subtraction = uint8(or_subtraction); end 63 | 64 | % entropy image 65 | J = entropyfilt(or_subtraction,ones(5)); 66 | 67 | M = J < Jthresh; 68 | 69 | M = bwareaopen(M,minPix); 70 | M = ~bwareaopen(~M,minPix); 71 | 72 | % resize ortho to 8m 73 | if res ~= pres 74 | M = imresize(M,sz,'nearest'); 75 | end 76 | 77 | M(bg) = false; 78 | 79 | -------------------------------------------------------------------------------- /coregister2tile.m: -------------------------------------------------------------------------------- 1 | function [dtrans,rmse] = coregister2tile(metaFile,m,N,varargin) 2 | %coregrister2tile merge strip into mosaic file 3 | % 4 | % Subfunctions: readStripInTile, coregistedems, 5 | 6 | % 7 | % Ian Howat, Ohio State University 8 | % version 9 | 10 | % Set Defaults 11 | 12 | mask=cell(1,2); 13 | minOverlapPixels=100; 14 | 15 | dtrans=[NaN;NaN;NaN]; 16 | rmse=NaN; 17 | 18 | % parse input args 19 | for i=1:2:length(varargin) 20 | 21 | switch lower(varargin{i}) 22 | 23 | 24 | 25 | case 'mask' 26 | 27 | mask=varargin{i+1}; 28 | 29 | if ~iscell(mask) || size(mask,2) ~= 2 30 | error('input variable "mask" must be a cell array with two columns (x,y)') 31 | end 32 | 33 | case 'minoverlappixels' 34 | 35 | minOverlapPixels=varargin{i+1}; 36 | 37 | otherwise 38 | 39 | error('Unknown input arguments') 40 | end 41 | end 42 | 43 | 44 | % read this strip data 45 | [x,y,z,mt,~,c,r] = readStripInTile(metaFile,m.x,m.y,'mask',mask); 46 | 47 | % check for competely masked file 48 | if isempty(x) 49 | fprintf('no data\n',sum(c)*sum(r)); 50 | return; 51 | end 52 | 53 | % convert mosaic subset area to index 54 | c=find(c); 55 | r=find(r); 56 | 57 | if length(c)*length(r) < minOverlapPixels 58 | fprintf('%d pixel overlap is too small, skipping\n',sum(c)*sum(r)); 59 | return; 60 | end 61 | 62 | %% Check for new coverage 63 | 64 | %subset count grid 65 | Nsub=N(r,c); 66 | 67 | 68 | A= Nsub ~= 0 & ~isnan(z); % overlapping coverage mask 69 | 70 | 71 | %% Registration 72 | if sum(A(:)) < minOverlapPixels 73 | fprintf('%d pixel overlap is too small, skipping\n',sum(A(:))); 74 | return; 75 | end 76 | 77 | % crop to new coverage 78 | co= find(sum(A) ~= 0,1,'first'):find(sum(A) ~= 0,1,'last'); 79 | ro= find(sum(A,2) ~= 0,1,'first'):find(sum(A,2) ~= 0,1,'last'); 80 | 81 | % co-register strip to mosaic subset 82 | try 83 | [~,dtrans,rmse] = coregisterdems(... 84 | m.x(1,c(co)),m.y(r(ro),1),m.z(r(ro),c(co)),... 85 | x(co),y(ro),z(ro,co),m.mt(r(ro),c(co)),mt(ro,co)); 86 | catch 87 | 88 | fprintf('coregistration failure, skipping\n'); 89 | return; 90 | end 91 | 92 | % coregisterdems returns dtrans values as positive offsets, whereas 93 | % the gcp registration are negative, so need to reverse the sign: 94 | dtrans=-dtrans; 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /edgeMask.m: -------------------------------------------------------------------------------- 1 | function M = edgeMask(varargin) 2 | % edgeMask returns mask for bad edges using the matchag field 3 | % 4 | % m1 = edgeMask(m0) where m0 is the matchtag array returns a binary mask of 5 | % size(m0) designed to filter data bad edges using match point density 6 | m = varargin{1}; 7 | 8 | %defaults 9 | %2m 10 | n =21; % data density kernel 11 | Pmin=.8; % data density threshold for masking 12 | Amin=1000; % minimum data cluster area 13 | cf = 0.5; %boundary curvature factor (0= point boundary, 1 =conv hull) 14 | crop=n; 15 | 16 | %for 2m SETSM 2.xxxx 17 | % n=101; 18 | % Pmin=.99; 19 | 20 | 21 | 22 | % %8m 23 | % n =5; % data density kernel 24 | % Pmin=.8; % data density threshold for masking 25 | % Amin=250; % minimum data cluster area 26 | % cf = 0.5; %boundary curvature factor (0= point boundary, 1 =conv hull) 27 | % crop = n; 28 | 29 | % parse inputs 30 | for i = 2:2:length(varargin) 31 | switch lower(varargin{i}) 32 | case 'n' 33 | n=varargin{i+1}; 34 | case 'pmin' 35 | Pmin=varargin{i+1}; 36 | case 'amin' 37 | Amin=varargin{i+1}; 38 | case 'crop' 39 | crop=varargin{i+1}; 40 | case 'cf' 41 | cf=varargin{i+1}; 42 | case 'm0' 43 | M0=varargin{i+1}; 44 | end 45 | end 46 | 47 | 48 | P = DataDensityMap(m,n); % data density map is the fraction of pixels in 49 | % the kernel containing data points. 50 | 51 | if exist('M0','var') 52 | M=P>=Pmin | M0; % data density threshold for masking 53 | else 54 | M=P>=Pmin; % data density threshold for masking 55 | end 56 | 57 | 58 | if ~any(M(:)); return; end 59 | 60 | M = imfill(M,'holes'); % fill interior holes since we're just looking for 61 | % edges here. 62 | M = bwareaopen(M, Amin); % get rid of isolated little clusters of data 63 | 64 | if ~any(M(:)); return; end 65 | 66 | B = bwboundaries(M, 8, 'noholes'); % find data coverage boundaries 67 | 68 | B = cell2mat(B); % merge all the data clusters 69 | 70 | %k = convhull(B(:,2),B(:,1)); % find outer data boundary - the convex hull 71 | % gives the straight line trace of the outer 72 | % edge. 73 | 74 | k = boundary(B(:,2),B(:,1),cf); % find outer data boundary - this function 75 | % is like convhull but allows for some 76 | % inward "bending" to conform to the data. 77 | 78 | M = poly2mask(B(k,2),B(k,1), size(m,1),size(m,2)); % build edge mask 79 | 80 | M = imerode(M,ones(crop)); % apply crop 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /RTFDFilter.m: -------------------------------------------------------------------------------- 1 | function M = RTFDFilter(z,zr) 2 | % Residual Topography Fractional Difference Filter 3 | % 4 | % M = RTFDFilter(z,zr) Uses a relative of deviaton in the residual 5 | % topography (RT,the difference between the DEM and a smoothed DEM) between 6 | % the DEM z and a reference DEM zr to filter noise and create binary mask 7 | % M. DEM arrays z and zr are assumed coregistered and must have equal rows 8 | % and columms. z can be a 3-D array, in which case each z(:,:,i) is 9 | % fitlered independently and M is the same size as z. 10 | % 11 | % Requires inpaint_nans 12 | 13 | 14 | % kernel size (NxN) 15 | N = 5; 16 | % connected groups of pixels less than this value will be removed 17 | minPixClusterSize=1e4; 18 | 19 | %% Residual topography from reference dem 20 | 21 | % kernel average convolution of reference 22 | B = conv2(zr,ones(N),'same')./N^2; 23 | 24 | % apply standard deviation filter to RT 25 | B = stdfilt(zr-B,ones(N)); 26 | 27 | % standard deviation filter returns zeros where there are Nans, so set the 28 | % reference std dev filter results to nan to avoid artifacts in boundary 29 | % interpolation 30 | B(B==0) = NaN; 31 | 32 | % set very small reference RT values to 0.1 to prevent small denominator in 33 | % difference ratio 34 | B(B < 0.01) = 0.01; 35 | 36 | %% Make filter mask for each z(:,:,i) 37 | 38 | % intitiate output mask stack 39 | M = true(size(z)); 40 | 41 | % loop through stack and make masks 42 | i=1; 43 | for i=1:size(z,3) 44 | 45 | % kernel average convolution of strip 46 | A = conv2(z(:,:,i),ones(N),'same')./N^2; 47 | 48 | % standard deviation filter of residual topography 49 | A = stdfilt(z(:,:,i)-A,ones(N)); 50 | 51 | % difference of residual topography 52 | dAB = A-B; 53 | 54 | % select bad data as fractional difference in residual topography and 55 | % residual topography above a threshold 56 | bad = dAB./B >= 10 & dAB > 0.5; 57 | 58 | % steps below address boundary of std filter by setting out-of-bounds 59 | % to zero and the interpolating across bounary pixels, setting values 60 | % gt 0 to bad 61 | 62 | % covert to doubles for inserting nans and inpaint_nans only takes 63 | % doubles 64 | bad = double(bad); 65 | 66 | % set filtering boundary to NaN 67 | bad(A ==0)=NaN; 68 | 69 | % set strip boundary to zero 70 | bad(isnan(z(:,:,i)))=0; 71 | 72 | % interpolate between M=1 (bad) and boundary 73 | bad = inpaint_nans(bad,2); 74 | 75 | % set logical mask as M gt zero (accounting for interp) and strip 76 | % boundary 77 | M(:,:,i) = (bad < 0.01 | isnan(zr)) & ~isnan(z(:,:,i)); 78 | 79 | % remove small clusters of data 80 | M = bwareaopen(M,minPixClusterSize); 81 | end 82 | -------------------------------------------------------------------------------- /edgeWarp.m: -------------------------------------------------------------------------------- 1 | function [z0,exitFlag] = edgeWarp(z0,z1,varargin) 2 | % EDGEWARP Merge two DEMS, forcing vertical edge alignment 3 | % 4 | %W = edgeWarp(z0,z1,buff) where z0 and z1 are equally-sized arrays 5 | %to be merged with z0 taking presidence. The arrays are merged by applying 6 | %a linearly-increasing offset from 0, buff pixels away from the edge of 7 | %ovlap, to the full offset at the edge of z1. The larger buff, the wider 8 | %the region of warping and the smoother the transition will be for larger 9 | %offsets. Treats NaNs as nodata. 10 | 11 | exitFlag=false; % exitFlag will set true if 100% overlap exists between z0 and z1 12 | 13 | % set default buffer size or set to varargin 14 | buff=51; 15 | if nargin==3 16 | buff=varargin{1}; 17 | end 18 | 19 | % make BW array of region of overlap 20 | A= ~isnan(z0) & ~isnan(z1); 21 | 22 | % if no overlap, insert z1 data and return 23 | if ~any(A(:)) 24 | z0(~isnan(z1))=z1(~isnan(z1)); 25 | return 26 | end 27 | 28 | % define BW array of warp region 29 | warpRegion=imdilate(A,ones(buff)) & ~A & ~isnan(z1); 30 | 31 | % if 100% overlap, warn and return z0 32 | if ~any(warpRegion(:)) 33 | warning(... 34 | 'no edge to warp,z1 lies completely inside of z0, retuning z0 and setting exitFlag to 1'); 35 | exitFlag=true; 36 | return 37 | end 38 | 39 | % define BW of boundary pixels around warp region for interpolation 40 | B = imdilate(warpRegion,ones(11)); 41 | 42 | %set warp region pixels on image boundary to zero to constrain 43 | %interpolation 44 | B(1,:)=0; B(end,:) = 0; B(:,1)=0; B(end,:) = 0; 45 | 46 | B = B & ~warpRegion; 47 | 48 | % set pixel indices 49 | nWarp=find(warpRegion); % warp-region pixels 50 | ndz=find(A & B); % difference region boundary pixels 51 | nz1=find(~isnan(z1) & ~A & B); % z1 (zero) difference boundary pixels 52 | 53 | % if nz1 is empty, not enough non-overlapping coverage to constrain 54 | % interpolation 55 | if isempty(nz1) 56 | warning(... 57 | 'not enough non-overlap data to constrain warp'); 58 | exitFlag=true; 59 | return 60 | end 61 | 62 | 63 | 64 | % difference overlap pixels 65 | dz = z0(ndz)-z1(ndz); 66 | 67 | % exclude dz pixels > std(dz) 68 | n = abs(dz) <= std(dz); 69 | 70 | dz = dz(n); 71 | ndz=ndz(n); 72 | 73 | % row/column subscripts of boundaries 74 | [R,C]=ind2sub(size(z0),[ndz;nz1]); 75 | 76 | % build interpolant using boundary pixels 77 | warning off 78 | F = scatteredInterpolant(C,R,[double(dz);zeros(size(nz1))]); 79 | warning on 80 | 81 | % row/column subscripts of warp region 82 | [R,C]=ind2sub(size(z0),nWarp); 83 | 84 | % interpolate warp region, add difference to z1 and insert into z0; 85 | z0(nWarp)=z1(nWarp)+F(C,R); 86 | 87 | % insert remaining z1 into empty z0 88 | z0(isnan(z0))=z1(isnan(z0)); 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /readGeotiff.m: -------------------------------------------------------------------------------- 1 | function I= readGeotiff(varargin) 2 | % readGeotiff: read geotiff using imread and assign map info from infinfo. 3 | % 4 | % I = readGeotiff('filename'); reads entire .tif into map structure I 5 | % I = GEOTIFF_READ(...,'pixel_subset', [r0 r1 c0 c1]) reads the subset of 6 | % the image defined by row and column range. 7 | % I = GEOTIFF_READ(...,'map_subset', [x0 x1 y0 y1]); reads the subset of 8 | % the image defined by the map coordinate range. 9 | % output: 10 | % I.z, image data 11 | % I.x, x coordinate in map 12 | % I.y, y coordinate in map 13 | % I.info, misc. info 14 | % imshow(I.z, 'xdata', I.x, 'ydata', I.y); 15 | % shows image with map coordinate 16 | % Version by Yushin Ahn, ahn.74@osu.edu 17 | % Glacier Dynamics Laboratory, 18 | % Byrd Polar Resear Center, Ohio State University 19 | % Referenced enviread.m (Ian Howat) 20 | 21 | % get name 22 | name = varargin{1}; 23 | 24 | Tinfo = imfinfo(name); 25 | info.cols = Tinfo.Width; 26 | info.rows = Tinfo.Height; 27 | info.imsize = Tinfo.Offset; 28 | info.bands = Tinfo.SamplesPerPixel; 29 | 30 | info.map_info.dx = Tinfo.ModelPixelScaleTag(1); 31 | info.map_info.dy = Tinfo.ModelPixelScaleTag(2); 32 | info.map_info.mapx = Tinfo.ModelTiepointTag(4); 33 | info.map_info.mapy = Tinfo.ModelTiepointTag(5); 34 | 35 | subrows = [1 info.rows]; 36 | subcols = [1 info.cols]; 37 | 38 | minx = info.map_info.mapx; 39 | maxy = info.map_info.mapy; 40 | 41 | x = minx + ((0:info.cols-1).*info.map_info.dx); 42 | y = maxy - ((0:info.rows -1).*info.map_info.dy); 43 | 44 | mapInfoOnlyFlag=0; 45 | 46 | % get subset bounds if specified 47 | if nargin > 1; 48 | 49 | if strcmp(varargin{2},'pixel_subset'); 50 | subrows = varargin{3}(3:4); 51 | subcols = varargin{3}(1:2); 52 | 53 | elseif strcmp(varargin{2},'map_subset'); 54 | 55 | map_subset = varargin{3}; 56 | subcols = (map_subset(1:2)-info.map_info.mapx)./... 57 | info.map_info.dx+1; 58 | subrows = (info.map_info.mapy - map_subset([4,3]))./... 59 | info.map_info.dy+1; 60 | subcols= round(subcols); 61 | subrows = round(subrows); 62 | 63 | subcols(subcols < 1) = 1; 64 | subrows(subrows < 1) = 1; 65 | subcols(subcols > info.cols) = info.cols; 66 | subrows(subrows > info.rows) = info.rows; 67 | 68 | elseif strcmpi(varargin{2},'mapinfoonly'); 69 | mapInfoOnlyFlag=1; 70 | end 71 | 72 | end 73 | 74 | % define map vectors 75 | I.x = x(subcols(1):subcols(2)); 76 | I.y = y(subrows(1):subrows(2)); 77 | I.z = []; 78 | 79 | if ~mapInfoOnlyFlag 80 | % read image 81 | I.z = imread(name,'PixelRegion',{subrows,subcols}); 82 | end 83 | 84 | 85 | I.info = info; 86 | I.Tinfo = Tinfo; 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /stripmeta.m: -------------------------------------------------------------------------------- 1 | function stripmeta(f) 2 | % writes meta data file for strip mosaics of SETSM-generated scenes 3 | % 4 | % stripmeta(f) where f is the file path/*_dem.tif generated a *_meta.txt 5 | % file The file structure must be 6 | % the following: 7 | % 1. strip file located in /path/strips/*dem.tif' 8 | % 2. mosaicking alignment statistics file is /path/region/tif_results/strips/*trans.mat' 9 | % 3. Individual scene file meta data in ../*_meta.txt 10 | % 11 | % Uses gdal to get projection string 12 | 13 | % test strip 14 | %f='/data2/ArcticDEM/Greenland/region_03_greenland_southwest/tif_results/strips/WV01_20120619_102001001B432E00_102001001A8B4300_seg1_8m_dem.tif'; 15 | 16 | gdalpath=[]; 17 | if ismac 18 | gdalpath='/Library/Frameworks/GDAL.framework/Versions/1.11/Programs/'; 19 | end 20 | 21 | 22 | [~,projstr]=system([gdalpath,'gdalsrsinfo -o proj4 ',f]); 23 | 24 | projstr=strrep(projstr,'+x_0=1','+x_0=0'); 25 | projstr=strrep(projstr,'+y_0=1','+y_0=0'); 26 | 27 | outfile=strrep(f,'dem.tif','meta.txt'); 28 | transfile=strrep(f,'dem.tif','trans.mat'); 29 | 30 | %outfile='test_meta.txt'; 31 | 32 | a = readGeotiff(f); 33 | m = a.z ~= -9999; 34 | 35 | B = bwboundaries(m, 8, 'noholes'); % find data coverage boundaries 36 | 37 | B = cell2mat(B); % merge all the data clusters 38 | 39 | if ~isempty(B) 40 | 41 | k = convhull(B(:,2),B(:,1)); % find outer data boundary - the convex hull 42 | % gives the straight line trace of the outer 43 | % edge. 44 | 45 | B=DecimatePoly(B(k,:),[1 1]); 46 | 47 | x=a.x(B(:,2)); 48 | y=a.y(B(:,1)); 49 | else 50 | x=NaN; 51 | y=NaN; 52 | end 53 | 54 | fid=fopen(outfile,'w'); 55 | 56 | fprintf(fid,'Strip Metadata \n'); 57 | fprintf(fid,'Creation Date: %s\n',datestr(now)); 58 | stripdate=dir(transfile); stripdate=stripdate.date; 59 | fprintf(fid,'Strip creation date: %s\n',stripdate); 60 | fprintf(fid,'Strip projection (proj4): %s\n',projstr); 61 | fprintf(fid,'Strip Footprint Vertices \n'); 62 | fprintf(fid,'X: '); fprintf(fid,'%d ',x); fprintf(fid,'\n'); 63 | fprintf(fid,'Y: '); fprintf(fid,'%d ',y); fprintf(fid,'\n'); 64 | fprintf(fid,'\n'); 65 | 66 | load(transfile); 67 | fprintf(fid,'Mosaicking Alignment Statistics (meters) \n'); 68 | fprintf(fid,'scene, rmse, dz, dx, dy\n'); 69 | i=1; 70 | N=length(scene); 71 | for i=1:N 72 | fprintf(fid,'%s %.2f %.4f %.4f %.4f\n',scene{i},rmse(i),trans(:,i)'); 73 | end 74 | 75 | fprintf(fid,'\n'); 76 | fprintf(fid,'Scene Metadata \n'); 77 | fprintf(fid,'\n'); 78 | fclose(fid); 79 | 80 | i=1; 81 | N=length(scene); 82 | for i=1:N 83 | 84 | sceneMetaFile=[strrep(fileparts(f),'strips','tif_results'),'/',strrep(scene{i},'dem.tif','meta.txt')]; 85 | 86 | system(['echo scene ',num2str(i),' name=',scene{i},' >> ',outfile]); 87 | if exist(sceneMetaFile,'file') 88 | system(['cat ',sceneMetaFile,' >> ',outfile]); 89 | else 90 | system(['echo ',sceneMetaFile,' not found >> ',outfile]); 91 | end 92 | system(['echo " " >> ',outfile]); 93 | end 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /readStripInTile.m: -------------------------------------------------------------------------------- 1 | function [xi,yi,z,mt,or,c,r] = readStripInTile(metaFile,x,y,varargin) 2 | 3 | % parse input args 4 | for i=1:2:length(varargin) 5 | 6 | switch lower(varargin{i}) 7 | case 'mask' 8 | 9 | mask=varargin{i+1}; 10 | 11 | if ~iscell(mask) || size(mask,2) ~= 2 12 | error('input variable "mask" must be a cell array with two columns (x,y)') 13 | end 14 | 15 | otherwise 16 | 17 | error('Unknown input arguments') 18 | end 19 | end 20 | 21 | 22 | % readStrip read the strip and crop to a defined grid 23 | buff=0; 24 | 25 | % data file names 26 | demFile= strrep(metaFile,'meta.txt','dem.tif'); 27 | matchFile= strrep(metaFile,'meta.txt','matchtag.tif'); 28 | orthoFile= strrep(metaFile,'meta.txt','ortho.tif'); 29 | 30 | % read dem first 31 | z=readGeotiff(demFile,'map_subset',[min(x),max(x),min(y),max(y)]); 32 | 33 | xi=z.x; 34 | yi=z.y(:); 35 | z=z.z; 36 | 37 | % reset noData to NaNs 38 | z(z < -100 | z == 0 | z == -NaN ) = NaN; 39 | 40 | % check for blank DEM 41 | M = ~isnan(z); 42 | 43 | 44 | %% Apply mask if exists 45 | if any(M(:)) && exist('mask','var') 46 | 47 | fprintf('applying mask\n') 48 | for i=1:size(mask,1) 49 | if ~isempty(mask{i,1}) && ~isempty(mask{i,2}) 50 | M(roipoly(xi,yi,M,mask{i,1},mask{i,2}))=0; 51 | end 52 | end 53 | z(~M) = nan; 54 | end 55 | 56 | if ~any(M(:)) 57 | xi=[]; 58 | yi=[]; 59 | z=[]; 60 | mt=[]; 61 | or=[]; 62 | c = []; 63 | r = []; 64 | 65 | 66 | fprintf('no non-NaN data\n'); 67 | return; 68 | end 69 | 70 | % get boundaries 71 | 72 | rowsum = sum(M) ~= 0; 73 | colsum = sum(M,2) ~= 0; 74 | 75 | % crop the nans 76 | r = []; 77 | c = []; 78 | 79 | c(1) = find(rowsum,1,'first')-buff; 80 | c(2) = find(rowsum,1,'last')+buff; 81 | 82 | r(1) = find(colsum,1,'first')-buff; 83 | r(2) = find(colsum,1,'last')+buff; 84 | 85 | if c(1) < 1; c(1)=1; end 86 | if r(1) < 1; r(1)=1; end 87 | 88 | sz=size(z); 89 | 90 | if c(2) > sz(2); c(2)=sz(2); end 91 | if r(2) > sz(1); r(2)=sz(1); end 92 | 93 | z = z(r(1):r(2),c(1):c(2)); 94 | xi = xi(c(1):c(2)); 95 | yi = yi(r(1):r(2)); 96 | 97 | mt=readGeotiff(matchFile,'map_subset',[min(x),max(x),min(y),max(y)]); 98 | mt = mt.z(r(1):r(2),c(1):c(2)); 99 | 100 | or=readGeotiff(orthoFile,'map_subset',[min(x),max(x),min(y),max(y)]); 101 | or = or.z(r(1):r(2),c(1):c(2)); 102 | 103 | % crop mosic to this DEM's boundary 104 | c = x >= min(xi) & x <= max(xi); 105 | r = y >= min(yi) & y <= max(yi); 106 | 107 | if diff(x(1:2)) ~= diff(xi(1:2)); 108 | 109 | z=interp2(xi,yi,z,x(c),y(r),'*linear'); 110 | 111 | mt=single(mt); mt(mt == 0) =NaN; 112 | mt=interp2(xi,yi,mt,x(c),y(r),'*nearest'); 113 | mt(isnan(mt)) = 0; 114 | mt=logical(mt); 115 | 116 | or=single(or); or(or == 0) =NaN; 117 | or=interp2(xi,yi,or,x(c),y(r),'*nearest'); 118 | or(isnan(or)) = 0; 119 | or=int16(or); 120 | 121 | xi = x(c); yi=y(r); 122 | 123 | end 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /mask8m.m: -------------------------------------------------------------------------------- 1 | function m = mask8m(varargin) 2 | % MASK masking algorithm for 8m resolution data 3 | % 4 | % m = mask(demFile) 5 | % returns the mask stucture (m.x,m.y,m.z) for the demFile using the 6 | % given image parameters. 7 | % 8 | % m = mask(...,maxDigitalNumber,previewPlot) maxDigitalNumber is optional 9 | % for rescaling the orthoimage to the original source image range. 10 | % If it's mot included or is empty, no rescaling will be applied. If 11 | % previewPlot == 'true', a *_maskPreview.tif image will be saved to the 12 | % same directory as the demFile that shows the DEM hillshade with and 13 | % without the mask applied. 14 | % 15 | % m = mask(demFile,meta) returns the mask stucture (m.x,m.y,m.z) for the 16 | % demFile and meta structure, where meta is the output of readSceneMeta. 17 | % 18 | % REQUIRED FUNCTIONS: readGeotiff, DataDensityMap, edgeSlopeMask, 19 | % DataDensityMask 20 | % 21 | % Ian Howat, ihowat@gmail.com 22 | % 25-Jul-2017 12:49:25 23 | 24 | %% Parse inputs 25 | previewFlag = false; 26 | demFile=varargin{1}; 27 | 28 | % intialize output 29 | m = []; 30 | mtFile= strrep(demFile,'dem.tif','matchtag.tif'); 31 | 32 | %% read data and make initial arrays 33 | 34 | % read DEM data 35 | z = readGeotiff(demFile); 36 | 37 | % read matchtag 38 | mt = readGeotiff(mtFile); 39 | 40 | % size consistency checks 41 | if any(size(z.z) ~= size(mt.z)) 42 | error('size of dem and matchtag rasters dont match') 43 | end 44 | 45 | % initialize output 46 | m.x = z.x; 47 | m.y = z.y; 48 | m.z = false(size(z.z)); 49 | m.info = z.info; 50 | m.Tinfo = z.Tinfo; 51 | 52 | % parse structures 53 | x = z.x; 54 | y = z.y; 55 | z = z.z; 56 | z(z == -9999) = NaN; 57 | mt = mt.z; 58 | 59 | % make data density map 60 | P = DataDensityMap(mt,21); 61 | 62 | % set P no data 63 | P(isnan(z)) = NaN; 64 | 65 | %% edge crop 66 | M = edgeSlopeMask(x,y,z); 67 | 68 | M(isnan(z)) = false; 69 | 70 | % data existence check 71 | if ~any(M(:)); return; end 72 | 73 | clear z 74 | 75 | %% Data Density Filter 76 | n= 21; 77 | Pmin=0.90; 78 | Amin=1000; 79 | Amax=1000; 80 | 81 | M =DataDensityMask(M,'n',n,'Pmin',Pmin,'Amin',Amin,'Amax',Amax); 82 | 83 | % data existence check 84 | if ~any(M(:)); return; end 85 | 86 | M = bwareaopen(M,500); 87 | 88 | % add to structure 89 | m.z = M; 90 | % 91 | 92 | if ~previewFlag; return; end 93 | 94 | %% Display output 95 | % 96 | % read DEM data 97 | z = readGeotiff(demFile); 98 | x = z.x; 99 | y = z.y; 100 | z = z.z; 101 | z(z == -9999) = NaN; 102 | 103 | z_masked = z; 104 | z_masked(~M) = NaN; 105 | 106 | rf = 0.25; 107 | z = imresize(z,rf); 108 | x = imresize(x,rf); % only needed for hillshade 109 | y = imresize(y,rf); % only needed for hillshade 110 | z_masked = imresize(z_masked,rf); 111 | 112 | hill = hillshade(z,x,y); 113 | hill_masked = hillshade(z_masked,x,y); 114 | 115 | h=figure(1); 116 | set(gcf,'color','w','visible','off') 117 | hpos = get(gcf,'position'); 118 | hpos(3) = 2000; 119 | hpos(4) = 1000; 120 | set(gcf,'position',hpos); 121 | subplot(1,2,1) 122 | imagesc(hill,'alphadata',~isnan(z)); colormap gray; axis equal 123 | subplot(1,2,2) 124 | imagesc(hill_masked,'alphadata',~isnan(z_masked)); colormap gray; axis equal 125 | print(h,'-dtiff','-r100',strrep(demFile,'_dem.tif','_maskPreview.tif')); 126 | close(h) 127 | -------------------------------------------------------------------------------- /batchAlignTile.m: -------------------------------------------------------------------------------- 1 | function batchAlignTile(f,freg) 2 | % batchAlignTile: recursively apply alignTile to all nregistered tiles 3 | % 4 | % batchAlignTile(f,freg) where f and freg are cellstr of unregistered and 5 | % registered files, respectively. Filenames must be in the format 6 | % /rr_cc_*.mat where rr and cc are the tile row and column numbers. 7 | 8 | % orient horizontally 9 | f=f(:)'; 10 | freg=freg(:)'; 11 | 12 | % initialize fail counter 13 | failCount=0; 14 | 15 | while ~isempty(f) 16 | 17 | fprintf('%d unregistered tiles, %d registered tiles, %d failed to register\n',length(f),length(freg),failCount); 18 | 19 | % get file names for row/col extract 20 | [~,fname]=cellfun(@fileparts, f, 'UniformOutput',false); 21 | [~,fregname]=cellfun(@fileparts, freg, 'UniformOutput',false); 22 | 23 | % make sure no reg'd unregs 24 | [~,ia]=intersect(fname,fregname); 25 | if ~isempty(ia) 26 | error('unregistered and registerd file lists not unique'); 27 | end 28 | 29 | % tile row/col index from filename 30 | tc = char(fname(:)); 31 | tr= str2num(tc(:,1:2)); 32 | tc= str2num(tc(:,4:5)); 33 | 34 | tcreg = char(fregname(:)); 35 | trreg= str2num(tcreg(:,1:2)); 36 | tcreg= str2num(tcreg(:,4:5)); 37 | 38 | % get up/down/right/left neighbors 39 | A = [[0 1];[1 0];[-1 0];[0 -1]]; 40 | 41 | % for each unregistered file, get neighboring registered files 42 | nreg=zeros(length(f),4); 43 | for i = 1:length(f) 44 | 45 | for j=1:4 46 | 47 | n = find(tr(i)+A(j,1) == trreg & tc(i)+A(j,2) == tcreg); 48 | 49 | if ~isempty(n) 50 | nreg(i,j) = n; 51 | end 52 | 53 | end 54 | end 55 | 56 | % count number of registered files 57 | Nreg=sum(nreg ~= 0,2); 58 | 59 | % make sure there are neighbors 60 | if ~any(Nreg) 61 | fprintf('%d unregistered tiles remain with no registered neigbors\n',length(f)); 62 | break 63 | end 64 | 65 | % rank tiles in descending order of number of neighboring registered tiles 66 | [~,n]=sort(Nreg,'descend'); 67 | 68 | % initialize fail counter 69 | failCount=0; 70 | 71 | % sequentially attempt each file by n, breaking if successful or all fail. 72 | while failCount < length(f) 73 | 74 | % extract this n 75 | n1=n(1 + failCount); 76 | nreg1=nreg(n1,:); 77 | nreg1(nreg1 == 0) = []; 78 | 79 | fprintf('registering %s to %s, %s, %s, %s',f{n1},freg{nreg1}); fprintf('\n'); 80 | 81 | % try to coregister 82 | [outFlag,outname] = alignTile({f{n1},freg{nreg1}}); 83 | 84 | % outFlag = true is success, so update lists, break and redo neignbor search 85 | if outFlag 86 | f(n1)=[]; % remove this file from the unreg list 87 | freg=[freg {outname}]; % add this file to the reg list 88 | break 89 | else % if coreg failed update count and try the next one in the n list 90 | failCount=failCount + 1; 91 | fprintf('%d failed tiles, %d unregistered tiles left\n',failCount, length(f)) 92 | end 93 | end 94 | 95 | % if all remaining unreg tiles failed, quit 96 | if failCount >= length(f) 97 | fprintf('%d tiles cannot be registered, quitting\n',length(f)); 98 | break 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /tileRegMeta.m: -------------------------------------------------------------------------------- 1 | function tileRegMeta(f,sensors) 2 | % tileRegMeta: write meta file for ArcticDEM mosaic tile registration stats 3 | % 4 | % tileRegMeta(f,sensors) where f is either the filename or matfile handle of the 5 | % tile matfile. Sensors is a cellstr of sensor tags. If sensors ={'neigbor align'} 6 | % a truncated format will be used. 7 | 8 | %% first test if input arg is either a valid filename or mat file handle 9 | if ischar(f) % it's a string, might be a filename 10 | if exist(f,'file') % yup its a file 11 | m = matfile(f); % load it 12 | else 13 | error('File does not exist'); 14 | end 15 | elseif isvalid(f) % not a string, is it a valid file handle? 16 | m = f; % yes, so set m to f and get the filename for f 17 | f = m.Properties.Source; 18 | else 19 | error('input arg must be a filename or valid matfile handle') 20 | end 21 | 22 | % if sensors is passed as a string, change it to cellstr for consistency 23 | if ~iscell(sensors); sensors={sensors}; end 24 | 25 | % make outname 26 | outfile=strrep(f,'_dem.mat','.txt'); 27 | 28 | % test if exists, skip if does 29 | if exist(outfile,'file') 30 | fprintf('%s exists, skipping\n',outfile); 31 | return; 32 | end 33 | 34 | %% Alternate Write for Neighbor Align Tile 35 | if length(sensors)==1 36 | if strcmpi(sensors{1},'Neighbor Align') == 1 37 | j=1; 38 | 39 | %% Create meta file and write universal info 40 | fid=fopen(outfile,'w'); 41 | fprintf(fid,'DEM Filename: %s\n',f); 42 | fprintf(fid,'Registration Dataset %d Name: %s\n',j,sensors{j}); 43 | 44 | for i=1:length(m.rmse) 45 | fprintf(fid,'Statistics for Coregistration Cluster %d:\n',i); 46 | fprintf(fid,'# GCPs=NaN\n'); 47 | fprintf(fid,'Mean Vertical Residual (m)=%.3f\n',m.rmse(:,i)); 48 | fprintf(fid,'Median Vertical Residual (m)=NaN\n'); 49 | fprintf(fid,'Translation Vector (dz,dx,dy)(m)= %.3f, %.3f, %.3f \n',m.dtrans(:,i)'); 50 | fprintf(fid,'Vertical Deviation Percentiles(m):\n'); 51 | fprintf(fid,'NaN\n'); 52 | fprintf(fid,'\n'); 53 | end 54 | fclose(fid); 55 | return 56 | end 57 | end 58 | 59 | %% double check to make sure this isnt an unregfile sent here by mistake 60 | if isempty(whos(m,'dzall')) 61 | error('oops, I think you sent an unregistered tile here by mistake'); 62 | end 63 | 64 | %% Create meta file and write universal info 65 | fid=fopen(outfile,'w'); 66 | fprintf(fid,'DEM Filename: %s\n',f); 67 | for j=1:length(sensors) 68 | fprintf(fid,'Registration Dataset %d Name: %s\n',j,sensors{j}); 69 | end 70 | 71 | %% Read and format strip info 72 | dzall=m.dzall; 73 | dtrans=m.dtrans; 74 | 75 | for i=1:length(dzall) 76 | fprintf(fid,'Statistics for Coregistration Cluster %d:\n',i); 77 | 78 | % find points used in registration 79 | nn = abs(dzall{i} - nanmedian(dzall{i})) < nanstd(dzall{i}); 80 | 81 | % calculate percentiles 82 | ptiles=50:5:100; 83 | dzptiles=prctile(abs(dzall{i}),ptiles); 84 | 85 | fprintf(fid,'# GCPs=%d\n',sum(nn)); 86 | fprintf(fid,'Mean Vertical Residual (m)=%.3f\n',nanmean(dzall{i}(nn))); 87 | fprintf(fid,'Median Vertical Residual (m)=%.3f\n',nanmedian(dzall{i}(nn))); 88 | fprintf(fid,'Translation Vector (dz,dx,dy)(m)= %.3f, %.3f, %.3f \n',dtrans(:,i)'); 89 | fprintf(fid,'Vertical Deviation Percentiles(m):\n'); 90 | for j=1:length(ptiles) 91 | fprintf(fid,'%dth percentile=',ptiles(j)); fprintf(fid,'%.3f ',dzptiles(j)); fprintf(fid,'\n'); 92 | end 93 | 94 | fprintf(fid,'\n'); 95 | 96 | end 97 | fclose(fid); 98 | 99 | -------------------------------------------------------------------------------- /QCStrips.m: -------------------------------------------------------------------------------- 1 | function QCStrips(stripDir,varargin) 2 | 3 | 4 | outFile=[stripDir,'/qc.mat']; 5 | 6 | system(['rm ',stripDir,'/._*']); 7 | 8 | fileNames=dir([stripDir,'/*dem_browse.tif']); 9 | 10 | fileNames=cellfun( @(x) [stripDir,'/',x], {fileNames.name},'uniformOutput',false); 11 | fileNames=fileNames(:); 12 | 13 | flag=zeros(size(fileNames)); 14 | x=cell(size(fileNames)); 15 | y=cell(size(fileNames)); 16 | 17 | if exist(outFile,'file') 18 | 19 | a=load(outFile); 20 | 21 | [~,IA,IB] = intersect(fileNames,a.fileNames); 22 | 23 | flag(IA) = a.flag(IB); 24 | x(IA) = a.x(IB); 25 | y(IA) = a.y(IB); 26 | 27 | clear a 28 | 29 | end 30 | 31 | 32 | if ~isempty(varargin) 33 | 34 | switch lower(varargin{1}) 35 | 36 | case 'redolast' 37 | 38 | n=find(flag ~=0,1,'last'); 39 | 40 | flag(n)=0; 41 | x{n}=[]; 42 | y{n}=[]; 43 | end 44 | end 45 | 46 | 47 | for i=1:length(fileNames) 48 | 49 | if flag(i) ~= 0; continue; end 50 | 51 | fprintf('%d of %d\n',sum(flag ~= 0) + 1,length(fileNames)) 52 | 53 | [x{i},y{i},flag(i)]=imedit(fileNames{i}); 54 | 55 | save(outFile,'fileNames','x','y','flag'); 56 | eval(['! chmod 664 ',outFile]); 57 | 58 | end 59 | 60 | 61 | 62 | function [x,y,flag]=imedit(fileName) 63 | 64 | I=readGeotiff(fileName); 65 | %Ior=readGeotiff(strrep(fileName,'_dem_browse.tif','_ortho_browse.tif')); 66 | 67 | % %% 68 | % landclass = subsetGimpIceMask(... 69 | % min(I.x)-30,max(I.x)+30,min(I.y)-30,max(I.y)+30); 70 | % 71 | % if ~isempty(landclass.ocean) 72 | % 73 | % if any(landclass.ocean(:)) 74 | % 75 | % 76 | % I.ocean = interp2(landclass.x,landclass.y(:),single(landclass.ocean),... 77 | % I.x,I.y(:),'*nearest'); 78 | % 79 | % I.z(logical(I.ocean))=0; 80 | % end 81 | % end 82 | %% 83 | imagesc(I.x,I.y,I.z,'alphadata',I.z ~= 0) 84 | set(gca,'color','r') 85 | axis xy equal tight; 86 | colormap gray; 87 | hold on; 88 | 89 | % set(gcf,'units','normalized'); 90 | % set(gcf,'position',[0.01,0.01,.35,.9]) 91 | 92 | 93 | flag=[]; 94 | x=cell(1); 95 | y=cell(1); 96 | 97 | i=1; 98 | while i 99 | fprintf('%s\n',fileName) 100 | flag=input('Enter quality flag: 1=good, 2=partial, 3=manual edit, 4=poor, 5=bad/error\n'); 101 | 102 | if ~isempty(flag) 103 | if isnumeric(flag) 104 | if flag == 1 || flag == 2 || flag == 3 || flag == 4 || flag == 5 105 | break; 106 | end 107 | end 108 | end 109 | 110 | if iscell(flag); flag=flag{1}; end 111 | 112 | fprintf('%d not recogized, try again\n',flag); 113 | 114 | end 115 | 116 | if flag == 1 || flag == 2; clf; return; end 117 | 118 | if flag == 4 || flag == 5 119 | x{1} = [min(I.x);min(I.x);max(I.x);max(I.x);min(I.x)]; 120 | y{1} = [min(I.y);max(I.y);max(I.y);min(I.y);min(I.y)]; 121 | clf 122 | return 123 | end 124 | 125 | fprintf('entering manual edit mode\n') 126 | 127 | while i 128 | 129 | [~,x{i},y{i}] = roipoly; 130 | 131 | plot(x{i},y{i},'r') 132 | 133 | 134 | while i 135 | s=input('continue editing this image? (y/n)\n','s'); 136 | 137 | if ~strcmpi(s,'y') && ~strcmpi(s,'n') 138 | fprintf('%s not recogized, try again\n',s); 139 | else 140 | break 141 | end 142 | end 143 | 144 | if strcmpi(s,'n'); break; end 145 | 146 | i=i+1; 147 | end 148 | 149 | clf 150 | 151 | 152 | -------------------------------------------------------------------------------- /edgeFeather.m: -------------------------------------------------------------------------------- 1 | function W = edgeFeather(M0,M1,varargin) 2 | % EDGEFEATHER get weights for merging two images with edge feathering 3 | % 4 | %W = edgeFeather(M0,M1,buff) where M0 and M1 are equally sized BW images 5 | %with ones for data and zeros for no data. buff is the cutline buffer for 6 | %merging - greater == means more overlap used for feather 7 | 8 | buff=0; 9 | f0=0; % overlap fraction where back z weight goes to zero 10 | f1=1; % overlap fraction where back z weight goes to one 11 | 12 | maxInterpPixels = 1e6; 13 | 14 | % parse input args 15 | for i=1:2:length(varargin) 16 | 17 | switch lower(varargin{i}) 18 | 19 | case 'buffer' 20 | 21 | buff=varargin{i+1}; 22 | 23 | case 'overlaprange' 24 | 25 | f0=varargin{i+1}(1); 26 | f1=varargin{i+1}(2); 27 | 28 | otherwise 29 | 30 | error('Unknown input arguments') 31 | end 32 | end 33 | 34 | % crop to just region of overlap 35 | A= single(M0 & M1); 36 | 37 | % if no overlap, send 1/0 mask back 38 | if ~any(A(:)) 39 | W=nan(size(M0)); 40 | W(M0) = 1; 41 | W(M1) = 0; 42 | return 43 | end 44 | 45 | % crop to area of overlap 46 | A(A==0)=NaN; 47 | [~,rb,cb] = cropnans(A,buff); 48 | 49 | %Make overlap mask removing isolated pixels 50 | A=single(bwareaopen(... 51 | ~M0(rb(1):rb(2),cb(1):cb(2)) & M1(rb(1):rb(2),cb(1):cb(2)),... 52 | 1000)); % nodata in back and data in front = 1 53 | 54 | A(bwareaopen(... 55 | M0(rb(1):rb(2),cb(1):cb(2)) & ~M1(rb(1):rb(2),cb(1):cb(2)),... 56 | 1000)) =2 ; % data in back and nodata in front = 2 57 | 58 | 59 | % do weight interpolation on low res grid for speed 60 | if numel(A) <= maxInterpPixels; maxInterpPixels=numel(A); end 61 | Ar=imresize(A,maxInterpPixels./numel(A),'nearest'); 62 | %Ar=imresize(A,.1,'nearest'); 63 | 64 | fprintf('%d pixels\n',numel(Ar)); 65 | 66 | % interpolation grid 67 | [C,R] = meshgrid(1:size(Ar,2),1:size(Ar,1)); 68 | 69 | % pixles on outside of boundary of overlap region 70 | B = bwboundaries(Ar~=0, 8, 'noholes'); 71 | B = cell2mat(B); 72 | 73 | if size(B,2) ~= 2 74 | warning('no overlap boundaries returned, no feathering applied'); 75 | W=nan(size(M0)); 76 | W(M1) = 0; 77 | W(M0) = 1; 78 | return 79 | end 80 | 81 | n=sub2ind(size(Ar),B(:,1),B(:,2)); 82 | 83 | warning off 84 | F = scatteredInterpolant(C(n),R(n),double(Ar(n))); 85 | warning on 86 | 87 | try 88 | Ar(Ar==0)=F(C(Ar==0),R(Ar==0)); 89 | catch 90 | warning('interpolation failed, no feathering applied'); 91 | W=nan(size(M0)); 92 | W(M1) = 0; 93 | W(M0) = 1; 94 | return 95 | end 96 | 97 | Ar=imresize(Ar,size(A),'bilinear'); 98 | Ar(A==1 & Ar ~=1)=1; 99 | Ar(A==2 & Ar ~=2)=2; 100 | A=Ar-1; 101 | A(A < 0) = 0; 102 | A(A > 1) = 1; 103 | 104 | W=single(M0); 105 | W(rb(1):rb(2),cb(1):cb(2)) = A; 106 | W(M0 & ~M1) = NaN; 107 | 108 | clear A 109 | 110 | % shift weights so that more of the reference layer is kept 111 | W=(1/(f1-f0)).*W-f0/(f1-f0); 112 | W(W > 1) = 1; 113 | W(W < 0) = 0; 114 | 115 | function [A,r,c] = cropnans(varargin) 116 | % cropnans crop array of bordering nans 117 | % 118 | % [A,r,c] = cropnans(A) 119 | 120 | A=varargin{1}; 121 | buff=0; 122 | if nargin == 2; buff=varargin{2}; end 123 | 124 | r = []; 125 | c = []; 126 | 127 | M = ~isnan(A); 128 | 129 | if ~any(M(:)); return; end 130 | 131 | rowsum = sum(M) ~= 0; 132 | colsum = sum(M,2) ~= 0; 133 | 134 | c(1) = find(rowsum,1,'first')-buff; 135 | c(2) = find(rowsum,1,'last')+buff; 136 | 137 | r(1) = find(colsum,1,'first')-buff; 138 | r(2) = find(colsum,1,'last')+buff; 139 | 140 | if c(1) < 1; c(1)=1; end 141 | if r(1) < 1; r(1)=1; end 142 | if c(2) > size(A,2); c(2)=size(A,2); end 143 | if r(2) > size(A,1); r(2)=size(A,1); end 144 | 145 | A = A(r(1):r(2),c(1):c(2)); 146 | -------------------------------------------------------------------------------- /batch_mask.m: -------------------------------------------------------------------------------- 1 | % batch_mask: batch script for priducing edgemask and datamask files 2 | % from DEM scene pairs. 3 | 4 | % Ian Howat, ihowa@gmail.com, Ohio State University 5 | 6 | %% ArcticDEM input directory settings 7 | updir='/data2/ArcticDEM'; %location of region directory 8 | regionnum='30'; % ArcticDEM region # 9 | res='2'; % DEM resolution 10 | 11 | %% load file names 12 | demdir=dir([updir,'/region_',regionnum,'*']); 13 | demdir=[updir,'/',demdir(1).name,'/tif_results/',res,'m']; 14 | 15 | fprintf('working: %s\n',demdir); 16 | 17 | f = dir([demdir,'/*_matchtag.tif']); 18 | 19 | fdate=[f.datenum]; 20 | f={f.name}; 21 | 22 | %% Update Mode - will only reprocess masks older than the matchtag file 23 | fedge = dir([demdir,'/*_edgemask.tif']); 24 | 25 | if ~isempty(fedge) 26 | fedgeDate=[fedge.datenum]; 27 | fedge={fedge.name}; 28 | [~,IA,IB] = intersect(f,strrep(fedge,'edgemask.tif','matchtag.tif')); 29 | n= fedgeDate(IB) - fdate(IA) >= -6.9444e-04; 30 | f(IA(n))=[]; 31 | 32 | clear fdate fedge fedgeDate 33 | end 34 | 35 | i=1; 36 | for i=1:1:length(f) 37 | 38 | matchFile = [demdir,'/',f{i}]; 39 | 40 | fprintf('processing %d of %d: %s \n',i,length(f),matchFile) 41 | OutEdgeMaskName= strrep(matchFile,'matchtag.tif','edgemask.tif'); 42 | OutDataMaskName= strrep(matchFile,'matchtag.tif','datamask.tif'); 43 | 44 | m=readGeotiff(matchFile); 45 | 46 | fprintf('making %s \n',OutEdgeMaskName) 47 | 48 | %find SETSM version 49 | metaFileName=strrep(matchFile,'matchtag.tif','meta.txt'); 50 | if ~exist(metaFileName,'file'); 51 | disp('no meta file, assumimg SETSM version > 2.0'); 52 | setsmVersion=3; 53 | else 54 | c=textread(metaFileName,'%s','delimiter','\n'); 55 | r=find(~cellfun(@isempty,strfind(c,'SETSM Version='))); 56 | if ~isempty(r) 57 | setsmVersion=deblank(strrep(c{r(1)},'SETSM Version=','')); 58 | else; setsmVersion='2.03082016'; end 59 | fprintf('Using settings for SETSM Version = %s\n',setsmVersion) 60 | setsmVersion=str2num(setsmVersion); 61 | end 62 | 63 | if setsmVersion < 2.01292016 64 | n= floor(21*2/m.info.map_info.dx); 65 | Pmin=0.8; 66 | Amin=2000/m.info.map_info.dx; 67 | crop=n; 68 | else 69 | n= floor(101*2/m.info.map_info.dx); 70 | Pmin=0.99; 71 | Amin=2000/m.info.map_info.dx; 72 | crop=n; 73 | end 74 | 75 | % shadeFileName=strrep(matchFile,'matchtag.tif','shade.tif'); 76 | % s=readGeotiff(shadeFileName); 77 | 78 | m1.z=edgeMask(m.z,'n',n,'Pmin',Pmin,'Amin',Amin,'crop',crop); 79 | 80 | if isfield(m.Tinfo,'GeoDoubleParamsTag') 81 | 82 | if m.Tinfo.GeoDoubleParamsTag(1) > 0 83 | projstr='polar stereo north'; 84 | else 85 | projstr='polar stereo south'; 86 | end 87 | 88 | else 89 | 90 | projstr=m.Tinfo.GeoAsciiParamsTag; 91 | a=findstr( projstr, 'Zone'); 92 | b=findstr( projstr, ','); 93 | c = findstr( projstr,'Northern Hemisphere'); 94 | 95 | if ~isempty(c) 96 | projstr=[projstr(a+4:b-1),' North']; 97 | else 98 | projstr=[projstr(a+4:b-1),' South']; 99 | end 100 | end 101 | 102 | writeGeotiff(OutEdgeMaskName,m.x,m.y,m1.z,1,0,projstr) 103 | 104 | m.z(~m1.z)=uint8(0); 105 | 106 | clear m1 107 | 108 | fprintf('making %s \n',OutDataMaskName) 109 | 110 | if setsmVersion <= 2.0 111 | 112 | n= floor(21*2/m.info.map_info.dx); 113 | Pmin=0.3; 114 | Amin=1000; 115 | Amax=10000; 116 | 117 | else 118 | n= floor(101*2/m.info.map_info.dx); 119 | Pmin=0.90; 120 | Amin=1000; 121 | Amax=1000; 122 | 123 | end 124 | 125 | 126 | m.z=DataDensityMask(m.z,'n',n,'Pmin',Pmin,'Amin',Amin,'Amax',Amax); 127 | 128 | writeGeotiff(OutDataMaskName,m.x,m.y,m.z,1,0,projstr) 129 | 130 | end 131 | 132 | -------------------------------------------------------------------------------- /coregisterdems.m: -------------------------------------------------------------------------------- 1 | function [z2out,p,d0] = coregisterdems(x1,y1,z1,x2,y2,z2,varargin) 2 | % COREGISTERDEM registers a floating to a reference DEM 3 | % 4 | % [z2r,trans,rms] = coregisterdems(x1,y1,z1,x2,y2,z2) registers the 5 | % floating DEM in 2D array z2 with coordinate vectors x2 and y2 to the 6 | % reference DEM in z1 using the iterative procedure in Nuth and Kaab, 7 | % 2010. z2r is the regiestered DEM, p is the z,x,y transformation 8 | % parameters and rms is the rms of the transformation in the vertical. 9 | 10 | % Maximum offset allowed 11 | maxp = 15; 12 | 13 | x1=x1(:)'; 14 | y1=y1(:); 15 | 16 | x2=x2(:)'; 17 | y2=y2(:); 18 | 19 | if length(x1) < 3 || length(y1) < 3 || length(x1) < 3 || length(y1) < 3 20 | error('minnimum array dimension is 3') 21 | end 22 | 23 | interpflag=true; 24 | if (length(x1) == length(x2)) && (length(y1) == length(y2)) 25 | if ~any(x2-x1) && ~any(y2-y1) 26 | interpflag=false; 27 | end 28 | end 29 | 30 | if length(varargin) == 2 31 | 32 | m1=varargin{1}; 33 | m2=varargin{2}; 34 | 35 | end 36 | 37 | rx = x1(2)-x1(1); % coordinate spacing 38 | p = [0;0;0]; % initial trans variable 39 | pn = p; % iteration variable 40 | d0 = inf; % initial rmse 41 | it = 1; % iteration step 42 | 43 | while it 44 | 45 | if interpflag 46 | % interpolate the floating data to the reference grid 47 | z2n = interp2(x2 - pn(2),y2 - pn(3),z2 - pn(1),... 48 | x1,y1,'*linear'); 49 | if exist('m2','var') 50 | m2n = interp2(x2 - pn(2),y2 - pn(3),single(m2),... 51 | x1,y1,'*nearest'); 52 | m2n(isnan(m2n)) = 0; % convert back to uint8 53 | m2n = logical(m2n); 54 | end 55 | else 56 | z2n=z2-pn(1); 57 | if exist('m2','var'); m2n=m2; end 58 | end 59 | 60 | interpflag=true; 61 | 62 | % slopes 63 | [sx,sy] = gradient(z2n,rx); 64 | sx = -sx; 65 | 66 | fprintf('Planimetric Correction Iteration %d ',it) 67 | 68 | % difference grids 69 | dz = z2n - z1; 70 | 71 | if exist('m1','var') && exist('m2','var'); dz(~m2n | ~m1) = NaN; end 72 | 73 | if ~any(~isnan(dz(:))); disp('No overlap'); z2out=z2; p=[NaN;NaN;NaN]; d0=NaN; return; end 74 | 75 | % filter NaNs and outliers 76 | n = ~isnan(sx) & ~isnan(sy) & ... 77 | abs(dz - nanmedian(dz(:))) <= nanstd(dz(:)); 78 | 79 | if ~any(n(:)) 80 | fprintf('regression failure, all overlap filtered\n') 81 | p = [NaN;NaN;NaN]; % initial trans variable 82 | d0 = NaN; 83 | z2out=z2; 84 | break 85 | end 86 | 87 | 88 | % get RMSE and break if below threshold 89 | d1 = sqrt(mean(dz(n).^2)); 90 | 91 | % keep median dz if first iteration 92 | if it == 1 93 | meddz=median(dz(n)); 94 | d00=sqrt(mean((dz(n)-meddz).^2)); 95 | end 96 | 97 | fprintf('rmse= %.3f ',d1) 98 | 99 | if d0 - d1 < .001 || isnan(d0) 100 | 101 | fprintf('stopping \n') 102 | % if fails after first registration attempt, set dx and dy to zero 103 | % and subtract the median offset 104 | if it == 2 105 | fprintf('regression failure, returning median vertical offset: %.3f\n',meddz) 106 | p(1)=meddz; d0=d00; 107 | z2out = z2-meddz; 108 | end 109 | break 110 | end 111 | 112 | %keep this adjustment 113 | p = pn; 114 | d0 = d1; 115 | z2out = z2n; 116 | 117 | % build design matrix 118 | X = [ones(size(dz(n))),sx(n),sy(n)]; 119 | 120 | % solve for new adustment 121 | pn = p + X\dz(n); 122 | 123 | % display offsets 124 | fprintf('offset(z,x,y): %.3f, %.3f, %.3f\n',pn') 125 | 126 | if any(abs(pn(2:end)) > maxp) 127 | fprintf('maximum horizontal offset reached, returning median vertical offset: %.3f\n',meddz) 128 | p=[meddz;0;0]; d0=d00; 129 | z2out = z2-meddz; 130 | break 131 | end 132 | 133 | % update iteration vars 134 | it = it+1; 135 | end 136 | 137 | 138 | -------------------------------------------------------------------------------- /remaMask2a.m: -------------------------------------------------------------------------------- 1 | function m = remaMask2a(demFile) 2 | % remaMask2a mask dem using point density and slopes deviations 3 | % 4 | % m = remaMask2a(demFile) masks the dem file by first applying an edgemask 5 | % and then using an iterative bad pixel search starting from points of low 6 | % point density and high standard deviation in slope. 7 | 8 | % plot hillshade browse image? 9 | plotBrowse = false; 10 | 11 | %% Filenames 12 | mtFile= strrep(demFile,'dem.tif','matchtag.tif'); 13 | 14 | %% read DEM data 15 | z = readGeotiff(demFile); 16 | 17 | %% initialize output 18 | m.x = z.x; 19 | m.y = z.y; 20 | m.z = false(size(z.z)); 21 | m.info = z.info; 22 | m.Tinfo = z.Tinfo; 23 | 24 | x = z.x; 25 | y = z.y; 26 | z = z.z; 27 | z(z == -9999) = NaN; 28 | 29 | %% edge crop 30 | 31 | % slope of z 32 | [dx,dy] = gradient(z,x,y); 33 | 34 | % aspect and grade (aspect is used in the cloud filter) 35 | [~,r] = cart2pol(dx,dy); 36 | 37 | % meang grade over n-pixel kernel 38 | n=5; 39 | Mr = conv2(r,ones(n)/(n.^2),'same'); 40 | 41 | % mask mean slopes greater than 1 42 | M = Mr < 1; 43 | 44 | % dilate high mean slope pixels by 51 pixels and set to false 45 | M(imdilate(Mr > 1,ones(13))) = false; 46 | 47 | clear Mr 48 | 49 | % fill interior holes since we're just looking for edges here. 50 | M = imfill(M,'holes'); 51 | 52 | % get rid of isolated little clusters of data 53 | M = bwareaopen(M, 1000); 54 | 55 | % no data check 56 | if ~any(M(:)); fprintf('boundary filter removed all data\n'); return; end; 57 | 58 | % find data coverage boundaries 59 | B = bwboundaries(M, 8, 'noholes'); 60 | 61 | % merge all the data clusters 62 | B = cell2mat(B); 63 | 64 | % find outer data boundary 65 | k = boundary(B(:,2),B(:,1),0.5); %allows for some inward "bending" to conform to the data. 66 | %k = convhull(B(:,2),B(:,1));% straight line trace of the outer edge. 67 | 68 | % build edge mask 69 | M = poly2mask(B(k,2),B(k,1), size(M,1),size(M,2)); 70 | 71 | clear B k 72 | 73 | % apply mask 74 | z(~M) = NaN; 75 | 76 | clear M 77 | 78 | dx(isnan(z))=NaN; 79 | dy(isnan(z))=NaN; 80 | 81 | 82 | %% Iterative expanding mt density/slope mask 83 | 84 | % standard deviation of slopes 85 | n=5; 86 | Mdx =conv2(dx,ones(n)/(n.^2),'same'); 87 | Sdx=sqrt( conv2(dx.^2,ones(n)/(n.^2),'same') - Mdx.^2 ); 88 | 89 | Mdy =conv2(dy,ones(n)/(n.^2),'same'); 90 | Sdy=sqrt( conv2(dy.^2,ones(n)/(n.^2),'same') - Mdy.^2 ); 91 | 92 | Sd = sqrt(Sdx.^2 + Sdy.^2); 93 | 94 | % read matchtag 95 | mt = readGeotiff(mtFile); 96 | mt = mt.z; 97 | 98 | % make data density map 99 | P = DataDensityMap(mt,n); 100 | 101 | % set P no data 102 | P(isnan(z)) = NaN; 103 | 104 | %locate probable cloud pixels 105 | M = Sd > 0.75 & P < 1; 106 | 107 | % remove small clusters 108 | M = bwareaopen(M,10); 109 | 110 | % initialize masked pixel counters 111 | N0 = sum(M(:)); 112 | N1 = inf; 113 | 114 | % background mask 115 | MM = isnan(z) | isnan(Sd); 116 | 117 | % expand mask to surrounding bad pixels, stop when mask stops growing 118 | while N0 ~= N1 119 | 120 | N0 = N1; % set new to old 121 | M = imdilate(M,ones(11)); % dialate the mask 122 | M(MM | Sd < 0.1) = false; % unmask low sd pixels 123 | N1 = sum(M(:)); % count the number of new masked pixels. 124 | 125 | end 126 | 127 | % remove small data gaps 128 | M = ~bwareaopen(~M,5000); 129 | 130 | % remove border effect 131 | M = M | imdilate(isnan(z),ones(5)); 132 | 133 | % remove small data gaps 134 | M = ~bwareaopen(~M,5000); 135 | 136 | % put mask into output stucture with good data == 1 137 | m.z = ~M; 138 | 139 | 140 | if ~plotBrowse; return; end 141 | 142 | 143 | %% Plot browse hillshade image 144 | 145 | z = readGeotiff(demFile); 146 | x = z.x; 147 | y = z.y; 148 | z = z.z; 149 | z(z == -9999) = NaN; 150 | hill = hillshade(z,x,y); 151 | 152 | 153 | h=figure(1); 154 | %set(gcf,'color','w','visible','off') 155 | set(gcf,'color','w','visible','on') 156 | hpos = get(gcf,'position'); 157 | hpos(3) = 2000; 158 | hpos(4) = 1000; 159 | set(gcf,'position',hpos); 160 | subplot(1,2,1) 161 | imagesc(hill,'alphadata',~isnan(z)); colormap gray; axis equal 162 | subplot(1,2,2) 163 | imagesc(hill,'alphadata',m.z); colormap gray; axis equal 164 | %print(h,'-dtiff','-r100',strrep(demFile,'_dem.tif','_mask3e_8m_preview.tif')); 165 | input('') 166 | close(h) 167 | 168 | 169 | -------------------------------------------------------------------------------- /mosaic.m: -------------------------------------------------------------------------------- 1 | function mosaic(db,tiles,res,outdir,varargin) 2 | % MOSAIC upper level function for mosaicking DEM strips to a set of tiles 3 | % 4 | % mosaic(db,tiles,res,outdir) 5 | % mosaic(db,tiles,res,outdir,gcp) 6 | 7 | %% parse and error check varargin 8 | if ~isempty(varargin) 9 | 10 | gcp=varargin{1}; 11 | 12 | if ~isfield(gcp,'x') || ~isfield(gcp,'y') || ~isfield(gcp,'z') 13 | error('gcp arg must be structure with fields x,y,z') 14 | end 15 | end 16 | 17 | tilef = cellstr([char(tiles.I),... 18 | repmat(['_',num2str(res),'m_dem.mat'],length(tiles.I),1)]); 19 | 20 | %% Loop through tiles and mosaic 21 | for n=1:length(tiles.I) 22 | 23 | outname=[outdir,'/',tiles.I{n},'_',num2str(res),'m_dem.mat']; 24 | 25 | if exist(outname,'file') 26 | fprintf('tile dem %s exists\n',tiles.I{n}) 27 | % fprintf('tile %s exists deleting\n',tiles.I{n}) 28 | % delete(outname); 29 | else 30 | fprintf('building tile %s: %d of %d\n',tiles.I{n},n,length(tiles.I)) 31 | strips2tile(db,tiles.x0(n),tiles.x1(n),tiles.y0(n),tiles.y1(n),res,... 32 | outname,'disableReg'); 33 | end 34 | 35 | if exist(outname,'file') 36 | if exist(strrep(outname,'dem.mat','reg_dem.mat'),'file') 37 | fprintf('tile reg dem %s exists\n',tiles.I{n}); 38 | else 39 | % Apply gcp registration if gcp's provided 40 | if exist('gcp','var') 41 | fprintf('appling ground control\n'); 42 | m = matfile(outname); 43 | registerTile(m,gcp); 44 | clear m 45 | end 46 | end 47 | end 48 | end 49 | 50 | 51 | %% Align unregistered tiles 52 | 53 | % find unregistered tiles by intersect/removing with registered list 54 | f=dir([outdir,'/*m_dem.mat']); 55 | f=cellfun(@(x) [outdir,'/',x], {f.name}, 'UniformOutput',false); 56 | 57 | freg=dir([outdir,'/*m_reg_dem.mat']); 58 | freg=cellfun(@(x) [outdir,'/',x], {freg.name}, 'UniformOutput',false); 59 | 60 | % remove unregistered files that already have registered files 61 | [~,IA]=intersect(f,strrep(freg,'_reg','')); 62 | f(IA)=[]; 63 | 64 | batchAlignTile(f,freg); 65 | 66 | 67 | %% Blend tile edges 68 | % get list of tiles from current directory 69 | tilef=dir([outdir,'/*m_reg_dem.mat']); 70 | tilef = cellfun(@(x) [outdir,'/',x], {tilef.name}, 'UniformOutput',false); 71 | 72 | %batchMergeTileBuffer(tilef); 73 | 74 | %% Crop Buffers and Write Tiles To Geotiffs 75 | tilef=dir([outdir,'/*m_dem.mat']); 76 | tilef = cellfun(@(x) [outdir,'/',x], {tilef.name}, 'UniformOutput',false); 77 | for i=1:length(tilef) 78 | 79 | fprintf('writing tif %d of %d\n',i,length(tilef)) 80 | 81 | if exist(strrep(tilef{i},'dem.mat','reg_dem.mat'),'file') 82 | fi=strrep(tilef{i},'dem.mat','reg_dem.mat'); 83 | else 84 | fi=tilef{i}; 85 | end 86 | 87 | fprintf('source: %s\n',fi); 88 | 89 | load(fi,'x','y'); 90 | % crop buffer tile 91 | x=x(101:end-100); 92 | y=y(101:end-100); 93 | 94 | OutDemName = strrep(fi,'.mat','.tif'); 95 | if ~exist(OutDemName,'file'); 96 | 97 | load(fi,'z'); 98 | z=z(101:end-100,101:end-100); 99 | z(isnan(z)) = -9999; 100 | 101 | writeGeotiff(OutDemName,x,y,z,4,-9999,'polar stereo north') 102 | clear z 103 | end 104 | 105 | OutMatchtagName = strrep(fi,'dem.mat','matchtag.tif'); 106 | if ~exist(OutMatchtagName,'file'); 107 | load(fi,'mt'); 108 | mt =mt(101:end-100,101:end-100); 109 | writeGeotiff(OutMatchtagName,x,y,mt,1,0,'polar stereo north') 110 | clear mt 111 | end 112 | 113 | OutOrthoName = strrep(fi,'dem.mat','ortho.tif'); 114 | if ~exist(OutOrthoName ,'file'); 115 | load(fi,'or'); 116 | or =or(101:end-100,101:end-100); 117 | writeGeotiff(OutOrthoName ,x,y,or,2,0,'polar stereo north') 118 | clear or 119 | end; 120 | 121 | OutDaynumName = strrep(fi,'dem.mat','daynum.tif'); 122 | if ~exist(OutDaynumName,'file'); 123 | load(fi,'dy'); 124 | dy =dy(101:end-100,101:end-100); 125 | writeGeotiff(OutDaynumName,x,y,dy,2,0,'polar stereo north') 126 | clear dy 127 | end 128 | 129 | clear x y 130 | end 131 | -------------------------------------------------------------------------------- /batch_scenes2strips.m: -------------------------------------------------------------------------------- 1 | % batch_scenes2strips: batch script for mosaicking scene into strip DEMs 2 | 3 | % Ian Howat, ihowa@gmail.com, Ohio State University 4 | 5 | %% ArcticDEM input directory settings 6 | updir='/data2/ArcticDEM'; %location of region directory 7 | regionnum='30'; % ArcticDEM region # 8 | res='2'; % DEM resolution 9 | 10 | %% load file names 11 | res = [res,'m']; 12 | demdir=dir([updir,'/region_',regionnum,'*']); 13 | demdir=[updir,'/',demdir(1).name,'/tif_results/',res]; 14 | 15 | outdir=strrep(demdir,'tif_results','strips'); 16 | 17 | fprintf('%s\n',demdir) 18 | 19 | if ~exist(outdir,'dir'); mkdir(outdir); end 20 | 21 | f = dir([demdir,'/*_dem.tif']); 22 | 23 | stripids=char(f.name); 24 | s=find(stripids(1,:)=='_'); 25 | stripids = stripids(:,s(2)+1:s(4)-1); 26 | stripids =cellstr(stripids); 27 | unique_stripids =unique(stripids); 28 | 29 | f={f.name}; 30 | 31 | strt=1; 32 | inc=1; 33 | for i=strt:inc:length(unique_stripids) 34 | 35 | %% find scenes with this strip pair id 36 | n = find(strcmp(unique_stripids{i},stripids)); 37 | 38 | fprintf('merging pair id: %s, %d of %d, %d scenes\n',unique_stripids{i},i,length(unique_stripids),length(n)) 39 | 40 | %existance check 41 | a=dir([outdir,'/',f{n(1)}(1:48),'seg*_',res,'_dem.tif']); 42 | 43 | if ~isempty(a) 44 | b=dir([demdir,'/',f{n(1)}(1:48),'*_datamask.tif']); 45 | 46 | a=min([a.datenum]); %date of strip creation 47 | b=max([b.datenum]); %date of data mask creation 48 | 49 | % if dem date is less than strip date continue 50 | if b < a; fprintf('younger strip exists, skipping\n'); continue; end 51 | 52 | fprintf('old strip exists, deleting and reprocessing\n'); 53 | eval(['!rm -f ',outdir,'/',f{n(1)}(1:48),'*']); 54 | 55 | end 56 | 57 | %% make sure all ortho's and matchtags exist, if missing, skip 58 | missingflag=0; 59 | j=1; 60 | for j=1:length(n) 61 | if ~exist(strrep([demdir,'/',f{n(j)}],'dem.tif','matchtag.tif'),'file') 62 | fprintf('matchtag file for %s missing, skipping this strip\n',f{n(j)}); 63 | missingflag=1; 64 | end 65 | if ~exist(strrep([demdir,'/',f{n(j)}],'dem.tif','ortho.tif'),'file') 66 | fprintf('ortho file for %s missing, skipping this strip\n',f{n(j)}); 67 | missingflag=1; 68 | end 69 | end 70 | if missingflag==1 ;continue; end 71 | %% 72 | seg=1; 73 | while ~isempty(n) 74 | 75 | fprintf('building segment %d\n',seg) 76 | 77 | % send n scenes to strip mosaicker 78 | [x,y,z,m,o,trans,rmse,scene]=scenes2strips(demdir,f(n)); 79 | 80 | % find which files were used 81 | [~,IA] = intersect(f(n),scene); 82 | 83 | % remove these from the list 84 | n(IA) = []; 85 | 86 | if isempty(z); continue; end; 87 | 88 | 89 | OutDemName=[outdir,'/',scene{1}(1:48),'seg',num2str(seg),'_',res]; 90 | 91 | save([OutDemName,'_trans.mat'],'scene','trans','rmse') 92 | 93 | d=readGeotiff([demdir,'/',scene{1}],'MapInfoOnly'); 94 | 95 | 96 | if isfield(d.Tinfo,'GeoDoubleParamsTag') 97 | 98 | if d.Tinfo.GeoDoubleParamsTag(1) > 0 99 | projstr='polar stereo north'; 100 | else 101 | projstr='polar stereo south'; 102 | end 103 | 104 | else 105 | 106 | projstr=d.Tinfo.GeoAsciiParamsTag; 107 | a=findstr( projstr, 'Zone'); 108 | b=findstr( projstr, ','); 109 | c = findstr( projstr,'Northern Hemisphere'); 110 | 111 | if ~isempty(c) 112 | projstr=[projstr(a+4:b-1),' North']; 113 | else 114 | projstr=[projstr(a+4:b-1),' South']; 115 | end 116 | end 117 | 118 | writeGeotiff([OutDemName,'_dem.tif'],x,y,z,4,-9999,projstr); 119 | 120 | OutMatchName=[OutDemName,'_matchtag.tif']; 121 | writeGeotiff(OutMatchName,x,y,m,1,0,projstr); 122 | 123 | OutOrthoName=[OutDemName,'_ortho.tif']; 124 | writeGeotiff(OutOrthoName,x,y,o,2,0,projstr); 125 | 126 | stripmeta([OutDemName,'_dem.tif']) 127 | 128 | seg = seg + 1; 129 | 130 | end 131 | 132 | end 133 | -------------------------------------------------------------------------------- /mergeTileBuffer.m: -------------------------------------------------------------------------------- 1 | function mergeTileBuffer(f0,f1) 2 | % mergeTileBuffer: merge the edge buffers two tiles with linear feathering 3 | % 4 | % mergeTileBuffer(f0,f1) where f0 and f1 are the file names or mat file 5 | % handles of neighboring tiles. 6 | 7 | %% first test if input args are either valid filenames or mat file handles 8 | fprintf('Merging tile %s with %s\n', f0, f1) 9 | 10 | if isstr(f0) % it's a string, might be a filename 11 | if exist(f0,'file') % yup its a file 12 | m0 = matfile(f0); % load it 13 | else 14 | error('File does not exist'); 15 | end 16 | elseif isvalid(f0) % not a string, is it a valid file handle? 17 | m0 = f0; % yes, so set m to f and get the filename for f 18 | f0 = m0.Properties.Source; 19 | else error('input arg must be a filename or valid matfile handle') 20 | end 21 | 22 | if isstr(f1) % it's a string, might be a filename 23 | if exist(f1,'file') % yup its a file 24 | m1 = matfile(f1); % load it 25 | else 26 | error('File does not exist'); 27 | end 28 | elseif isvalid(f1) % not a string, is it a valid file handle? 29 | m1 = f1; % yes, so set m to f and get the filename for f 30 | f1 = m1.Properties.Source; 31 | else error('input arg must be a filename or valid matfile handle') 32 | end 33 | 34 | % make sure writeable 35 | m0.Properties.Writable = true; 36 | m1.Properties.Writable = true; 37 | 38 | %% crop tiles to buffer 39 | c0 = m0.x >= min(m1.x) & m0.x <= max(m1.x); 40 | r0 = m0.y >= min(m1.y) & m0.y <= max(m1.y); 41 | c0 = [find(c0,1,'first'),find(c0,1,'last')]; 42 | r0 = [find(r0,1,'first'),find(r0,1,'last')]; 43 | 44 | if isempty(c0) || isempty(r0); error('no overlap betwene tiles'); end 45 | 46 | c1 = m1.x >= min(m0.x) & m1.x <= max(m0.x); 47 | r1 = m1.y >= min(m0.y) & m1.y <= max(m0.y); 48 | c1 = [find(c1,1,'first'),find(c1,1,'last')]; 49 | r1 = [find(r1,1,'first'),find(r1,1,'last')]; 50 | 51 | %% Make weight array based on boundary being merged 52 | info0=whos(m0,'z'); 53 | sz0=info0.size; 54 | 55 | if c0(1) > 1; % merging on f0's right boundary 56 | 57 | W0 = linspace(1,0,diff(c0)+1); 58 | W0 = repmat(W0,diff(r0)+1,1); 59 | 60 | m0.mergedRight=true; 61 | m1.mergedLeft=true; 62 | 63 | elseif c0(2) < sz0(2) % merging on f0's left boundary 64 | 65 | W0 = linspace(0,1,diff(c0)+1); 66 | W0 = repmat(W0,diff(r0)+1,1); 67 | 68 | m0.mergedLeft=true; 69 | m1.mergedRight=true; 70 | 71 | elseif r0(2) < sz0(2) % merging on f0's top boundary 72 | 73 | W0 = linspace(0,1,diff(r0)+1); 74 | W0 = repmat(W0(:),1,diff(c0)+1); 75 | 76 | m0.mergedTop=true; 77 | m1.mergedBottom=true; 78 | 79 | 80 | elseif r0(1) > 1 % merging on f0's bottom boundary 81 | 82 | W0 = linspace(1,0,diff(r0)+1); 83 | W0 = repmat(W0(:),1,diff(c0)+1); 84 | 85 | m0.mergedBottom=true; 86 | m1.mergedTop=true; 87 | 88 | else 89 | 90 | error('buffer region is same size as full grid') 91 | 92 | end 93 | 94 | %% merge z 95 | z0= m0.z(r0(1):r0(2),c0(1):c0(2)); 96 | z1= m1.z(r1(1):r1(2),c1(1):c1(2)); 97 | 98 | z=(z0.*W0)+(z1.*(1-W0)); 99 | 100 | n=isnan(z0) & ~isnan(z1); 101 | z(n)=z1(n); 102 | n=~isnan(z0) & isnan(z1); 103 | z(n)=z0(n); 104 | 105 | m0.z(r0(1):r0(2),c0(1):c0(2))=z; 106 | m1.z(r1(1):r1(2),c1(1):c1(2))=z; 107 | clear z0 z1 z; 108 | 109 | %% merge mt 110 | mt0= m0.mt(r0(1):r0(2),c0(1):c0(2)); 111 | mt1= m1.mt(r1(1):r1(2),c1(1):c1(2)); 112 | 113 | mt = mt0 | mt1; 114 | 115 | m0.mt(r0(1):r0(2),c0(1):c0(2))=mt; 116 | m1.mt(r1(1):r1(2),c1(1):c1(2))=mt; 117 | clear mt0 mt1 mt; 118 | 119 | %% merge or 120 | or0= m0.or(r0(1):r0(2),c0(1):c0(2)); 121 | or1= m1.or(r1(1):r1(2),c1(1):c1(2)); 122 | 123 | or0=single(or0); 124 | or1=single(or1); 125 | 126 | or=(or0.*W0)+(or1.*(1-W0)); 127 | 128 | n= or0 == 0 & or1 ~= 0; 129 | or(n)=or1(n); 130 | n= or0 ~= 0 & or1 == 0; 131 | or(n)=or0(n); 132 | 133 | or=int16(or); 134 | 135 | m0.or(r0(1):r0(2),c0(1):c0(2))=or; 136 | m1.or(r1(1):r1(2),c1(1):c1(2))=or; 137 | clear or0 or1 or; 138 | 139 | %% merge dy 140 | dy0= m0.dy(r0(1):r0(2),c0(1):c0(2)); 141 | dy1= m1.dy(r1(1):r1(2),c1(1):c1(2)); 142 | 143 | dy0=single(dy0); 144 | dy1=single(dy1); 145 | 146 | dy=(dy0.*W0)+(dy1.*(1-W0)); 147 | 148 | n= dy0 == 0 & dy1 ~= 0; 149 | dy(n)=dy1(n); 150 | n= dy0 ~= 0 & dy1 == 0; 151 | dy(n)=dy0(n); 152 | 153 | dy=int16(dy); 154 | 155 | m0.dy(r0(1):r0(2),c0(1):c0(2))=dy; 156 | m1.dy(r1(1):r1(2),c1(1):c1(2))=dy; 157 | clear dy0 dy1 dy; 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /batch_applyRegistration.m: -------------------------------------------------------------------------------- 1 | %batch_applyRegistration initializes the calls to applyRegistration, which 2 | %shifts and interpolates the tile mosaics (all rasters) within mat files. 3 | % Currently set to operate on tiles within a region directory. 4 | % Makes a new .mat file called *_reg.mat 5 | % 6 | % Compatible with v4 mosaics. 7 | % 8 | % Should be resolution agnostic. 9 | 10 | %% Paths/files 11 | % upper level directory where region directories sit 12 | %remaTileDir='~/Desktop'; 13 | %remaTileDir='/fs/project/howat.4/howat.4/automosaic13e'; 14 | remaTileDir='~/project/howat.4/rema_mosaic_v13e'; 15 | 16 | % region name (which is the name of the region directory) 17 | regionName='rema_21_mbl_north'; 18 | 19 | % File with list of registration parameters, indexed by tile name 20 | %registrationFile= /fs/byo/howat-data4/REMA/altimetryByTile/rema_{region number}_{region name}_is2reg.mat 21 | registrationFile= '~/data4/REMA/altimetryByTile/rema_21_mbl_north_is2reg.mat'; 22 | 23 | %% Read file list 24 | demMatFiles=dir([remaTileDir,'/',regionName,'/*_10m.mat']); 25 | demMatFiles=cellfun( @(x) [remaTileDir,'/',regionName,'/',x],... 26 | {demMatFiles.name}, 'uniformoutput',0); 27 | 28 | %% loop through tiles 29 | i=1; 30 | for i=1:length(demMatFiles) 31 | 32 | 33 | % File to register 34 | demMatFile=demMatFiles{i}; 35 | 36 | if ~exist(demMatFile,'file') 37 | warning('%s doesnt exist, skipping',demMatFile) 38 | continue 39 | end 40 | 41 | % get the region and tile names from the mat file name 42 | S=strsplit(demMatFile,'/'); 43 | %regionName=S{end-1}; already set this above 44 | tileName=S{end}(1:5); 45 | 46 | %find this tile 47 | regData=load(registrationFile); 48 | n = find(contains(regData.tileNames,tileName)); 49 | 50 | if isempty(n) 51 | warning('could not find registration data for %s, skipping',tileName) 52 | continue 53 | end 54 | 55 | % extract data for this tile from registration data 56 | regData = rmfield(regData,{'tileFiles','tileNames'}); 57 | regData.reg = structfun( @(x) x(n,:), regData.reg,'uniformoutput',0); 58 | regData.unreg = structfun( @(x) x(n,:), regData.unreg,'uniformoutput',0); 59 | 60 | if isnan(regData.reg.p(1)) 61 | warning('NaN offset for %s, skipping',tileName) 62 | continue 63 | end 64 | 65 | % load demMatFile into a matfile object 66 | m = matfile(demMatFile); 67 | 68 | % get grid size 69 | [nrows,ncols] = size(m,'z'); 70 | 71 | % make coregistration cluster mask - not currently used 72 | C = true(nrows,ncols); 73 | 74 | % make out mat name to write to 75 | outName=strrep(demMatFile,'.mat','_reg.mat'); 76 | 77 | % if only vertical registration only z is changed. 78 | if regData.reg.p(2) == 0 && regData.reg.p(3) == 0 79 | 80 | % copy the matfile to the outname 81 | eval(['cp ',demMatFile,' ',outName]); 82 | 83 | % subtract the vertical offset from the dem 84 | z = m.z - regData.reg.p(1); 85 | 86 | % append the z (will overwrite) and the regdata to the out matfile 87 | save(outName,'-append','z','regData') 88 | 89 | % else due interpolation on each var 90 | else 91 | % load x and y coordintes 92 | x = m.x; 93 | y = m.y; 94 | 95 | % save to matfile 96 | save(outName,'regData','x','y'); 97 | 98 | % register z and add to mat file 99 | z = applyRegistration(regData.reg.p,m,C,'gridvar','z','subsetSize',5000); 100 | save(outName,'-append','z'); 101 | clear z 102 | 103 | % register z_mad and add to mat file 104 | z_mad = applyRegistration(regData.reg.p,m,C,'gridvar','z_mad','subsetSize',5000); 105 | save(outName,'-append','z_mad'); 106 | clear z_mad 107 | 108 | % register N and add to mat file 109 | N = applyRegistration(regData.reg.p,m,C,'gridvar','N','subsetSize',5000); 110 | save(outName,'-append','N'); 111 | clear N 112 | 113 | % register Nmt and add to mat file 114 | Nmt = applyRegistration(regData.reg.p,m,C,'gridvar','Nmt','subsetSize',5000); 115 | save(outName,'-append','Nmt'); 116 | clear Nmt 117 | 118 | % register tmin and add to mat file 119 | tmin = applyRegistration(regData.reg.p,m,C,'gridvar','tmin','subsetSize',5000); 120 | save(outName,'-append','tmin'); 121 | clear tmin 122 | 123 | % register tmax and add to mat file 124 | tmax = applyRegistration(regData.reg.p,m,C,'gridvar','tmax','subsetSize',5000); 125 | save(outName,'-append','tmax'); 126 | clear tmax 127 | 128 | end 129 | end 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /registerDEM2LIDAR.m: -------------------------------------------------------------------------------- 1 | function [p,dz] = registerDEM2LIDAR(x,y,z,rx,ry,rz) 2 | % registerDEM2LIDAR Register raster DEM to LIDAR point cloud 3 | % 4 | % [dtrans,dz] = registerDEM2LIDAR(x,y,z,rx,ry,rz) where x,y,z are the DEM 5 | % coordinate vectors and array and rx,ry,rz are the control point vectors. 6 | % The output p is the dz,dx,dy offets and dz are the residuals, so that the 7 | % corrected dem is x+p(2),y+(3),z+p(1) with the 1-sigma of the fit as 8 | % nanstd(dz). 9 | 10 | 11 | 12 | if iscell(z) 13 | 14 | x = cellfun(@(x) double(x), x, 'UniformOutput',false); 15 | y = cellfun(@(x) double(x), y, 'UniformOutput',false); 16 | z = cellfun(@(x) double(x), z, 'UniformOutput',false); 17 | 18 | % slopes 19 | dx = diff(x{1}(1:2)); 20 | [sx,sy] = cellfun(@(x) gradient(x,dx), z, 'UniformOutput',false); 21 | sx = cellfun(@(x) -x, sx, 'UniformOutput',false); 22 | 23 | else 24 | 25 | x = double(x); 26 | y = double(y); 27 | z = double(z); 28 | 29 | 30 | % slopes 31 | [sx,sy] = gradient(z,x(2)-x(1)); 32 | sx = -sx; 33 | 34 | end 35 | 36 | 37 | rx = double(rx); 38 | ry = double(ry); 39 | rz = double(rz); 40 | 41 | % set new iteration values 42 | xn = x; 43 | yn = y; 44 | zn = z; 45 | 46 | % set iteration loop variables 47 | d0 = inf; 48 | it = 1; 49 | dz = []; 50 | dtrans = []; 51 | p = [0;0;0]; 52 | 53 | %% Planimetric Correction Iteration Loop 54 | while it 55 | 56 | % interpolate DEM to lidar x,y 57 | 58 | if iscell(z) 59 | 60 | zi = nan(size(rz)); sxi = zi; syi = zi; 61 | i=1; 62 | for i=1:length(z); 63 | zi(i) = interp2(xn{i},yn{i},zn{i},rx(i),ry(i)); 64 | sxi(i) = interp2(xn{i},yn{i},sx{i},rx(i),ry(i)); 65 | syi(i) = interp2(xn{i},yn{i},sy{i},rx(i),ry(i)); 66 | end 67 | else 68 | 69 | zi = interp2(xn,yn,zn,rx,ry); 70 | sxi = interp2(xn,yn,sx,rx,ry); 71 | syi = interp2(xn,yn,sy,rx,ry); 72 | end 73 | 74 | % difference DEM and LIDAR 75 | dzn = zi-rz; 76 | 77 | % filter NANs for speed 78 | n = ~isnan(sxi) & ~isnan(syi) & ~isnan(dzn); 79 | % filter outliers for solution 80 | n = n & (abs(dzn - median(dzn(n))) < std(dzn(n))); 81 | 82 | % if too few non-nan points found, return 83 | if sum(n) < 4 84 | fprintf('Too few (%d) non-nan DEM points, returning\n',sum(n)); 85 | return; 86 | end 87 | 88 | % calculate & display rmse 89 | d1 = sqrt(mean(dzn(n).^2)); 90 | fprintf('N=%d dz=%.2f dx=%.2f dy=%.2f rmse=%.3f\n ',... 91 | sum(n),sum([dtrans,p],2)',d1) 92 | 93 | % break if rmse increases, keeping last iteration values 94 | if d1 >= d0 95 | fprintf('returning values from iteration n-1\n') 96 | break 97 | end 98 | 99 | % if better, then set new values to old 100 | z = zn; 101 | x = xn; 102 | y = yn; 103 | dz = dzn; 104 | 105 | dtrans = [dtrans,p]; 106 | 107 | % break if rmse decrease is small, keeping these iteration values 108 | if (d0 - d1)./d0 <= 1e-3 109 | break 110 | end 111 | 112 | % dependent variable matrix 113 | X = [ones(size(dz(n))),sxi(n),syi(n)]; 114 | 115 | % solve for parameters 116 | %warning off 117 | p = X\dz(n); 118 | % warning on 119 | 120 | % apply offsets to generate new values to test 121 | if iscell(z) 122 | zn = cell(size(z)); xn = zn; yn = zn; 123 | i=1; 124 | for i=1:length(z); 125 | zn{i} = z{i} - p(1); 126 | xn{i} = x{i} - p(2); 127 | yn{i} = y{i} - p(3); 128 | end 129 | else 130 | zn = z - p(1); 131 | xn = x - p(2); 132 | yn = y - p(3); 133 | end 134 | % next interation 135 | it = it+1; 136 | d0 = d1; 137 | 138 | end 139 | 140 | p = -sum(dtrans,2); 141 | 142 | % 143 | % %% x,y,z - Dependent Bias Correction 144 | % %disp('Ramp and Elevation Bias Correction') 145 | % 146 | % % filter NaNs and outliers for solution 147 | % n = ~isnan(sxi) & ~isnan(syi) & ~isnan(dz); 148 | % n = n & (abs(dz - median(dz(n))) < 2.*std(dz(n))); 149 | % 150 | % % dependent variable matrix 151 | % X = [ones(size(dz(n))),rz(n),rx(n),ry(n)]; 152 | % 153 | % % solve for parameters and display offsets 154 | % %p = X\dz(n); 155 | % 156 | % [b,bint,r,rint,stats] = regress(dz(n),X); 157 | % 158 | % 159 | % 160 | % % grid dem coordinates and apply corrections 161 | % [xgrid,ygrid] = meshgrid(x,y); 162 | % z = z - (p(1) + p(2).*z + p(3).*xgrid + p(4).*ygrid); 163 | % 164 | % delev = p; 165 | % 166 | % % calculate final rmse 167 | % zi = interp2(x,y,z,rx,ry); 168 | % dz = zi-rz; 169 | % n = ~isnan(dz); 170 | % % filter outliers for solution 171 | % n = n & (abs(dz - median(dz(n))) < 2.*std(dz(n))); 172 | % zrms = rms(dz(n)); 173 | % zmed = median(dz(n)); 174 | % zmn = mean(dz(n)); 175 | % fprintf('rmse = %.2f, dz mean = %.2f, dz median = %.2f \n',... 176 | % zrms,zmed,zmn); 177 | -------------------------------------------------------------------------------- /boundaryAdjust.m: -------------------------------------------------------------------------------- 1 | function boundaryAdjust(fileNames,varargin) 2 | % boundaryAdjust adjust tile based on offsets with neighbors over buffers 3 | % 4 | % boundaryAdjust(fileNames) where fileNames is a cell of full filenames of 5 | % tile files. Files must be named row_col_*.mat, where the row and col are 6 | % the tile row and column numbers in the tile arrangment (so the tile above 7 | % would be (row+1)_col_*.mat . The tiles above, below, left and right are 8 | % loading and the offset over the buffer is caculated. The values are then 9 | % upscaled by the resizeFraction, which should be ~ the fractional width 10 | % of the buffer, and added along the edges of a correction field (dz0) 11 | % that is then filled through interpolation. dz0 is generated for all 12 | % tiles in the list and then, afterward, is applied to all tiles in the 13 | % list. A flag, adusted, is added with value false if dz0 has not been 14 | % applied and true if it has. 15 | % 16 | % boundaryAdjust(fileNames,'noApply')only calculates/saves the dz0 field 17 | % but does not apply it to z. 18 | 19 | resizeFraction=0.1; 20 | 21 | if ~iscell(fileNames) 22 | fileNames = {fileNames}; 23 | end 24 | 25 | %% dz0 calc and write loop - this could be run simultaneously/parallel 26 | i=1; 27 | for i =1:length(fileNames) 28 | fprintf('Calculating offset grid for %s .... ',fileNames{i}) 29 | 30 | m0=matfile(fileNames{i}); 31 | if any(strcmp(fields(m0),'dz0')) 32 | fprintf('dz0 already exists, skipping\n') 33 | continue 34 | end 35 | 36 | calcdz0(fileNames{i},resizeFraction) 37 | fprintf('done\n') 38 | end 39 | 40 | if ~isempty(varargin) 41 | if any(strcmpi(varargin,'noapply')) 42 | return 43 | end 44 | end 45 | 46 | 47 | %% dz0 apply loop - this could be run simultaneously/parallel 48 | i=1; 49 | for i =1:length(fileNames) 50 | fprintf('Applying offset to %s ....',fileNames{i}) 51 | 52 | m0=matfile(fileNames{i}); 53 | 54 | if any(strcmp(fields(m0),'adjusted')) 55 | if m0.adjusted 56 | fprintf('adjusted flag already true, skipping\n') 57 | continue 58 | end 59 | end 60 | 61 | if ~any(strcmp(fields(m0),'dz0')) 62 | fprintf('dz0 doesnt exist, skipping\n') 63 | continue 64 | end 65 | 66 | m0.Properties.Writable = true; 67 | m0.z = m0.z - m0.dz0; 68 | m0.adjusted = true; 69 | fprintf('done\n') 70 | end 71 | 72 | function calcdz0(fileName,resizeFraction) 73 | 74 | m0=matfile(fileName); 75 | s=whos(m0); 76 | sz= s(strcmp({s.name},'z')).size; 77 | dz0 = nan(ceil(sz.*resizeFraction)); 78 | 79 | [path,name,ext]=fileparts(fileName); 80 | 81 | if isempty(path) 82 | path='.'; 83 | end 84 | 85 | a = strsplit(name,'_'); 86 | stringLength=num2str(length(a{1})); 87 | tileRow=str2num(a{1}); 88 | tileCol=str2num(a{2}); 89 | 90 | formatString = ['%s/%0',stringLength,'d_%0',stringLength,'d']; 91 | 92 | % above 93 | file1 = [sprintf(formatString,path,tileRow+1,tileCol),... 94 | sprintf('_%s',a{3:end}),ext]; 95 | if exist(file1,'file') 96 | m1=matfile(file1); 97 | dzbuff = getdzbuff(m0,m1); 98 | dz0(1,:) = imresize(nanmean(dzbuff),resizeFraction); 99 | else 100 | dz0(1,:) = 0; 101 | end 102 | 103 | % below 104 | file1 = [sprintf(formatString,path,tileRow-1,tileCol),... 105 | sprintf('_%s',a{3:end}),ext]; 106 | if exist(file1,'file') 107 | m1=matfile(file1); 108 | dzbuff = getdzbuff(m0,m1); 109 | dz0(end,:) =imresize(nanmean(dzbuff),resizeFraction); 110 | else 111 | dz0(end,:) = 0; 112 | end 113 | 114 | % left 115 | file1 = [sprintf(formatString,path,tileRow,tileCol-1),... 116 | sprintf('_%s',a{3:end}),ext]; 117 | if exist(file1,'file') 118 | m1=matfile(file1); 119 | dzbuff = getdzbuff(m0,m1); 120 | dz0(:,1) = imresize(nanmean(dzbuff,2),resizeFraction); 121 | else 122 | dz0(:,1) = 0; 123 | end 124 | 125 | % right 126 | file1 = [sprintf(formatString,path,tileRow,tileCol+1),... 127 | sprintf('_%s',a{3:end}),ext]; 128 | if exist(file1,'file') 129 | m1=matfile(file1); 130 | dzbuff = getdzbuff(m0,m1); 131 | dz0(:,end) = imresize(nanmean(dzbuff,2),resizeFraction); 132 | else 133 | dz0(:,end) = 0; 134 | end 135 | 136 | dz0=single(inpaint_nans(double(dz0),2)); 137 | 138 | dz0 = imresize(dz0,sz); 139 | 140 | dz0 = dz0./2; 141 | 142 | m0.Properties.Writable = true; 143 | m0.dz0 = dz0; 144 | m0.adjusted = false; 145 | 146 | function dzbuff=getdzbuff(m0,m1) 147 | 148 | c0 = m0.x >= min(m1.x) & m0.x <= max(m1.x); 149 | r0 = m0.y >= min(m1.y) & m0.y <= max(m1.y); 150 | c0 = [find(c0,1,'first'),find(c0,1,'last')]; 151 | r0 = [find(r0,1,'first'),find(r0,1,'last')]; 152 | 153 | if isempty(c0) || isempty(r0); error('no overlap between tiles'); end 154 | 155 | c1 = m1.x >= min(m0.x) & m1.x <= max(m0.x); 156 | r1 = m1.y >= min(m0.y) & m1.y <= max(m0.y); 157 | c1 = [find(c1,1,'first'),find(c1,1,'last')]; 158 | r1 = [find(r1,1,'first'),find(r1,1,'last')]; 159 | 160 | dzbuff = m0.z(r0(1):r0(2),c0(1):c0(2)) - m1.z(r1(1):r1(2),c1(1):c1(2)); 161 | 162 | 163 | -------------------------------------------------------------------------------- /enviwrite.m: -------------------------------------------------------------------------------- 1 | function enviwrite(varargin) 2 | % ENVIWRITE write an array to an ENVI binary raster file and header. 3 | % ENVIWRITE('filename',Z) writes the array Z to the ENVI default binary 4 | % format. Also written is the ascii ENVI header file 'filename.hdr'. 5 | % ENVIWRITE('filename',X,Y,Z,'proj','projection name') uses 6 | 7 | %enviwrite(file,x,y,z,fmt); 8 | %creates envi ascii data and header files from cartesian x,y,z arrays. 9 | minarg = 4; 10 | maxarg = 12; 11 | 12 | % Check input 13 | error(nargchk(minarg,maxarg,nargin)); 14 | 15 | % requisite arguments 16 | file = varargin{1}; 17 | x = varargin{2}; 18 | y = varargin{3}; 19 | z = varargin{4}; 20 | for i = 1:size(z,3); 21 | zr(:,:,i) = z(:,:,i)'; 22 | end 23 | 24 | z = zr; 25 | 26 | % Default option values 27 | fmt = 4; % floating point format 28 | proj = 'UTM'; 29 | hemi = 'north'; 30 | datum = 'wgs-84'; 31 | units = 'Meters'; 32 | proj_info = []; 33 | % Alteration of default values 34 | for n = (minarg+1:2:nargin-1); 35 | % Checks that every value is a numeric value and every property 36 | % is a sting 37 | if ~isstr(varargin{n}) 38 | error('Invalid parameter/value pair arguments.'); 39 | % These properties take string arguments otherwise it must be 40 | % numeric value 41 | elseif ~isnumeric(varargin{n+1}) &&... 42 | ~strcmpi(varargin{n},'proj') &&... 43 | ~strcmpi(varargin{n},'hemi') &&... 44 | ~strcmpi(varargin{n},'datum')&&... 45 | ~strcmpi(varargin{n},'units') 46 | 47 | msg = sprintf(['Bad value for property: '... 48 | '''' varargin{n} '''\n unknown option.']); 49 | error(msg); 50 | end 51 | 52 | % Sets property value 53 | switch lower(varargin{n}) 54 | case 'format' 55 | fmt = varargin{n+1}; 56 | case 'zone' 57 | zone = varargin{n+1}; 58 | case 'proj' 59 | proj = varargin{n+1}; 60 | case 'hemi' 61 | hemi = varargin{n+1}; 62 | case 'datum' 63 | datum = varargin{n+1}; 64 | case 'units' 65 | units = varargin{n+1}; 66 | end 67 | end 68 | 69 | 70 | switch lower(proj) 71 | 72 | case 'polar stereo north'; 73 | proj_info = '31, 6378137.0, 6356752.3, 70.000000, -45.000000, 0, 0, WGS-84, Polar Stereo North, units=Meters'; 74 | 75 | case 'polar stereo south'; 76 | proj_info = '31, 637813.0, 635674.5, -71.000000, 0.000000, 0, 0, WGS-84, Polar Stereo South, units=Meters'; 77 | 78 | case 'bamber' 79 | proj_info = '31, 6378137.0, 6356752.3, 70.000000, -39.000000, 1, 1, WGS-84, bamber, units=Meters'; 80 | case 'bamber2' 81 | proj_info = '31, 6378137.0, 6356752.3, 71.000000, -39.000000, 1, 1, WGS-84, bamber, units=Meters'; 82 | end 83 | 84 | if size(x,1) > 1 && size(x,2) > 1; 85 | x = x(1,:); 86 | else 87 | x = x(:); 88 | end; 89 | 90 | if size(y,1) > 1 && size(y,2) > 1; 91 | y = y(:,1); 92 | else 93 | y = y(:); 94 | end; 95 | 96 | 97 | d = [datestr(now,8),' ',datestr(now,3),' ',datestr(now,7),' ',datestr(now,13),' ',datestr(now,10)]; 98 | [samples,lines,bands] = size(z); 99 | x1 = min(x); 100 | y1 = max(y); 101 | dx = abs(diff(x(1:2))); 102 | dy = abs(diff(y(1:2))); 103 | 104 | 105 | switch fmt 106 | case 1; fmtstr = 'uint8'; 107 | case 2; fmtstr = 'int16'; 108 | case 3; fmtstr = 'int32'; 109 | case 4; fmtstr = 'single'; 110 | case 5; fmtstr = 'double'; 111 | case 6; error('cant write complex numbers') %complex single 112 | case 9; error('cant write complex numbers') %complex double 113 | case 11; fmtstr = 'int8'; fmt = 1; %envi can't do signed byte 114 | case 12; fmtstr = 'uint16'; 115 | case 13; fmtstr = 'uint32'; 116 | case 14; fmtstr = 'int64'; 117 | case 15; fmtstr = 'uint64'; 118 | end 119 | 120 | fid = fopen(file,'w'); 121 | count = fwrite(fid,z,fmtstr); fclose all; 122 | 123 | 124 | fid = fopen([file,'.hdr'],'w');% 125 | fprintf(fid,'%s\n','ENVI'); % 126 | fprintf(fid,'%s\n','description = {'); 127 | fprintf(fid,'%s\n',[' Exported Matlab Array [',d,']}']); 128 | fprintf(fid,'%s\n',['samples = ',num2str(samples)]); 129 | fprintf(fid,'%s\n',['lines = ',num2str(lines)]); 130 | fprintf(fid,'%s\n',['bands = ',num2str(size(z,3))]); 131 | fprintf(fid,'%s\n','header offset = 0'); 132 | fprintf(fid,'%s\n','file type = ENVI Standard'); 133 | fprintf(fid,'%s\n',['data type = ',num2str(fmt)]); 134 | fprintf(fid,'%s\n','interleave = bsq'); 135 | fprintf(fid,'%s\n','sensor type = Unknown'); 136 | fprintf(fid,'%s\n','byte order = 0'); 137 | if strcmpi(proj,'UTM'); 138 | fprintf(fid,'%s\n',['map info = { ',proj,', 1.000, 1.000, ',num2str(x1),', ',num2str(y1),', ',num2str(dx),', ',num2str(dy),', ',num2str(zone),', ',hemi,', ',datum,', units=',units,'}']); 139 | else 140 | fprintf(fid,'%s\n',['map info = { ',proj,', 1.000, 1.000, ',num2str(x1),', ',num2str(y1),', ',num2str(dx),', ',num2str(dy),',',datum,', units=',units,'}']); 141 | fprintf(fid,'%s\n',['projection info = {',proj_info,'}']); 142 | end; 143 | fprintf(fid,'%s\n','wavelength units = Unknown'); 144 | fclose(fid);% close the file 145 | -------------------------------------------------------------------------------- /applyRegistration.m: -------------------------------------------------------------------------------- 1 | function z = applyRegistration(dtrans,m,N,varargin) 2 | % ApplyRegistration applies 3-D transformation to grid. V4 compatible 3 | % z = applyRegistration(dtrans,m,z,N) dtrans is [x,y,z] offsets to be 4 | % *subtracted* . m is matfile object. N is coregistration mask. 5 | % z = applyRegistration(...,'gridVar','varname') 6 | % z = applyRegistration(...,'subsetSize',length) 7 | 8 | %check dtrans 9 | if numel(dtrans) ~= 3 10 | error('dtrans must be a 3 element vector') 11 | end 12 | if any(isnan(dtrans)) 13 | error('dtrans contains NaNs') 14 | end 15 | 16 | % parse/error check gridVar argument 17 | gridVar='z'; %default 18 | subsetSize=5000; % dimension of interpoaltion subset(pixels=subsetSize^2) 19 | 20 | 21 | if ~isempty(varargin) 22 | 23 | n = find(strcmpi(varargin,'gridvar')); 24 | if ~isempty(n) 25 | gridVar=varargin{n+1}; 26 | end 27 | 28 | n = find(strcmpi(varargin,'subsetsize')); 29 | if ~isempty(n) 30 | subsetSize=varargin{n+1}; 31 | end 32 | end 33 | 34 | % extract grid vectors for speed rather in pulling on each iter 35 | x=m.x; 36 | y=m.y; 37 | y = y(:); 38 | res = x(2)-x(1); 39 | 40 | % to speed up/converve memory, we'll perform gridded interpolation over 41 | % 1000x1000 pixel subsets, overlapping by one pixel. 42 | % Make a list of min max indices. 43 | sz = [length(y) length(x)]; 44 | 45 | intsCol=1:subsetSize:sz(2); 46 | intsCol=[intsCol(1:end-1)',intsCol(2:end)']; 47 | intsCol(end)=sz(2); 48 | intsCol(1:end-1,2)=intsCol(1:end-1,2)-1; 49 | 50 | intsRow=1:subsetSize:sz(1); 51 | intsRow=[intsRow(1:end-1)',intsRow(2:end)']; 52 | intsRow(end)=sz(1); 53 | intsRow(1:end-1,2)=intsRow(1:end-1,2)-1; 54 | 55 | % need to add pixel buffer to int to account for shifts, scaling with dtrans 56 | buff=max(ceil(abs(dtrans(2:3)./res))); 57 | buffsCol=[[0 buff];repmat([-buff buff],size(intsCol,1)-2,1);[-buff 0]]; 58 | buffsRow=[[0 buff];repmat([-buff buff],size(intsRow,1)-2,1);[-buff 0]]; 59 | 60 | %% nested interpolation loops with interpolation dependent on grid variable 61 | 62 | % initialize output 63 | switch gridVar 64 | case {'z','z_mad'} 65 | z = nan(sz,'single'); 66 | case {'tmin','tmax'} 67 | z= zeros(sz,'uint16'); 68 | case {'N','Nmt'} 69 | z = zeros(sz,'uint8'); 70 | end 71 | 72 | % subset interpolation loop 73 | for i=1:length(intsRow) 74 | 75 | % make subset row vector with buffer 76 | intRowBuff=intsRow(i,1)+buffsRow(i,1):intsRow(i,2)+buffsRow(i,2); 77 | 78 | for j=1:length(intsCol) 79 | 80 | % make subset column vector with buffer 81 | intColBuff=intsCol(j,1)+buffsCol(j,1):intsCol(j,2)+buffsCol(j,2); 82 | 83 | % extract subset from unregistered tile with buffer and add offset 84 | eval(['z0 = m.',gridVar,'(intRowBuff,intColBuff);']); 85 | 86 | % convert to single for interpolation 87 | z0 = single(z0); 88 | 89 | % apply coregistration cluster mask 90 | z0(~N(intRowBuff,intColBuff)) = NaN; 91 | 92 | % check for empty subset to skip 93 | if ~any(~isnan(z0(:)) & z0(:) ~= 0); continue; end 94 | 95 | % make unbuffered row and column vectors to insert into new 96 | % array 97 | intRow=intsRow(i,1):intsRow(i,2); 98 | intCol=intsCol(j,1):intsCol(j,2); 99 | 100 | x1 = x(intColBuff)-dtrans(2); 101 | y1 = y(intRowBuff)-dtrans(3); 102 | 103 | % interpolate depending on variable 104 | switch gridVar 105 | 106 | case 'z' 107 | 108 | % apply xy,z offsets and interpolate 109 | z(intRow,intCol) =interp2(x1,y1, z0-dtrans(1),... 110 | x(intCol),y(intRow),'*linear'); 111 | 112 | case 'z_mad' % error grid 113 | 114 | % apply only x and y offsets and interpolate 115 | z(intRow,intCol) =interp2(x1,y1, z0,... 116 | x(intCol),y(intRow),'*linear'); 117 | 118 | case {'N','Nmt'} % stack and matchtag numbers 119 | 120 | z0(isnan(z0))=0; 121 | 122 | % apply horizontal offsets and interpolate 123 | z0=interp2(x1,y1, z0, x(intCol),y(intRow),'*nearest'); 124 | 125 | % convert back to uint8 and insert into array 126 | z0(isnan(z0)) = 0; 127 | z(intRow,intCol)=uint8(z0); 128 | 129 | clear z0 130 | 131 | case 'or' 132 | 133 | % apply horizontal offsets and interpolate 134 | z0=interp2(x1,y1, z0, x(intCol),y(intRow),'*cubic'); 135 | 136 | % convert back to int16 and insert into array 137 | z0(isnan(z0)) = 0; 138 | z(intRow,intCol)=int16(z0); 139 | 140 | case {'tmin','tmax'} 141 | 142 | % apply horizontal offsets and interpolate 143 | z0=interp2(x1,y1, z0, x(intCol),y(intRow),'*linear'); 144 | 145 | z0(isnan(z0)) = 0; % convert back to uint8 146 | z(intRow,intCol)=uint16(z0); 147 | 148 | otherwise 149 | error('grid variable input argument not recognized, must be ''z'',''z_mad'',''tmin'',''tmax'',''N'' or ''Nmt''') 150 | 151 | end 152 | 153 | clear z0 154 | 155 | end 156 | end 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /registerTile.m: -------------------------------------------------------------------------------- 1 | function registerTile(m,gcp) 2 | % registerTile register tiles to ground control 3 | % 4 | % registerTile(m,gcp) registers the DEM tile mosaic z in matlab file 5 | % handle m (with m.z as elevation) to the gcp points in structure gcp, 6 | % which has fields gcp.x, gcp.y and gcp.z. Optional field dataset is the 7 | % name/source of the gcp dataset 8 | % 9 | % subfuncs: polyCropGCPs, pointAreaSubsets, registerDEM2LIDAR, 10 | % applyRegistration 11 | % 12 | % Ian Howat, Ohio State, ihowat@gmail.com 13 | % version 1.0 06-Dec-2016 14:49:11 14 | 15 | maxGcps=1000; % maximum number of gcps to use. Will sample gcp vector evenly to give the closest integer N > maxGcps. 16 | 17 | %% Check for multiple coregistration clusters 18 | 19 | % fastest is to look for a zero dtrans's 20 | coregClusters=sum(sum(m.dtrans==0)==3); 21 | 22 | % if no un-co-registered data, return 23 | if coregClusters == 0 24 | fprintf('All data is registered, skipping\n'); 25 | return 26 | end 27 | 28 | % load coregistration cluster 29 | C=m.C; 30 | 31 | sz = whos(m,'z'); sz = sz.size; % image dimensions info 32 | z = nan(sz,'single'); % initialize output 33 | 34 | %create output file 35 | outname=strrep(m.Properties.Source,'dem.mat','reg_dem.mat'); 36 | 37 | m1 = matfile(outname,'Writable',true); 38 | 39 | %initialize registration offsets/residual output 40 | dtrans=nan(3,coregClusters); 41 | dzall=cell(1,coregClusters); 42 | 43 | % cluster coregistraton loop 44 | for i=1:coregClusters 45 | 46 | fprintf('Registering coregistered cluster %d of %d\n',i,coregClusters); 47 | 48 | % make a mask of this cluster 49 | N = C == i+1; 50 | 51 | %% load overlapping GCPs full tile boundaries 52 | 53 | % make tile polygon footprint vertices from rectangular coordinate range 54 | x=m.x; 55 | y=m.y; 56 | 57 | minx=min(x); 58 | maxx=max(x); 59 | miny=min(y); 60 | maxy=max(y); 61 | 62 | xv=[minx,minx,maxx,maxx,minx]; 63 | yv=[miny,maxy,maxy,miny,miny]; 64 | 65 | n=polyCropGCPs(gcp,xv,yv,'rectangle'); 66 | 67 | % skip if too few 68 | if length(n) < 4 69 | fprintf('%d overlapping points, too few,skipping\n',length(n)); 70 | continue 71 | end 72 | 73 | % find gcp's over these pixels 74 | col = round((gcp.x(n) - minx)/(x(2)-x(1))); 75 | row = round((gcp.y(n) - maxy)/(y(2)-y(1))); 76 | col(col < 1)=1; row(row < 1) = 1; 77 | col(col > length(x)) = length(x); row(row > length(y)) = length(y); 78 | gcpind = sub2ind([length(y),length(x)],row,col); 79 | nn = N(gcpind); 80 | 81 | n = n(nn); 82 | 83 | % skip if too few 84 | if length(n) < 4 85 | fprintf('%d overlapping points, too few,skipping\n',length(n)); 86 | continue 87 | end 88 | 89 | %subsample GCPs to near maximum # for speed/memory 90 | if length(n) > maxGcps 91 | n=n(1:floor(length(n)/ maxGcps):length(n)); 92 | end 93 | 94 | %% Registration with subsetting 95 | % For fitting to control, we can either load the whole image or load 96 | % subsets of the image in the neighborhood of each control point with 97 | % a size large enough to allow for shifting within the expected image 98 | % registration error. The former is fastest for larger numbers of GCPs 99 | % and or smaller image sizes. We can select by calculating the total 100 | % number of pixels to be loaded by each method and use the less. 101 | 102 | % subset size expect +/- 20m maximum image displacement 103 | res=diff(x(1:2)); 104 | dd=20/res; 105 | 106 | [xsub,ysub,zsub]=pointAreaSubsets(gcp.x(n),gcp.y(n),x,y,m,dd,N); 107 | 108 | % send to registration fx 109 | [dtrans(:,i),dzall{i}] = ... 110 | registerDEM2LIDAR(xsub,ysub,zsub,gcp.x(n),gcp.y(n),gcp.z(n)); 111 | 112 | if any(isnan(dtrans(:,i))); continue; end 113 | 114 | clear xsub ysub zsub n 115 | 116 | %% Apply registration 117 | ztemp = applyRegistration(dtrans(:,i),m,N); 118 | 119 | clear N 120 | 121 | n=isnan(z) & ~isnan(ztemp); 122 | z(n) = ztemp(n); 123 | 124 | clear ztemp n 125 | 126 | end 127 | 128 | % check for failure 129 | if ~any(~isnan(dtrans(:))); fprintf('registration failed\n'); return; end 130 | 131 | % write to matfile 132 | m1.dtrans = dtrans; 133 | m1.dzall = dzall; 134 | m1.x=x; 135 | m1.y=y; 136 | m1.z=z; 137 | 138 | clear z 139 | 140 | %% Apply registration to mt grid variable 141 | 142 | % cluster coregistraton loop 143 | mt = false(sz); % initialize output 144 | for i=1:coregClusters 145 | 146 | if any(isnan(dtrans(:,i))); continue; end 147 | 148 | % make a mask of this cluster 149 | N = C == i+1; 150 | 151 | % Apply registration 152 | mttemp = applyRegistration(dtrans(:,i),m,N,'mt'); 153 | 154 | % add in any matches 155 | mt = mt | mttemp; 156 | 157 | clear mttemp 158 | 159 | end 160 | 161 | m1.mt=mt; 162 | 163 | clear mt 164 | 165 | %% Apply registration to or grid variable 166 | 167 | % cluster coregistraton loop 168 | or = zeros(sz,'int16'); % initialize output 169 | for i=1:coregClusters 170 | 171 | if any(isnan(dtrans(:,i))); continue; end 172 | 173 | % make a mask of this cluster 174 | N = C == i+1; 175 | 176 | % Apply registration 177 | ortemp = applyRegistration(dtrans(:,i),m,N,'or'); 178 | 179 | clear N 180 | 181 | n= or==0 & ortemp ~= 0; 182 | or(n) = ortemp(n); 183 | 184 | clear ortemp n 185 | 186 | end 187 | 188 | m1.or=or; 189 | 190 | clear or 191 | 192 | %% Apply registration to dy grid variable 193 | 194 | % cluster coregistraton loop 195 | dy= zeros(sz,'int16'); % initialize output 196 | for i=1:coregClusters 197 | 198 | if any(isnan(dtrans(:,i))); continue; end 199 | 200 | % make a mask of this cluster 201 | N = C == i+1; 202 | 203 | % Apply registration 204 | dytemp = applyRegistration(dtrans(:,i),m,N,'dy'); 205 | 206 | clear N 207 | 208 | n= dy==0 & dytemp ~= 0; 209 | dy(n) = dytemp(n); 210 | 211 | clear dytemp n 212 | 213 | end 214 | 215 | m1.dy=dy; 216 | 217 | clear dy 218 | 219 | if isfield(gcp,'dataset') 220 | dataset=gcp.dataset; 221 | else 222 | dataset='unknown'; 223 | end 224 | 225 | tileRegMeta(m1,dataset) 226 | -------------------------------------------------------------------------------- /compileStripMeta.m: -------------------------------------------------------------------------------- 1 | function [f,creationDate,stripDate,res,x,y,avg_rmse,med_rmse,max_rmse,Nscenes,reg]=compileStripMeta(fdir) 2 | meta_str='meta'; 3 | reg_str='reg'; 4 | disableReg=true; 5 | 6 | f=dir([fdir,'/*',meta_str,'.txt']); 7 | f={f.name}'; 8 | 9 | creationDate=nan(size(f)); 10 | x=cell(size(f)); 11 | y=cell(size(f)); 12 | 13 | res=nan(size(f)); 14 | avg_rmse=nan(size(f)); 15 | med_rmse=nan(size(f)); 16 | max_rmse=nan(size(f)); 17 | Nscenes=nan(size(f)); 18 | 19 | stripDate=char(f{:}); 20 | stripDate=datenum(stripDate(:,6:13),'yyyymmdd'); 21 | 22 | reg.datasets = cell(size(f)); 23 | reg.Ngcps = nan(size(f)); 24 | reg.NgcpsRock = nan(size(f)); 25 | reg.NgcpsIce = nan(size(f)); 26 | reg.quads = nan(size(f)); 27 | reg.avgIceGCPdt = nan(size(f)); 28 | reg.trans = nan(size(f,1),3); 29 | reg.avgdz = nan(size(f)); 30 | reg.avgdz_rock = nan(size(f)); 31 | reg.avgdz_ice = nan(size(f)); 32 | reg.stddz = nan(size(f)); 33 | reg.stddz_rock = nan(size(f)); 34 | reg.stddz_ice = nan(size(f)); 35 | reg.dzpc = nan(size(f,1),11); 36 | reg.dzpc_rock = nan(size(f,1),11); 37 | reg.dzpc_ice = nan(size(f,1),11); 38 | 39 | 40 | for i=1:length(f) 41 | 42 | c=textread([fdir,'/',f{i}],'%s','delimiter','\n'); 43 | 44 | r=find(~cellfun(@isempty,strfind(c,'Strip creation date:'))); 45 | creationDate(i)=datenum(deblank(strrep(c{r},'Strip creation date:',''))); 46 | 47 | info=imfinfo(strrep([fdir,'/',f{i}],'_meta.txt','_dem.tif')); 48 | res(i)=info.ModelPixelScaleTag(1); 49 | 50 | r=find(~cellfun(@isempty,strfind(c,'X:'))); 51 | x{i}=str2num(strrep(c{r},'X:','')); 52 | 53 | r=find(~cellfun(@isempty,strfind(c,'Y:'))); 54 | y{i}=str2num(strrep(c{r},'Y:','')); 55 | 56 | 57 | r=find(~cellfun(@isempty,strfind(c,'Mosaicking Alignment Statistics (meters)'))); 58 | r=r+3; 59 | A=[]; 60 | while r 61 | if isempty(c{r}); break; end 62 | A=[A;sscanf(c{r},'%*s %f %f %f %f')']; 63 | r=r+1; 64 | end 65 | 66 | clear c 67 | 68 | if isempty(A); 69 | Nscenes(i)=1; 70 | else 71 | 72 | avg_rmse(i)=nanmean(A(:,1)); 73 | med_rmse(i)=nanmedian(A(:,1)); 74 | max_rmse(i)=nanmax(A(:,1)); 75 | Nscenes(i)=size(A,1)+1; 76 | 77 | end 78 | 79 | 80 | regfile=strrep([fdir,'/',f{i}],meta_str,reg_str); 81 | if exist(regfile,'file') && ~disableReg 82 | 83 | c=textread(regfile,'%s','delimiter','\n'); 84 | 85 | astr='Registration Dataset 1 Name:'; 86 | r=find(~cellfun(@isempty,strfind(c,astr))); 87 | if length(r) > 1; disp('multiple datasets'); end 88 | reg.datasets{i} = strrep(c{r},astr,''); 89 | 90 | astr='# GCPs='; 91 | r=find(~cellfun(@isempty,strfind(c,astr))); 92 | reg.Ngcps(i) = str2num(strrep(c{r},astr,'')); 93 | 94 | astr='# Rock GCPs='; 95 | r=find(~cellfun(@isempty,strfind(c,astr))); 96 | if (~exist('r')) 97 | reg.NgcpsRock(i) = str2num(strrep(c{r},astr,'')); 98 | end 99 | 100 | astr='# Ice GCPs='; 101 | r=find(~cellfun(@isempty,strfind(c,astr))); 102 | if (~exist('r')) 103 | reg.NgcpsIce(i) = str2num(strrep(c{r},astr,'')); 104 | end 105 | 106 | astr='Quadrants with GCPs='; 107 | r=find(~cellfun(@isempty,strfind(c,astr))); 108 | if (~exist('r')) 109 | reg.quads(i) = str2num(strrep(c{r},astr,'')); 110 | end 111 | 112 | if reg.NgcpsIce(i) > 0; 113 | astr='Average Ice GCP time offset='; 114 | r=find(~cellfun(@isempty,strfind(c,astr))); 115 | reg.avgIceGCPdt(i)= str2num(strrep(c{r},astr,'')); 116 | end 117 | 118 | astr='Translation vector (dz,dx,dy)(meters)='; 119 | r=find(~cellfun(@isempty,strfind(c,astr))); 120 | reg.trans(i,:)= str2num(strrep(c{r},astr,'')); 121 | 122 | astr='All GCP DZ AVG='; 123 | r=find(~cellfun(@isempty,strfind(c,astr))); 124 | if (~exist('r')) 125 | reg.avgdz(i)= str2num(strrep(c{r},astr,'')); 126 | end 127 | 128 | astr='Rock GCP DZ AVG='; 129 | r=find(~cellfun(@isempty,strfind(c,astr))); 130 | if (~exist('r')) 131 | reg.avgdz_rock(i)= str2num(strrep(c{r},astr,'')); 132 | end 133 | 134 | astr='Ice GCP DZ AVG='; 135 | r=find(~cellfun(@isempty,strfind(c,astr))); 136 | if (~exist('r')) 137 | reg.avgdz_ice(i)= str2num(strrep(c{r},astr,'')); 138 | end 139 | 140 | astr='All GCP DZ MED='; 141 | r=find(~cellfun(@isempty,strfind(c,astr))); 142 | if (~exist('r')) 143 | reg.avgdz(i)= str2num(strrep(c{r},astr,'')); 144 | end 145 | 146 | astr='Rock GCP DZ MED='; 147 | r=find(~cellfun(@isempty,strfind(c,astr))); 148 | if (~exist('r')) 149 | reg.avgdz_rock(i)= str2num(strrep(c{r},astr,'')); 150 | end 151 | 152 | astr='Ice GCP DZ MED='; 153 | r=find(~cellfun(@isempty,strfind(c,astr))); 154 | if (~exist('r')) 155 | reg.avgdz_ice(i)= str2num(strrep(c{r},astr,'')); 156 | end 157 | 158 | astr='All GCP DZ STD='; 159 | r=find(~cellfun(@isempty,strfind(c,astr))); 160 | if (~exist('r')) 161 | reg.stddz(i)= str2num(strrep(c{r},astr,'')); 162 | end 163 | 164 | astr='Rock GCP DZ STD='; 165 | r=find(~cellfun(@isempty,strfind(c,astr))); 166 | if (~exist('r')) 167 | reg.stddz_rock(i)= str2num(strrep(c{r},astr,'')); 168 | end 169 | 170 | astr='Ice GCP DZ STD='; 171 | r=find(~cellfun(@isempty,strfind(c,astr))); 172 | if (~exist('r')) 173 | reg.stddz_ice(i)= str2num(strrep(c{r},astr,'')); 174 | end 175 | 176 | astr='All='; 177 | r=find(~cellfun(@isempty,strfind(c,astr))); 178 | if (~exist('r')) 179 | reg.dzpc(i,:) = str2num(strrep(c{r},astr,'')); 180 | end 181 | 182 | astr='Rock='; 183 | r=find(~cellfun(@isempty,strfind(c,astr))); 184 | if (~exist('r')) 185 | reg.dzpc_rock(i,:)= str2num(strrep(c{r},astr,'')); 186 | end 187 | 188 | astr='Ice='; 189 | r=find(~cellfun(@isempty,strfind(c,astr))); 190 | if (~exist('r')) 191 | reg.dzpc_ice(i,:) = str2num(strrep(c{r},astr,'')); 192 | end 193 | 194 | end 195 | 196 | end 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /registerStrips.m: -------------------------------------------------------------------------------- 1 | function registerStrips(demdir,gcp,sensor) 2 | % REGISTERSTRIPS register DEM strip data to ground control point cloud 3 | % 4 | % registerStrips(demdir,gcp,sensor) where demdir is the dem directory path 5 | % gcp is a structure with fields x,y,z and optional t. sensor is the sensor 6 | % id string. OUtputs a *_reg.txt file with translation parameters and 7 | % statistics. Uses the interative regression method of Nuth and Kaab, 2011 8 | 9 | % Ian Howat, Ohio State University 10 | % Version 1: 27-Sep-2016 09:57:42 11 | 12 | fdem=dir([demdir,'/*_dem.tif']); 13 | fdem={fdem.name}; 14 | fdem=fdem(:); 15 | 16 | if isempty(fdem); return; end 17 | 18 | imdate=char(fdem); 19 | imdate=imdate(:,6:13); 20 | imdate=datenum(imdate,'yyyymmdd'); 21 | 22 | % get integer month of each control point 23 | if isfield(gcp,'t'); [~,gcp.mo] = datevec(gcp.t); end 24 | 25 | % loop through DEMs 26 | for i=1:length(fdem) 27 | 28 | % make outname 29 | outfile = [demdir,'/',strrep(fdem{i},'_dem.tif','_reg.txt')]; 30 | 31 | if exist(outfile,'file'); 32 | disp('reg file exists, skipping'); 33 | continue 34 | end 35 | 36 | fprintf('processing %d of %d, %s\n',i,length(fdem),[demdir,'/',fdem{i}]); 37 | 38 | % get polygon from meta file 39 | metafile=[demdir,'/',strrep(fdem{i},'_dem.tif','_meta.txt')]; 40 | 41 | c=textread(metafile,'%s','delimiter','\n'); % read metafile into cellstr 42 | 43 | r=find(~cellfun(@isempty,strfind(c,'X:'))); % extract x verts 44 | xv=str2num(strrep(c{r},'X:','')); 45 | 46 | r=find(~cellfun(@isempty,strfind(c,'Y:'))); % extract y verts 47 | yv=str2num(strrep(c{r},'Y:','')); 48 | 49 | clear c r 50 | 51 | %% Find overlapping GCPs 52 | 53 | % fast rectangle crop gcp data to rectangularized DEM boundarys 54 | n= gcp.x >= min(xv) & gcp.x <=max(xv) &... 55 | gcp.y >= min(yv) & gcp.y <=max(yv); 56 | 57 | if ~any(n); fprintf('0 overlapping points, skipping\n'); continue; end 58 | 59 | % polygon crop 60 | n=find(n); % reduce index to fast crop selection 61 | 62 | nn = inpolygon(gcp.x(n),gcp.y(n),xv,yv); % polygon crop 63 | 64 | % skip if too few 65 | if sum(nn) < 4; 66 | fprintf('%d overlapping points, too few,skipping\n',sum(nn)); 67 | continue; 68 | end 69 | 70 | n=n(nn); clear nn % reduce index to fast crop selection 71 | 72 | if isfield(gcp,'t'); 73 | %% Month selection 74 | % In order to reduce seasonal variability in height, select gcps within 75 | % +/- 1 month of the image date. If this results in too few points, 76 | % expand range incrementally until enough points are found. 77 | 78 | [~,mo]=datevec(imdate(i));% get image integer month to select to gcps 79 | 80 | % get absolute difference in months between image and gcps accounting 81 | % for annual wrap-around 82 | dmo=abs(gcp.mo(n)-mo); 83 | dmo(dmo > 6) = 12-dmo(dmo > 6); 84 | 85 | 86 | month_range=0;% initial month range +/- 87 | nn=0; % initialize index 88 | while sum(nn) < 4; % test if enough gcps 89 | % expand range by +/- 1 month 90 | month_range=month_range+1; 91 | % search for gcps in ranage 92 | nn = dmo <= month_range; 93 | end 94 | 95 | n=n(nn); clear nn % crop index to selection 96 | fprintf('%d points within %d month(s), performing registration\n',... 97 | length(n),month_range); 98 | else 99 | fprintf('%d overlapping points found, performing registration\n',length(n)); 100 | month_range=12; 101 | end 102 | 103 | %% Registration with subsetting 104 | % For fitting to control, we can either load the whole image or load 105 | % subsets of the image in the neighborhood of each control point with 106 | % a size large enough to allow for shifting within the expected image 107 | % registration error. The former is fastest for larger numbers of GCPs 108 | % and or smaller image sizes. We can select by calculating the total 109 | % number of pixels to be loaded by each method and use the less. 110 | 111 | % subset size expect +/- 20m maximum image displacement 112 | dd=20; 113 | 114 | info=imfinfo([demdir,'/',fdem{i}]); % image dimensions info 115 | if ceil(2.*dd/info.ModelPixelScaleTag(1)+1).^2 * length(n) < info.Width * info.Height %test 116 | 117 | % subsetting loads fewer pixels, so we'll use it 118 | 119 | % initialize coordinate subset cells 120 | x=cell(length(n),1); 121 | y=cell(length(n),1); 122 | z=cell(length(n),1); 123 | 124 | % subsetting loop 125 | j=1; 126 | for j=1:length(n); 127 | 128 | a= readGeotiff([demdir,'/',fdem{i}],... 129 | 'map_subset',[ gcp.x(n(j)) - dd, gcp.x(n(j)) + dd, ... 130 | gcp.y(n(j)) - dd, gcp.y(n(j)) + dd ]); 131 | 132 | x{j}=a.x; 133 | y{j}=a.y; 134 | a.z(a.z <= -100) = NaN; 135 | z{j} = a.z; 136 | clear a 137 | end 138 | 139 | else % load whole image 140 | z= readGeotiff([demdir,'/',fdem{i}]); 141 | x=z.x; 142 | y=z.y; 143 | z=z.z; 144 | z(z <= -100) = NaN; 145 | end 146 | 147 | % send to registration fx 148 | [dtrans,dzall] = ... 149 | registerDEM2LIDAR(x,y,z,gcp.x(n),gcp.y(n),gcp.z(n)); 150 | 151 | % find points used in registration 152 | nn = abs(dzall - nanmedian(dzall)) < nanstd(dzall); 153 | 154 | clear x y z 155 | 156 | if isempty(dzall); continue; end 157 | 158 | ptiles=[50:5:100]; 159 | dzptiles=prctile(abs(dzall),ptiles); 160 | 161 | fid=fopen(outfile,'w'); 162 | fprintf(fid,'DEM Filename: %s\n',fdem{i}); 163 | j=1; for j=1:length(sensor); fprintf(fid,'Registration Dataset %d Name: %s\n',j,sensor{j});end 164 | fprintf(fid,'GCP Month Range (+/- months)=%d\n',month_range); 165 | fprintf(fid,'# GCPs=%d\n',sum(nn)); 166 | fprintf(fid,'Mean Vertical Residual (m)=%.3f\n',nanmean(dzall(nn))); 167 | fprintf(fid,'Median Vertical Residual (m)=%.3f\n',nanmedian(dzall(nn))); 168 | fprintf(fid,'Translation Vector (dz,dx,dy)(m)= %.3f, %.3f, %.3f \n',dtrans); 169 | fprintf(fid,'Vertical Deviation Percentiles(m):\n'); 170 | j=1; for j=1:length(ptiles); 171 | fprintf(fid,'%dth percentile=',ptiles(j)); fprintf(fid,'%.3f ',dzptiles(j)); fprintf(fid,'\n'); 172 | end 173 | fclose(fid); 174 | clear n nn dzall ptiles dzptiles 175 | fprintf('\n'); 176 | end 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /alignTile.m: -------------------------------------------------------------------------------- 1 | function [outFlag,outname]=alignTile(f) 2 | % alignTile Aligns unregistered tile to registered neigbors 3 | % 4 | % [outFlag,outname]=alignTile(f) where f is a cellstr of path/files with 5 | % f{1} the floating file to be aligned and f{2:end} the registered files to 6 | % align it to. outFlag returns true if successful, with output file name. 7 | % If fail, outFlag will be false and outname will be empty. 8 | 9 | % set out flag 10 | outFlag=false; 11 | outname=[]; 12 | 13 | %% first test if input args are either valid filenames or mat file handles 14 | nfiles=length(f); 15 | for i=1:nfiles 16 | 17 | if ischar(f{i}) % it's a string, might be a filename 18 | if exist(f{i},'file') % yup its a file 19 | m = matfile(f{i}); % load it 20 | else 21 | error('File does not exist'); 22 | end 23 | elseif isvalid(f{i}) % not a string, is it a valid file handle? 24 | m = f{i}; % yes, so set m to f and get the filename for f 25 | f{i} = m.Properties.Source; 26 | else 27 | error('input arg must be a filename or valid matfile handle') 28 | end 29 | 30 | eval(['f',num2str(i-1),'=f{i};']); 31 | eval(['m',num2str(i-1),'=m;']); 32 | 33 | clear m 34 | end 35 | 36 | info0=whos(m0,'z'); 37 | sz0=info0.size; 38 | 39 | dtrans=cell(1,nfiles-1); 40 | rmse=cell(1,nfiles-1); 41 | 42 | %% Coregister to each neighbor 43 | for i=2:nfiles 44 | 45 | eval(['m=m',num2str(i-1),';']); 46 | 47 | % crop reference to buffer 48 | c0 = m0.x >= min(m.x) & m0.x <= max(m.x); 49 | r0 = m0.y >= min(m.y) & m0.y <= max(m.y); 50 | c0 = [find(c0,1,'first'),find(c0,1,'last')]; 51 | r0 = [find(r0,1,'first'),find(r0,1,'last')]; 52 | 53 | if isempty(c0) || isempty(r0); error('no overlap between tiles'); end 54 | 55 | % crop floating tile to buffer 56 | c1 = m.x >= min(m0.x) & m.x <= max(m0.x); 57 | r1 = m.y >= min(m0.y) & m.y <= max(m0.y); 58 | c1 = [find(c1,1,'first'),find(c1,1,'last')]; 59 | r1 = [find(r1,1,'first'),find(r1,1,'last')]; 60 | 61 | % Coregistration cluster array 62 | C0=m0.C(r0(1):r0(2),c0(1):c0(2)); 63 | 64 | coregClusters=max(C0(:))-1; 65 | 66 | if coregClusters == -1; fprintf('no overlapping data in %s\n',f{i}); continue; end 67 | 68 | if coregClusters == 0; error('Grid appears to be already registered'); end 69 | 70 | %% pull buffer data and align 71 | x0 = m0.x(1,c0(1):c0(2)); 72 | y0 = m0.y(r0(1):r0(2),1); 73 | z0 = m0.z(r0(1):r0(2),c0(1):c0(2)); 74 | mt0= m0.mt(r0(1):r0(2),c0(1):c0(2)); 75 | x1 = m.x(1,c1(1):c1(2)); 76 | y1 = m.y(r1(1):r1(2),1); 77 | z1 = m.z(r1(1):r1(2),c1(1):c1(2)); 78 | %mt1= m.mt(r0(1):r0(2),c0(1):c0(2)); 79 | 80 | % cluster coregistraton loop 81 | for j=1:coregClusters 82 | 83 | fprintf('Registering coregistered cluster %d of %d\n',j,coregClusters); 84 | 85 | % make a mask of this cluster 86 | mt0c= mt0 & (C0 == j+1); 87 | 88 | % co-register floating tile to reference tile 89 | [~,dtrans{j,i-1},rmse{j,i-1}] = coregisterdems(x0, y0, z0, x1, y1, z1, mt0c); 90 | 91 | end 92 | 93 | clear m r0 c0 r1 c1 C0 x0 y0 z0 mt0 x1 y1 z1 mt1 mt0c 94 | 95 | end 96 | 97 | % check to see if no overlap = all empty dtrans cells 98 | emptyCheck=cellfun(@isempty,dtrans); 99 | if ~any(~emptyCheck) 100 | fprintf('unable to align: no overlap \n'); 101 | return 102 | end 103 | 104 | % take average dtrans for each cluster 105 | for i=1:size(dtrans,1) 106 | dtrans{i,1} = nanmean([dtrans{i,:}],2); 107 | rmse{i,1} = nanmean([rmse{i,:}],2); 108 | end 109 | 110 | dtrans=dtrans(:,1); 111 | rmse=rmse(:,1); 112 | 113 | % reorganize dtrans and rmse for consistency with gcp reg files 114 | dtrans=dtrans'; 115 | dtrans = [dtrans{:}]; 116 | 117 | rmse=rmse'; 118 | rmse = [rmse{:}]; 119 | 120 | if ~any(~isnan(rmse)) 121 | fprintf('coregistration failure\n'); 122 | return 123 | end 124 | 125 | %% create output file 126 | 127 | outname=strrep(m0.Properties.Source,'_dem.mat','_reg_dem.mat'); 128 | 129 | clear m1 130 | m1 = matfile(outname,'Writable',true); 131 | 132 | fprintf('Setting x and y\n'); 133 | m1.x=m0.x; 134 | m1.y=m0.y; 135 | 136 | m1.dtrans = dtrans; 137 | m1.rmse = rmse; 138 | 139 | %% Regridding 140 | 141 | % get cluster array 142 | C=m0.C; 143 | 144 | % Apply registration to z grid variable 145 | 146 | fprintf('Regridding z\n'); 147 | z= nan(sz0); % initialize output 148 | for i=1:size(dtrans,2) 149 | 150 | if any(isnan(dtrans(:,i))); continue; end 151 | 152 | % make a mask of this cluster 153 | N = C == i+1; 154 | 155 | % apply registration 156 | ztemp = applyRegistration(dtrans(:,i),m0,N); 157 | clear N 158 | 159 | n=isnan(z) & ~isnan(ztemp); 160 | z(n) = ztemp(n); 161 | 162 | clear ztemp n 163 | 164 | end 165 | 166 | m1.z=z; 167 | 168 | clear z 169 | 170 | % Apply registration to mt grid variable 171 | 172 | fprintf('Regridding mt\n'); 173 | % cluster coregistraton loop 174 | mt = false(sz0); % initialize output 175 | for i=1:size(dtrans,2) 176 | 177 | if any(isnan(dtrans(:,i))); continue; end 178 | 179 | % make a mask of this cluster 180 | N = C == i+1; 181 | 182 | % Apply registration 183 | mttemp = applyRegistration(dtrans(:,i),m0,N,'mt'); 184 | 185 | clear N 186 | 187 | % add in any matches 188 | mt = mt | mttemp; 189 | 190 | clear mttemp 191 | 192 | end 193 | 194 | m1.mt=mt; 195 | 196 | clear mt 197 | 198 | %% Apply registration to or grid variable 199 | 200 | fprintf('Regridding or\n'); 201 | % cluster coregistraton loop 202 | or = zeros(sz0,'int16'); % initialize output 203 | for i=1:size(dtrans,2) 204 | 205 | if any(isnan(dtrans(:,i))); continue; end 206 | 207 | % make a mask of this cluster 208 | N = C == i+1; 209 | 210 | % Apply registration 211 | ortemp = applyRegistration(dtrans(:,i),m0,N,'or'); 212 | 213 | clear N 214 | 215 | n= or==0 & ortemp ~= 0; 216 | or(n) = ortemp(n); 217 | 218 | clear ortemp n 219 | 220 | end 221 | 222 | m1.or=or; 223 | 224 | clear or 225 | 226 | %% Apply registration to dy grid variable 227 | 228 | fprintf('Regridding dy\n'); 229 | % cluster coregistraton loop 230 | dy= zeros(sz0,'int16'); % initialize output 231 | for i=1:size(dtrans,2) 232 | 233 | if any(isnan(dtrans(:,i))); continue; end 234 | 235 | % make a mask of this cluster 236 | N = C == i+1; 237 | 238 | % Apply registration 239 | dytemp = applyRegistration(dtrans(:,i),m0,N,'dy'); 240 | 241 | clear N 242 | 243 | n= dy==0 & dytemp ~= 0; 244 | dy(n) = dytemp(n); 245 | 246 | clear dytemp n 247 | 248 | end 249 | 250 | m1.dy=dy; 251 | 252 | clear dy 253 | 254 | fprintf('Building metadata\n'); 255 | tileRegMeta(m1,{'Neighbor Align'}) 256 | 257 | outFlag=true; 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /DecimatePoly.m: -------------------------------------------------------------------------------- 1 | function [C_out,i_rem]=DecimatePoly(C,opt) 2 | % Reduce the complexity of a 2D simple (i.e. non-self intersecting), closed 3 | % piecewise linear contour by specifying boundary offset tolerance. 4 | % IMPORTANT: This function may not preserve the topology of the original 5 | % polygon. 6 | % 7 | % INPUT ARGUMENTS: 8 | % - C : N-by-2 array of polygon co-ordinates, such that the first, 9 | % C(1,:), and last, C(end,:), points are the same. 10 | % - opt : opt can be specified in one of two ways: 11 | % ----------------------APPROACH #1 (default) ----------------- 12 | % - opt : opt=[B_tol 1], where B_tol is the maximum acceptible 13 | % offset from the original boundary, B_tol must be 14 | % expressed in the same lenth units as the co-ords in 15 | % C. Default setting is B_tol=Emin/2, where Emin is the 16 | % length of the shortest edge. 17 | % ----------------------APPROACH #2---------------------------- 18 | % - opt : opt=[P_tol 2], where P_tol is the fraction of the 19 | % total number of polygon's vertices to be retained. 20 | % Accordingly, P_tol must be a real number on the 21 | % interval (0,1). 22 | % 23 | % OUTPUT: 24 | % - C_out : M-by-2 array of polygon coordinates. 25 | % - i_rem : N-by-1 logical array used to indicate which vertices were 26 | % removed during decimation. 27 | % 28 | % ALGORITHM: 29 | % 1) For every vertex compute the boundary offset error. 30 | % 2) Rank all vertics according to the error score from step 1. 31 | % 3) Remove the vertex with the lowest error. 32 | % 4) Recompute and accumulate the errors for the two neighbours adjacent to 33 | % the deleted vertex and go back to step 2. 34 | % 5) Repeat step 2 to 4 until no more vertices can be removed or the number 35 | % of vertices has reached the desired number. 36 | % 37 | % AUTHOR: Anton Semechko (a.semechko@gmail.com) 38 | % DATE: Jan.2011 39 | % 40 | 41 | % Check the input args 42 | if nargin<2, opt={}; end 43 | opt=CheckInputArgs(C,opt); 44 | 45 | N=size(C,1); 46 | i_rem=false(N,1); 47 | if N<=4, 48 | C_out=C; 49 | return 50 | end 51 | 52 | % Tolerance parameter, perimeter and area of the input polygon 53 | [Po,Emin]=PolyPerim(C); 54 | B_tol=Emin/2; 55 | Ao=PolyArea(C); 56 | No=N-1; 57 | 58 | if ~isempty(opt), B_tol=opt(1); end 59 | 60 | Nmin=3; 61 | if opt(2)==2 62 | Nmin=round((N-1)*opt(1)); 63 | if (N-1)==Nmin, return; end 64 | if Nmin<3, Nmin=3; end 65 | end 66 | 67 | % Remove the (repeating) end-point 68 | C(end,:)=[]; 69 | N=N-1; 70 | 71 | 72 | % Compute the distance offset errors -------------------------------------- 73 | D31=circshift(C,[-1 0])-circshift(C,[1 0]); 74 | D21=C-circshift(C,[1 0]); 75 | dE_new2=sum(D31.^2,2); % length^2 of potential new edges 76 | 77 | % Find the closest point to the current vertex on the new edge 78 | t=sum(D21.*D31,2)./dE_new2; 79 | if t<0, t(t<0)=0; end %#ok<*BDSCI> 80 | if t>1, t(t>1)=1; end 81 | V=circshift(C,[1 0])+bsxfun(@times,t,D31); 82 | 83 | % Evaluate the distance^2 84 | Err_D2=sum((V-C).^2,2); 85 | 86 | % Initialize distance error accumulation array 87 | DEAA=zeros(N,1); 88 | 89 | 90 | % Begin decimation -------------------------------------------------------- 91 | idx_ret=1:N; % keep track of retained vertices 92 | while true 93 | 94 | % Find the vertices whose removal will satisfy the decimation criterion 95 | idx_i=Err_D2Nmin && opt(2)==2 97 | B_tol=B_tol*sqrt(1.5); 98 | continue 99 | end 100 | 101 | idx_i=find(idx_i); 102 | if isempty(idx_i) || N==Nmin, break; end 103 | N=N-1; 104 | 105 | % Vertex with the smallest net error 106 | [~,i_min]=min(Err_D2(idx_i)); 107 | idx_i=idx_i(i_min); 108 | 109 | 110 | % Update the distance error accumulation array 111 | DEAA(idx_i)=DEAA(idx_i)+sqrt(Err_D2(idx_i)); 112 | 113 | i1=idx_i-1; if i1<1, i1=N; end 114 | i3=idx_i+1; if i3>N, i3=1; end 115 | 116 | DEAA(i1)=DEAA(idx_i); 117 | DEAA(i3)=DEAA(idx_i); 118 | 119 | % Recompute the errors for the vertices neighbouring the vertex marked 120 | % for deletion 121 | i1_1=i1-1; if i1_1<1, i1_1=N; end 122 | i1_3=i3; 123 | 124 | i3_1=i1; 125 | i3_3=i3+1; if i3_3>N, i3_3=1; end 126 | 127 | err_D1=RecomputeErrors(C([i1_1,i1,i1_3],:)); 128 | err_D3=RecomputeErrors(C([i3_1,i3,i3_3],:)); 129 | 130 | % Upadate the errors 131 | Err_D2(i1)=(sqrt(err_D1)+ DEAA(i1)).^2; 132 | Err_D2(i3)=(sqrt(err_D3)+ DEAA(i3)).^2; 133 | 134 | % Remove the vertex 135 | C(idx_i,:)=[]; 136 | idx_ret(idx_i)=[]; 137 | DEAA(idx_i)=[]; 138 | 139 | Err_D2(idx_i)=[]; 140 | 141 | end 142 | C=[C;C(1,:)]; C_out=C; 143 | 144 | i_rem(idx_ret)=true; 145 | i_rem=~i_rem; 146 | i_rem(end)=i_rem(1); 147 | 148 | % Perimeter and area of the simplified polygon 149 | P=PolyPerim(C); 150 | A=PolyArea(C); 151 | 152 | % Performance summary 153 | fprintf('\t\t# of verts\t\tperimeter\t\tarea\n') 154 | fprintf('in\t\t%-5u\t\t\t%-.2f\t\t\t%-.2f\n',No,Po,Ao) 155 | fprintf('out\t\t%-5u\t\t\t%-.2f\t\t\t%-.2f\n',N,P,A) 156 | fprintf('-----------------------------------------------------\n') 157 | fprintf('change\t%-5.2f%%\t\t\t%-5.2f%%\t\t\t%-5.2f%%\n\n',(N-No)/No*100,(P-Po)/Po*100,(A-Ao)/Ao*100) 158 | 159 | 160 | %========================================================================== 161 | function err_D2=RecomputeErrors(V) 162 | % Recompute the distance offset error for a small subset of polygonal 163 | % vertices. 164 | % 165 | % - V : 3-by-2 array of triangle vertices, where V(2,:) is the vertex 166 | % marked for removal. 167 | 168 | % Compute the distance offset error --------------------------------------- 169 | D31=V(3,:)-V(1,:); 170 | D21=V(2,:)-V(1,:); 171 | dE_new2=sum(D31.^2,2); % length^2 of potential new edge 172 | 173 | % Find the closest point to the current vertex on the new edge 174 | t=sum(D21.*D31,2)/dE_new2; 175 | if t<0, t(t<0)=0; end 176 | if t>1, t(t>1)=1; end 177 | p=V(1,:)+bsxfun(@times,t,D31); 178 | 179 | % Evaluate the distance^2 180 | err_D2=sum((p-V(2,:)).^2); 181 | 182 | 183 | %========================================================================== 184 | function [P,Emin]=PolyPerim(C) 185 | % Polygon perimeter. 186 | 187 | dE=C(2:end,:)-C(1:(end-1),:); 188 | dE=sqrt(sum(dE.^2,2)); 189 | P=sum(dE); 190 | Emin=min(dE); 191 | 192 | 193 | %========================================================================== 194 | function A=PolyArea(C) 195 | % Polygon area. 196 | 197 | dx=C(2:end,1)-C(1:(end-1),1); 198 | dy=C(2:end,2)+C(1:(end-1),2); 199 | A=abs(sum(dx.*dy)/2); 200 | 201 | 202 | %========================================================================== 203 | function opt=CheckInputArgs(C,opt) 204 | % Check the validity of the input arguments 205 | 206 | siz=size(C); 207 | if numel(siz)~=2 || siz(2)>siz(1) || ~isnumeric(C) || ~ismatrix(C) || ndims(C)~=2 208 | error('First input argument must be a N-by-2 array of polygon vertices') 209 | end 210 | 211 | if ~isequal(C(1,:),C(end,:)) 212 | error('First and last points in C must be the same') 213 | end 214 | 215 | if isempty(opt), return; end 216 | 217 | if ~isnumeric(opt) || numel(opt)~=2 218 | error('Incorrect entry for 2nd input argument') 219 | end 220 | 221 | if ~(opt(2)==1 || opt(2)==2) 222 | error('Incorrect entry for 2nd input argument. opt(2) must be set to 1 or 2.') 223 | end 224 | 225 | if opt(2)==1 && opt(1)<=eps 226 | error('Incorrect entry for 2nd input argument. When opt(2)==1, opt(1) must be greater than zero.') 227 | end 228 | 229 | if opt(2)==2 && (opt(1)<=eps || opt(1)>=1) 230 | error('Incorrect entry for 2nd input argument. When opt(2)==2, opt(1) must be on the interval (0,1).') 231 | end 232 | 233 | -------------------------------------------------------------------------------- /addStrip2Mosaic.m: -------------------------------------------------------------------------------- 1 | function [N,returnFlag] = addStrip2Mosaic(metaFile,m,dy0,N,dtrans,rmse,varargin) 2 | %addStrip2Meta merge strip into mosaic file 3 | % N = addStrip2Mosaic(metaFile,m,dy0,N,dtrans) adds the strip data 4 | % specified in the *_meta.txt file into the mat file handle m. N is the 5 | % mosaic data/no data byte mask. dtrans are [dz;dx;dy] registration 6 | % offsets to be applied to the strip before merging (where z1=z0+dz). If 7 | % dtrans=[0;0;0], no registration will be applied. If dtrans=[], the 8 | % strip will be corgistered to existing data in the mosaic if exists, or 9 | % inserted without registration if not. 10 | % 11 | % N = addStrip2Mosaic(...,'option',value) where options are: 12 | % 'mergeMethod' can be one of: 13 | % 'feather' Apply a linear weighting between the edges of 14 | % overlapping strips. DEFAULT 15 | % 16 | % 'warp' Force the edge of the new data to align with the edge 17 | % of the mosaic and throw away new data that overlaps. 18 | % 19 | % 'underprint' Only add new data where there is no existing data 20 | % 21 | % 'mask', mask where mask is an nx2 cell array of mask vertices 22 | % coordinates with columns x and y, and each row a polygon 23 | % 24 | % 'maxrmse', value specifies a maximum rmse, over which the results will 25 | % be rejected and nan's will be returned 26 | % 27 | % Subfunctions: readStripInTile, edgeFeather, edgeWarp, coregistedems, 28 | % interpolate2grid 29 | % 30 | % Ian Howat, Ohio State University 31 | % version 1.0; 28-Jul-2016 09:50:32 32 | % version 1.1; 06-Dec-2016 14:29:02 33 | % - changed to NaNs in dtrans triggers coregistration 34 | 35 | % Set Defaults 36 | mergeMethod='feather'; 37 | mask=cell(1,2); 38 | maxrmse=inf; 39 | minNewPixels=100; 40 | minOverlapPixels=100; 41 | returnFlag = false; 42 | 43 | % parse input args 44 | for i=1:2:length(varargin) 45 | 46 | switch lower(varargin{i}) 47 | 48 | case 'mergemethod' 49 | 50 | mergeMethod=varargin{i+1}; 51 | 52 | if ~strcmp(mergeMethod,'feather') && ~strcmp(mergeMethod,'warp') && ~strcmp(mergeMethod,'underprint') 53 | error('unrecognized merge method string') 54 | end 55 | 56 | case 'mask' 57 | 58 | mask=varargin{i+1}; 59 | 60 | if ~iscell(mask) || size(mask,2) ~= 2 61 | error('input variable "mask" must be a cell array with two columns (x,y)') 62 | end 63 | 64 | case 'maxrmse' 65 | 66 | maxrmse=varargin{i+1}; 67 | 68 | case 'minnewpixels' 69 | 70 | minNewPixels=varargin{i+1}; 71 | 72 | 73 | case 'minoverlappixels' 74 | 75 | minOverlapPixels=varargin{i+1}; 76 | 77 | otherwise 78 | 79 | error('Unknown input arguments') 80 | end 81 | end 82 | 83 | 84 | % read this strip data 85 | [x,y,z,mt,or,c,r] = readStripInTile(metaFile,m.x,m.y,'mask',mask); 86 | 87 | % check for competely masked file 88 | if isempty(x) 89 | m.dtrans=[m.dtrans,[NaN;NaN;NaN]]; 90 | m.rmse=[m.rmse,NaN]; 91 | returnFlag=true; 92 | return; 93 | end 94 | 95 | % convert mosaic subset area to index 96 | c=find(c); 97 | r=find(r); 98 | 99 | if length(c)*length(r) < minOverlapPixels 100 | fprintf('%d pixel overlap is too small, skipping\n',sum(c)*sum(r)); 101 | return; 102 | end 103 | 104 | %% Check for new coverage 105 | 106 | %subset count grid 107 | Nsub=N(r,c); 108 | 109 | %quit if too few new pixels added 110 | newPixels=sum(Nsub(:)==0 & ~isnan(z(:))); 111 | if newPixels < minNewPixels 112 | disp('redundant coverage, skipping'); 113 | m.dtrans=[m.dtrans,[NaN;NaN;NaN]]; 114 | m.rmse=[m.rmse,NaN]; 115 | returnFlag=true; 116 | return; 117 | end 118 | 119 | A= Nsub ~= 0 & ~isnan(z); % overlapping coverage mask 120 | 121 | 122 | %% Registration 123 | 124 | % if no dtrans given, but there's no overlap to coregister, then set 0 125 | if ~any(A(:)) && isempty(dtrans); dtrans=[0;0;0]; end 126 | 127 | % if dtrans is empty and there's overlap, then apply coregistration 128 | if isempty(dtrans) || any(isnan(dtrans)) 129 | 130 | if sum(A(:)) < minOverlapPixels 131 | fprintf('%d pixel overlap is too small, skipping\n',sum(A(:))); 132 | return; 133 | end 134 | 135 | % crop to new coverage 136 | co= find(sum(A) ~= 0,1,'first'):find(sum(A) ~= 0,1,'last'); 137 | ro= find(sum(A,2) ~= 0,1,'first'):find(sum(A,2) ~= 0,1,'last'); 138 | 139 | % co-register strip to mosaic subset 140 | [~,dtrans,rmse] = coregisterdems(... 141 | m.x(1,c(co)),m.y(r(ro),1),m.z(r(ro),c(co)),... 142 | x(co),y(ro),z(ro,co),m.mt(r(ro),c(co)),mt(ro,co)); 143 | 144 | % coregisterdems returns dtrans values as positive offsets, whereas 145 | % the gcp registration are negative, so need to reverse the sign: 146 | dtrans=-dtrans; 147 | 148 | % check for coregistration failure 149 | if isnan(rmse) || rmse > maxrmse 150 | 151 | disp('coregistration failure, skipping'); 152 | m.dtrans=[m.dtrans,[NaN;NaN;NaN]]; 153 | m.rmse=[m.rmse,NaN]; 154 | 155 | return; 156 | end; 157 | 158 | end 159 | 160 | % apply dtrans and interpolate back to grid (but only dtrans is not zeros) 161 | if any(dtrans ~= 0) 162 | 163 | x = x + dtrans(2); 164 | y = y + dtrans(3); 165 | z = z + dtrans(1); 166 | 167 | % interpolate the first dem to the same grid 168 | [z,mt,or] = interpolate2grid(x,y,z,mt,or,m.x(1,c),m.y(r,1)); 169 | 170 | end 171 | 172 | 173 | %% Data Merge 174 | 175 | % if no overlap, set to underprint 176 | if ~any(A(:)); mergeMethod = 'underprint'; end 177 | 178 | % make date grid 179 | dy=~isnan(z).*dy0; 180 | 181 | switch mergeMethod 182 | 183 | case 'underprint' 184 | 185 | n = find(Nsub == 0 & ~isnan(z)); 186 | 187 | zsub = m.z(r,c); 188 | zsub(n)=z(n); 189 | m.z(r,c)=zsub; 190 | clear z zsub 191 | 192 | orsub=m.or(r,c); 193 | orsub(n)=or(n); 194 | m.or(r,c) = orsub; 195 | clear or orsub 196 | 197 | dysub=m.dy(r,c); 198 | dysub(n)=dy0; 199 | m.dy(r,c) = dysub; 200 | clear dy dysub 201 | 202 | case 'feather' 203 | 204 | 205 | dx = m.x(1,2)-m.x(1,1); 206 | buff=10*dx+1; 207 | 208 | tic; 209 | W = edgeFeather(Nsub~=0,~isnan(z),'buffer',buff); 210 | toc; 211 | 212 | 213 | % make weighted elevation grid 214 | zsub=m.z(r,c); 215 | A= zsub.*W + z.*(1-W); 216 | n = isnan(zsub) & ~isnan(z); A(n)= z(n); 217 | n = ~isnan(zsub) & isnan(z); A(n)= zsub(n); 218 | 219 | % put strip subset back into full array 220 | m.z(r,c) = A; 221 | clear A z 222 | 223 | % make weighted ortho grid 224 | or = single(or); 225 | or(or ==0) = NaN; 226 | orsub=single(m.or(r,c)); 227 | orsub(orsub==0) = NaN; 228 | A= orsub.*W + or.*(1-W); 229 | 230 | A( isnan(orsub) & ~isnan(or))= or( isnan(orsub) & ~isnan(or)); 231 | A(~isnan(orsub) & isnan(or))= orsub(~isnan(orsub) & isnan(or)); 232 | A(isnan(A)) = 0; % convert back to uint16 233 | A = uint16(A); 234 | m.or(r,c) = A; 235 | clear A or 236 | 237 | % make weighted dy grid 238 | dy = single(dy); 239 | dy(dy==0) = NaN; 240 | dysub = single(m.dy(r,c)); 241 | dysub(dysub==0) = NaN; 242 | 243 | A= dysub.*W + dy.*(1-W); 244 | 245 | A( isnan(dysub) & ~isnan(dy))= dy ( isnan(dysub) & ~isnan(dy)); 246 | A(~isnan(dysub) & isnan(dy))= dysub(~isnan(dysub) & isnan(dy)); 247 | 248 | A(isnan(A)) = 0; % convert back to uint16 249 | A = uint16(A); 250 | m.dy(r,c) = A; 251 | clear A dy 252 | 253 | clear W 254 | 255 | case 'warp' 256 | 257 | % merge z by warping edge of z to match zsub 258 | [m.z(r,c),edgeWarpflag] = edgeWarp(m.z(r,c),z); 259 | if edgeWarpflag 260 | disp('redundant coverage, skipping'); 261 | m.dtrans=[m.dtrans,[NaN;NaN;NaN]]; 262 | m.rmse=[m.rmse,NaN]; 263 | returnFlag=true; 264 | return; 265 | end 266 | 267 | orsub=m.or(r,c); 268 | orsub(orsub == 0)=or(orsub == 0); 269 | m.or(r,c) = orsub; 270 | clear orsub 271 | 272 | dysub=m.dy(r,c); 273 | dysub(dysub == 0)=dy(dysub == 0); 274 | m.dy(r,c) = dysub; 275 | clear dysub 276 | 277 | end 278 | 279 | % for the matchtag, alwats just straight combination 280 | m.mt(r,c) = m.mt(r,c) | mt; 281 | clear mt 282 | 283 | % put coregistration data into file 284 | m.dtrans=[m.dtrans,dtrans(:)]; 285 | m.rmse=[m.rmse,rmse]; 286 | 287 | 288 | N(r,c)=~isnan(m.z(r,c)); 289 | returnFlag=true; 290 | 291 | -------------------------------------------------------------------------------- /scenes2strips.m: -------------------------------------------------------------------------------- 1 | function [X,Y,Z,M,O,trans,rmse,f]=scenes2strips(demdir,f) 2 | %SCENES2STRIPS merge scenes into strips 3 | % 4 | % [x,y,z,m,o,trans,rmse,f]=scenes2strips(demdir,f) merges the 5 | % scene geotiffs listed in cellstr f within directory demdir after 6 | % ordering them by position. If a break in coverage is detected between 7 | % scene n and n+1 only the first 1:n scenes will be merged. The data are 8 | % coregistered at overlaps using iterative least squares, starting with 9 | % scene n=1. 10 | % Outputs are the strip grid coorinates x,y and strip elevation, z, 11 | % matchtag, m and orthoimage, o. The 3D translations are given in 3xn 12 | % vector trans, along with root-mean-squared of residuals, rmse. The 13 | % output f gives the list of filenames in the mosaic. If a break is 14 | % detected, the list of output files will be less than the input. 15 | % 16 | % Version 3.0, Ian Howat, Ohio State University, 2015 17 | 18 | 19 | %% Order Scenes in north-south or east-west direction by aspect ratio 20 | fprintf('ordering %d scenes\n',length(f)) 21 | 22 | f = orderPairs(demdir,f); 23 | 24 | % intialize output stats 25 | trans=zeros(3,length(f)); 26 | rmse=zeros(1,length(f)); 27 | 28 | % file loop 29 | for i=1:length(f) 30 | 31 | % construct filenames 32 | demFile = [demdir,'/',f{i}]; 33 | matchFile= strrep(demFile,'dem.tif','matchtag.tif'); 34 | orthoFile= strrep(demFile,'dem.tif','ortho.tif'); 35 | %shadeFile= strrep(demFile,'dem.tif','dem_shade.tif'); 36 | edgeMaskFile= strrep(demFile,'dem.tif','edgemask.tif'); 37 | dataMaskFile= strrep(demFile,'dem.tif','datamask.tif'); 38 | 39 | fprintf('scene %d of %d: %s\n',i,length(f),demFile) 40 | 41 | try 42 | [x,y,z,o,m,me,md] = loaddata(demFile,matchFile,orthoFile,edgeMaskFile,... 43 | dataMaskFile); 44 | catch ME 45 | fprintf('%s %s: data read error, skipping \n',ME.identifier,ME.message); 46 | continue; 47 | end 48 | 49 | % check for no data 50 | if ~any(md(:)); fprintf('no data, skipping \n'); continue; end; 51 | 52 | %Apply Masks 53 | [x,y,z,o,m] = applyMasks(x,y,z,o,m,me,md); 54 | 55 | dx = x(2)-x(1); 56 | dy = y(2)-y(1); 57 | 58 | % check for non-integer grid 59 | if rem(x(1)./dx,1) ~= 0 || rem(y(1)./dy,1) ~= 0 60 | [x,y,z,m,o] = regrid(x,y,z,m,o); 61 | end 62 | 63 | % if first scene in strip, set as strip and continue to next scene 64 | if ~exist('X','var') 65 | X=x; clear x; 66 | Y=y; clear y; 67 | Z=z; clear z; 68 | M=m; clear m; 69 | O=o; clear o; 70 | 71 | continue; 72 | end 73 | 74 | % pad new arrays to stabilize interpolation 75 | buff=10*dx+1; 76 | z=padarray(z,[buff buff],NaN); 77 | m=padarray(m,[buff buff]); 78 | o=padarray(o,[buff buff]); 79 | x=[x(1)-dx.*(buff:-1:1),x,x(end)+dx.*(1:buff)]; 80 | y=[y(1)+dx.*(buff:-1:1),y,y(end)-dx.*(1:buff)]; 81 | 82 | % expand strip coverage to encompass new scene 83 | if x(1) < X(1); 84 | X1=x(1):dx:X(1)-dx; 85 | X=[X1,X]; 86 | Z=[nan(size(Z,1),length(X1)),Z]; 87 | M=[false(size(M,1),length(X1)),M]; 88 | O=[zeros(size(O,1),length(X1)),O]; 89 | clear X1 90 | end 91 | if x(end) > X(end); 92 | X1=X(end)+dx:dx:x(end); 93 | X=[X,X1]; 94 | Z=[Z,nan(size(Z,1),length(X1))]; 95 | M=[M,false(size(M,1),length(X1))]; 96 | O=[O,zeros(size(O,1),length(X1))]; 97 | clear X1 98 | end 99 | if y(1) > Y(1); 100 | Y1=y(1):-dx:Y(1)+dx; 101 | Y=[Y1,Y]; 102 | Z=[nan(length(Y1),size(Z,2));Z]; 103 | M=[false(length(Y1),size(M,2));M]; 104 | O=[zeros(length(Y1),size(O,2));O]; 105 | clear Y1 106 | end 107 | if y(end) < Y(end); 108 | Y1=Y(end)-dx:-dx:y(end); 109 | Y=[Y,Y1]; 110 | Z=[Z;nan(length(Y1),size(Z,2))]; 111 | M=[M;false(length(Y1),size(M,2))]; 112 | O=[O;zeros(length(Y1),size(O,2))]; 113 | clear Y1; 114 | end 115 | 116 | % map new dem pixels to swath. These must return integers. If not, 117 | % interpolation will be required, which is currently not supported. 118 | c0 = find(x(1) == X); 119 | c1 = find(x(end) == X); 120 | r0 = find(y(1) == Y); 121 | r1 = find(y(end) == Y); 122 | 123 | %crop to overlap 124 | Zsub=Z(r0:r1,c0:c1); 125 | Xsub=X(c0:c1); 126 | Ysub=Y(r0:r1); 127 | Msub=M(r0:r1,c0:c1); 128 | Osub=O(r0:r1,c0:c1); 129 | 130 | %% NEW MOSAICING CODE 131 | 132 | % crop to just region of overlap 133 | A= single( ~isnan(Zsub) & ~isnan(z)); 134 | 135 | %check for segment break 136 | if sum(A(:)) <= 1000; 137 | f=f(1:i-1); 138 | trans = trans(:,1:i-1); 139 | rmse = rmse(1:i-1); 140 | break 141 | end 142 | 143 | A(A==0)=NaN; 144 | 145 | [~,r,c] = cropnans(A,buff); 146 | 147 | %Make overlap mask removing isolated pixels 148 | A=single(bwareaopen(... 149 | isnan(Zsub(r(1):r(2),c(1):c(2))) & ~isnan(z(r(1):r(2),c(1):c(2))),... 150 | 1000)); % nodata in strip and data in scene is a one 151 | 152 | % check for redundant scene 153 | if sum(A(:)) <= 1000; fprintf('redundant scene, skipping \n'); continue; end; 154 | 155 | A(bwareaopen(... 156 | ~isnan(Zsub(r(1):r(2),c(1):c(2))) & isnan(z(r(1):r(2),c(1):c(2))),... 157 | 1000))=2; 158 | % data in strip and no data in scene is a two 159 | 160 | % tic 161 | % % USING REGIONFILL - Requires matlab 2015a or newer 162 | % A = regionfill(A,A==0) -1; 163 | % toc 164 | 165 | Ar=imresize(A,.1,'nearest'); 166 | 167 | [C R] = meshgrid(1:size(Ar,2),1:size(Ar,1)); 168 | 169 | % pixles on outside of boundary of overlap region 170 | B = bwboundaries(Ar~=0, 8, 'noholes'); 171 | B = cell2mat(B); 172 | 173 | n=sub2ind(size(Ar),B(:,1),B(:,2)); 174 | 175 | warning off 176 | F = scatteredInterpolant(C(n),R(n),double(Ar(n))); 177 | warning on 178 | 179 | Ar(Ar==0)=F(C(Ar==0),R(Ar==0)); 180 | 181 | Ar=imresize(Ar,size(A),'bilinear'); 182 | Ar(A==1 & Ar ~=1)=1; 183 | Ar(A==2 & Ar ~=2)=2; 184 | A=Ar-1; 185 | A(A < 0) = 0; 186 | A(A > 1) = 1; 187 | 188 | W=single(~isnan(Zsub)); 189 | W(r(1):r(2),c(1):c(2)) = A; clear A 190 | W(isnan(Zsub) & isnan(z)) = NaN; 191 | 192 | 193 | % shift weights so that more of the reference layer is kept 194 | f0=.25; % overlap fraction where ref z weight goes to zero 195 | f1=.55; % overlap fraction where ref z weight goes to one 196 | 197 | W=(1/(f1-f0)).*W-f0/(f1-f0); 198 | W(W > 1) = 1; 199 | W(W < 0) = 0; 200 | 201 | 202 | % remove <25% edge of coverage from each in pair 203 | Zsub(W == 0) = NaN; 204 | Msub(W == 0) = 0; 205 | Osub(W == 0) = 0; 206 | 207 | z(W >= 1) = NaN; 208 | m(W >= 1) = 0; 209 | o(W >= 1) = 0; 210 | 211 | %% Coregistration 212 | 213 | % coregister this scene to the strip mosaic 214 | [~,trans(:,i),rmse(i)] = ... 215 | coregisterdems(Xsub(c(1):c(2)),Ysub(r(1):r(2)),... 216 | Zsub(r(1):r(2),c(1):c(2)),... 217 | x(c(1):c(2)),y(r(1):r(2)),... 218 | z(r(1):r(2),c(1):c(2)),... 219 | Msub(r(1):r(2),c(1):c(2)),... 220 | m(r(1):r(2),c(1):c(2))); 221 | 222 | %check for segment break 223 | if isnan(rmse(i)); 224 | fprintf('Unable to coregister, breaking segment\n') 225 | f=f(1:i-1); 226 | trans = trans(:,1:i-1); 227 | rmse = rmse(1:i-1); 228 | break 229 | end 230 | 231 | % interpolation grid 232 | xi=x - trans(2,i); 233 | yi=y - trans(3,i); 234 | 235 | %check uniform spacing is maintained (sometimes rounding errors) 236 | if length(unique(diff(xi))) > 1; xi=round(xi,4); end 237 | if length(unique(diff(yi))) > 1; yi=round(yi,4); end 238 | 239 | % interpolate the floating data to the reference grid 240 | zi = interp2(xi,yi,z-trans(1,i),Xsub(:)',Ysub(:),'*linear'); 241 | clear z 242 | 243 | % interpolate the mask to the same grid 244 | mi = interp2(xi,yi,single(m),Xsub(:)',Ysub(:),'*nearest'); 245 | mi(isnan(mi)) = 0; % convert back to uint8 246 | mi = logical(mi); 247 | clear m 248 | 249 | % interpolate ortho to same grid 250 | oi = single(o); 251 | oi(oi==0) = NaN; % set border to NaN so wont be interpolated 252 | oi = interp2(xi,yi,oi,Xsub(:)',Ysub(:),'*cubic'); 253 | clear o 254 | 255 | clear Xsub Ysub 256 | 257 | % remove border 0's introduced by nn interpolation 258 | M3 = ~isnan(zi); 259 | M3=imerode(M3,ones( 6 )); % border cutline 260 | 261 | zi(~M3)=NaN; 262 | mi(~M3)=0; 263 | clear M3 264 | 265 | % remove border on orthos seperately 266 | M4= ~isnan(oi); 267 | M4=imerode(M4,ones( 6 )); 268 | oi(~M4)=NaN; 269 | clear M4 270 | 271 | % make weighted elevation grid 272 | A= Zsub.*W + zi.*(1-W); 273 | A( isnan(Zsub) & ~isnan(zi))= zi( isnan(Zsub) & ~isnan(zi)); 274 | A(~isnan(Zsub) & isnan(zi))=Zsub(~isnan(Zsub) & isnan(zi)); 275 | clear zi Zsub 276 | 277 | % put strip subset back into full array 278 | Z(r0:r1,c0:c1) = A; 279 | clear A 280 | 281 | % for the matchtag, just straight combination 282 | M(r0:r1,c0:c1) = Msub | mi; 283 | clear Msub Mi 284 | 285 | % make weighted ortho grid 286 | Osub = single(Osub); 287 | Osub(Osub==0) = NaN; 288 | A= Osub.*W + oi.*(1-W); 289 | 290 | clear W 291 | 292 | A( isnan(Osub) & ~isnan(oi))= oi( isnan(Osub) & ~isnan(oi)); 293 | A(~isnan(Osub) & isnan(oi))= Osub(~isnan(Osub) & isnan(oi)); 294 | clear Osub oi 295 | 296 | A(isnan(A)) = 0; % convert back to uint16 297 | A = uint16(A); 298 | 299 | O(r0:r1,c0:c1) = A; 300 | clear A 301 | 302 | 303 | end 304 | 305 | %crop to data 306 | if exist('Z','var') && any(~isnan(Z(:))) 307 | [Z,rcrop,ccrop] = cropnans(Z); 308 | if ~isempty(rcrop); 309 | X = X(ccrop(1):ccrop(2)); 310 | Y = Y(rcrop(1):rcrop(2)); 311 | M = M(rcrop(1):rcrop(2),ccrop(1):ccrop(2)); 312 | O = O(rcrop(1):rcrop(2),ccrop(1):ccrop(2)); 313 | Z(isnan(Z)) = -9999; 314 | end 315 | 316 | else 317 | X=[]; Y=[]; Z=[]; M=[]; O=[]; 318 | end 319 | 320 | function [x,y,z,o,m,me,md] = loaddata(demFile,matchFile,orthoFile,edgeMaskFile,dataMaskFile) 321 | % loaddata load data files and perform basic conversions 322 | 323 | d=readGeotiff(demFile); x=d.x; y=d.y; z=d.z; clear d 324 | sz=size(z); 325 | 326 | d=readGeotiff(matchFile); 327 | szd=size(d.z); 328 | if any(szd ~= sz); 329 | m = interp2(d.x,d.y,single(d.z),... 330 | x(:)',y(:),'*nearest'); 331 | m(isnan(m)) = 0; % convert back to uint16 332 | m = logical(m); 333 | else 334 | m=d.z; 335 | end 336 | clear d 337 | 338 | o=zeros(size(z)); 339 | if exist(orthoFile,'file') 340 | d=readGeotiff(orthoFile); 341 | szd=size(d.z); 342 | if any(szd ~= sz); 343 | d.z = single(d.z); 344 | d.z(d.z==0) = NaN; % set border to NaN so wont be interpolated 345 | o = interp2(d.x,d.y,d.z,... 346 | x(:)',y(:),'*cubic'); 347 | o(isnan(o)) = 0; % convert back to uint16 348 | o = uint16(o); 349 | else 350 | o=d.z; 351 | end 352 | clear d 353 | end 354 | 355 | 356 | if exist(edgeMaskFile,'file') 357 | d=readGeotiff(edgeMaskFile); 358 | szd=size(d.z); 359 | if any(szd ~= sz); error('edgemaskfile wrong dimensions'); end 360 | me=d.z; 361 | clear d; 362 | else 363 | warning('No edgeMaskFile found, no mask applied') 364 | me=ones(sz,'logical'); 365 | end 366 | 367 | if exist(dataMaskFile,'file') 368 | d=readGeotiff(dataMaskFile); 369 | szd=size(d.z); 370 | if any(szd ~= sz); error('datamaskfile wrong dimensions'); end 371 | md=d.z; 372 | else 373 | warning('No dataMaskFile found, no mask applied') 374 | md=ones(sz,'logical'); 375 | end 376 | 377 | z(z < -100 | z == 0 | z == -NaN | isinf(z) ) = NaN; 378 | 379 | 380 | function [x,y,z,o,m] = applyMasks(x,y,z,o,m,me,md) 381 | 382 | z(~md) = NaN; 383 | m(~md) = 0; 384 | o(~me) = 0; 385 | 386 | if any(~isnan(z(:))); 387 | [z,rcrop,ccrop] = cropnans(z); 388 | x = x(ccrop(1):ccrop(2)); 389 | y = y(rcrop(1):rcrop(2)); 390 | m = m(rcrop(1):rcrop(2),ccrop(1):ccrop(2)); 391 | o = o(rcrop(1):rcrop(2),ccrop(1):ccrop(2)); 392 | end 393 | 394 | function [X,Y,Z,M,O] = regrid(X,Y,Z,M,O) 395 | 396 | dx = X(2)-X(1); 397 | dy = Y(2)-Y(1); 398 | 399 | Xi = X(1) + (dx-rem(X(1)/dx,1)*dx):dx:X(end); 400 | Yi = Y(1) - (rem(Y(1)/dy,1)*dy):dy:Y(end); 401 | 402 | Zi = interp2(X,Y(:),Z,Xi,Yi(:),'*linear'); 403 | 404 | M = interp2(X,Y(:),single(M),Xi,Yi(:),'*nearest'); 405 | M(isnan(M)) = 0; % convert back to uint8 406 | M = logical(M); 407 | 408 | % interpolate ortho to same grid 409 | O = single(O); 410 | O(isnan(Z)) = NaN; % set border to NaN so wont be interpolated 411 | O = interp2(X,Y(:),O,Xi,Yi(:),'*cubic'); 412 | O(isnan(O)) = 0; % convert back to uint16 413 | O = uint16(O); 414 | 415 | Z = Zi; 416 | X = Xi; 417 | Y = Yi; 418 | 419 | 420 | function [A,r,c] = cropnans(varargin) 421 | % cropnans crop array of bordering nans 422 | % 423 | % [A,r,c] = cropnans(A) 424 | 425 | A=varargin{1}; 426 | buff=0; 427 | if nargin == 2; buff=varargin{2}; end 428 | 429 | r = []; 430 | c = []; 431 | 432 | 433 | M = ~isnan(A); 434 | 435 | if ~any(M(:)); return; end 436 | 437 | rowsum = sum(M) ~= 0; 438 | colsum = sum(M,2) ~= 0; 439 | 440 | c(1) = find(rowsum,1,'first')-buff; 441 | c(2) = find(rowsum,1,'last')+buff; 442 | 443 | r(1) = find(colsum,1,'first')-buff; 444 | r(2) = find(colsum,1,'last')+buff; 445 | 446 | if c(1) < 1; c(1)=1; end 447 | if r(1) < 1; r(1)=1; end 448 | if c(2) > size(A,2); c(2)=size(A,2); end 449 | if r(2) > size(A,1); r(2)=size(A,1); end 450 | 451 | A = A(r(1):r(2),c(1):c(2)); 452 | 453 | 454 | -------------------------------------------------------------------------------- /QCStripsByTile.m: -------------------------------------------------------------------------------- 1 | function QCStripsByTile(regionNum,varargin) 2 | 3 | %% Argins 4 | %regionNum='02'; % region number 5 | tilefile = 'PGC_Imagery_Mosaic_Tiles_Antarctica.mat'; %PGC/NGA Tile definition file, required 6 | arcdemfile= 'rema_tiles.mat'; % lists which tiles go to which regions, required 7 | dbasefile = 'rema_strips_8m_wqc_cs2bias.mat'; % database file 8 | changePath='/Users/ihowat'; %if set, will change the path to the REMA directory from what's in the database file. set to [] if none. 9 | 10 | % if an older set of mosaic files already exist, we can speed things up by 11 | % check to see if they already have 100% coverage - will skip if do. Leave 12 | % empty if none. 13 | tileDir= dir(['/data4/REMA/region_',regionNum,'*']); 14 | if ~isempty(tileDir) 15 | tileDir= ['/data4/REMA/',tileDir(1).name,'/mosaic_reg_qc_feather/40m/']; 16 | end 17 | 18 | %% defaults 19 | startfrom = 1; 20 | minN = 500; 21 | minArea = 500; 22 | 23 | % parse inputs 24 | for i=1:2:length(varargin) 25 | eval([varargin{i},'=',num2str(varargin{i+1}),';']); 26 | end 27 | 28 | %% Find tiles in this region and load meta 29 | 30 | % Get tile Defs 31 | tiles=load(tilefile); 32 | a=load(arcdemfile); 33 | 34 | %remove duplicated tile entries keeping just first occurence 35 | [~,n] = unique(a.tileName,'stable'); 36 | a = structfun(@(x) ( x(n) ), a, 'UniformOutput', false); 37 | 38 | % get index of this region number 39 | n=a.regionNum==str2num(regionNum); 40 | 41 | % check to make sure we find sum 42 | if ~(any(n)); error('no tiles matched for this region number'); end 43 | 44 | % match region number to overlapping tiles 45 | [~,n]=intersect(tiles.I,a.tileName(n)); 46 | 47 | % crop tile structure to overlapping tiles 48 | tiles = structfun(@(x) ( x(n) ), tiles, 'UniformOutput', false); 49 | 50 | % load database structure 51 | meta=load(dbasefile); 52 | 53 | % alter paths in database if set 54 | if ~isempty(changePath) 55 | meta.f = strrep(meta.f,'/data4',changePath); 56 | meta.region = strrep(meta.region,'/data4',changePath); 57 | end 58 | 59 | %check meta file for required fields 60 | if ~isfield(meta,'avg_rmse'); error('meta stucture missing avg_rmse field '); end 61 | if ~isfield(meta,'xmax'); error('meta stucture missing xmax field '); end 62 | if ~isfield(meta,'ymax'); error('meta stucture missing ymax field '); end 63 | if ~isfield(meta,'xmin'); error('meta stucture missing ymin field '); end 64 | if ~isfield(meta,'ymin'); error('meta stucture missing ymin field '); end 65 | if ~isfield(meta,'x'); error('meta stucture missing x field '); end 66 | if ~isfield(meta,'y'); error('meta stucture missing y field '); end 67 | if ~isfield(meta,'f'); error('meta stucture missing f field '); end 68 | 69 | % select the whichever registration has the better sigma_bias (all or 1 yr) 70 | if isfield(meta,'sigma_all') && isfield(meta,'sigma_1yr') && ~isfield(meta,'sigma') 71 | meta.sigma = nanmin([meta.sigma_all(:)';meta.sigma_1yr(:)'])'; 72 | end 73 | 74 | % if no ground control error field, just set to nan 75 | if ~isfield(meta,'sigma') 76 | meta.sigma = nan(size(meta.f)); 77 | end 78 | 79 | % if ground control error > 1, set to NaN 80 | meta.sigma(meta.sigma > 1) = NaN; 81 | 82 | % set 0 RMSE (single scenes) to NaN 83 | meta.avg_rmse(meta.avg_rmse == 0) = NaN; 84 | 85 | % tile loop 86 | for i=startfrom:length(tiles.I) 87 | fprintf(' \n') 88 | fprintf('Working tile %d of %d: %s \n',i,length(tiles.I),tiles.I{i}); 89 | tile = structfun(@(x) ( x(i) ), tiles, 'UniformOutput', false); 90 | 91 | % check existing tile for coverage 92 | if ~isempty(tileDir) 93 | if exist([tileDir,tile.I{1},'_40m_dem.mat'],'file') 94 | load([tileDir,tile.I{1},'_40m_dem.mat'],'N'); 95 | if sum(N(:))./numel(N) == 1 96 | fprintf('tile coverage complete, skipping\n') 97 | clear N 98 | continue 99 | end 100 | end 101 | end 102 | 103 | qctile(tile,meta,minN,minArea,changePath); 104 | 105 | end 106 | 107 | function qctile(tiles,meta,minN,minArea,changePath) 108 | 109 | %% Spatial coverage search 110 | 111 | % make tile boundary polygon 112 | 113 | tilevx = [tiles.x0;tiles.x0;tiles.x1;tiles.x1;tiles.x0]; 114 | tilevy = [tiles.y0;tiles.y1;tiles.y1;tiles.y0;tiles.y0]; 115 | 116 | % quick search: find strips within range of this tile. This does not 117 | % account for background area of around strips but just pairs them down to 118 | % speed the poly intersection loop 119 | 120 | n = meta.xmax > tiles.x0 & meta.xmin < tiles.x1 & ... 121 | meta.ymax > tiles.y0 & meta.ymin < tiles.y1; 122 | 123 | if ~any(n); fprintf('no strip overlap\n'); return; end 124 | 125 | % get polys 126 | meta = structfun(@(x) ( x(n,:) ), meta, 'UniformOutput', false); 127 | 128 | % search for all strips overlapping this tile 129 | in=zeros(size(meta.f)); 130 | 131 | for i=1:length(in) 132 | in(i) = any(inpolygon(meta.x{i},meta.y{i},tilevx,tilevy)) | ... 133 | any(inpolygon(tilevx,tilevy,meta.x{i},meta.y{i})); 134 | end 135 | 136 | if ~any(in); fprintf('no strip overlap\n'); return; end 137 | 138 | % crop meta data struct to only overlapping strips 139 | meta = structfun(@(x) ( x(logical(in),:) ), meta, 'UniformOutput', false); 140 | 141 | fprintf('%d files overlapping this tile, ',sum(in)); 142 | 143 | % add existing qc data 144 | meta = addQC2Meta(meta,changePath); 145 | 146 | fprintf('%d files with existing qc\n',sum(meta.qc ~= 0)); 147 | 148 | % remove already added entries from metadata 149 | if any(meta.qc == 5) 150 | fprintf('removing %d files with qc flag=5\n',sum(meta.qc == 5)); 151 | meta = structfun(@(x) ( x(meta.qc ~= 5 ,:) ), meta, 'UniformOutput', false); 152 | if isempty(meta.f); fprintf('all strips removed, returning \n'); return; end 153 | end 154 | 155 | % build grid 156 | res = 40; 157 | buff = 0; 158 | 159 | x = tiles.x0-buff*res: res:tiles.x1+buff*res; 160 | y = tiles.y1+buff*res:-res:tiles.y0-buff*res; 161 | y = y(:); 162 | 163 | N= zeros(length(y),length(x),'uint8'); 164 | 165 | % apply coastline 166 | if isfield(tiles,'coastline') 167 | fprintf('applying coastline, '); 168 | 169 | A = false(size(N)); 170 | i=1; 171 | for i=1:length(tiles.coastline{1}) 172 | if isempty(tiles.coastline{1}{i}); continue; end 173 | A(roipoly(x,y,N,tiles.coastline{1}{i}(1,:),... 174 | tiles.coastline{1}{i}(2,:))) = true; 175 | end 176 | 177 | percent_filled = 100*sum(~A(:))./numel(A); 178 | 179 | if percent_filled > 0.2 180 | N(~A) = 1; 181 | else 182 | percent_filled =0; 183 | end 184 | clear A 185 | 186 | fprintf('%.2f%% filled as water\n',percent_filled); 187 | 188 | if percent_filled == 100; fprintf('returning \n'); return; end 189 | end 190 | 191 | %% Build Grid Point Index Field 192 | % make a cell for each file containing the col-wise indices of the data 193 | % points on the tile grid. This is used search for new or existing data on 194 | % the grid within each search iteration. 195 | 196 | fprintf('calculating tile pixel coverage for each strip, '); 197 | 198 | % initialize output field 199 | meta.gridPointInd=cell(size(meta.f)); 200 | 201 | % can be slow, so we'll use a counter 202 | count = 0; 203 | 204 | % already qc'd files to add to N 205 | add2N = meta.qc > 0 & meta.qc < 4; 206 | 207 | fprintf('%d already qc-passed files will be added \n',sum(add2N)) 208 | 209 | if ~any(~add2N); fprintf('all files already passed qc, returning \n'); return; end 210 | 211 | % file loop 212 | for i=1:length(meta.f) 213 | 214 | % counter 215 | if i>1 for p=1:count fprintf('\b'); end; %delete line before 216 | count = fprintf('strip %d of %d',i,size(meta.f,1)); 217 | end 218 | 219 | % locate grid pixels within footprint polygon 220 | BW = roipoly(x, y, N, meta.x{i}, meta.y{i}); 221 | 222 | %if mask data exists, apply it 223 | if meta.qc(i) == 3 224 | j=1; 225 | for j=1:length(meta.maskPolyx{i}) 226 | BW(roipoly(x,y,BW,... 227 | meta.maskPolyx{i}{j},meta.maskPolyy{i}{j}))=0; 228 | end 229 | end 230 | 231 | % add if already qc'd 232 | if add2N(i); N(BW) = 1; continue; end 233 | 234 | % convert BW mask to col-wise indices and save to cell 235 | meta.gridPointInd{i}=find(BW); 236 | 237 | % get rid of mask 238 | clear BW 239 | end 240 | 241 | % clear counter line 242 | for p=1:count fprintf('\b'); end; 243 | 244 | % check if filled 245 | percent_filled = 100*sum(N(:))./numel(N); 246 | fprintf('%.2f%% filled\n',percent_filled); 247 | if percent_filled == 100; fprintf('returning \n'); return; end 248 | 249 | % remove already added entries from metadata 250 | meta = structfun(@(x) ( x(~add2N ,:) ), meta, 'UniformOutput', false); 251 | 252 | % make another field with the number of data points 253 | meta.gridPointN=cellfun(@length,meta.gridPointInd); 254 | 255 | 256 | %remove strips below a minimum size 257 | stripArea = nan(size(meta.f)); 258 | for i=1:length(meta.f) 259 | stripArea(i) = polyarea(meta.x{i}, meta.y{i})./1000^2; 260 | end 261 | 262 | fprintf('removing %d strips smaller than %.1f km^2\n',sum(stripArea < minArea),minArea); 263 | meta = structfun(@(x) ( x(stripArea >= minArea ,:) ), meta, 'UniformOutput', false); 264 | 265 | %% coverage test loop 266 | if ~isfield(meta,'rmse'); meta.rmse=nan(size(meta.f));end 267 | if ~isfield(meta,'dtrans'); meta.dtrans=zeros(length(meta.f),3); end 268 | if ~isfield(meta,'overlap'); meta.overlap=zeros(size(meta.f)); end % number existing data points overlapping file 269 | %% 270 | recountFlag=true; 271 | skipn = 0; 272 | while length(meta.f) >= 1 273 | 274 | percent_filled = 100*sum(N(:))./numel(N); 275 | 276 | if percent_filled == 100; fprintf('100%% filled returning \n',percent_filled); return; end 277 | 278 | fprintf('%.2f%% filled\n', percent_filled); 279 | 280 | % loop through files in boundary and find # of new/existing points 281 | if recountFlag 282 | for i=1:length(meta.f) 283 | 284 | % subset of N at data points in this file 285 | Nsub=N(meta.gridPointInd{i}); 286 | 287 | % count existing data points 288 | meta.overlap(i)=sum(Nsub); 289 | 290 | % count new data points 291 | meta.gridPointN(i) = sum(~Nsub); 292 | 293 | end 294 | end 295 | 296 | recountFlag=false; 297 | 298 | % remove rendundant files (with already 100% coverage) 299 | redundantFlag = meta.gridPointN < minN; 300 | 301 | if any(redundantFlag) 302 | 303 | % remove redundant files from lists 304 | meta = structfun(@(x) ( x(~redundantFlag,:) ), meta, 'UniformOutput', false); 305 | 306 | fprintf('%d redundant files (N < %d) removed\n',sum(redundantFlag),minN); 307 | 308 | if isempty(meta.f); fprintf('all strips removed, returning \n'); return; end 309 | end 310 | 311 | A = nansum([100.*meta.gridPointN./numel(N),1./(meta.avg_rmse.^2)],2); 312 | 313 | [~,n]=sort(A,'descend'); 314 | 315 | 316 | if skipn < 0; skipn = length(n)-1; end 317 | 318 | % skip if skipped on last iteration 319 | if length(n) >= 1+skipn 320 | n = n(1+skipn); 321 | else 322 | n=n(1); 323 | skipn = 0; 324 | end 325 | 326 | fprintf('%d of %d strips remaining\n',skipn+1,length(meta.f)); 327 | 328 | fileName=strrep(meta.f{n},'meta.txt','dem_browse.tif'); 329 | 330 | 331 | fprintf('%s\n',fileName); 332 | fprintf('%d new pointsm, gcp sigma=%.2f, mean coreg RMSE=%.2f, max coreg RMSE=%.2f \n',... 333 | meta.gridPointN(n),meta.sigma(n),meta.avg_rmse(n), meta.max_rmse(n)); 334 | 335 | % localfileName = strrep(fileName,'/data4/REMA','~/rema8mStripBrowse'); 336 | localfileName = fileName; 337 | 338 | I=readGeotiff(localfileName); 339 | 340 | Ni=interp2(x,y(:),N,I.x,I.y(:),'*nearest'); 341 | 342 | imagesc(I.x,I.y,I.z,'alphadata',single(I.z ~= 0)) 343 | set(gca,'color','r') 344 | axis xy equal tight; 345 | colormap gray; 346 | hold on; 347 | imagesc(I.x,I.y,Ni,'alphadata',single(Ni).*.5) 348 | 349 | 350 | if isfield(tiles,'coastline') 351 | i=1; 352 | for i=1:length(tiles.coastline{1}) 353 | if isempty(tiles.coastline{1}{i}); continue; end 354 | plot(tiles.coastline{1}{i}(1,:),... 355 | tiles.coastline{1}{i}(2,:),'b','linewidth',1) 356 | end 357 | end 358 | 359 | plot([tiles.x0,tiles.x0,tiles.x1,tiles.x1,tiles.x0], [tiles.y0,tiles.y1,tiles.y1,tiles.y0,tiles.y0],'w','linewidth',2) 360 | 361 | set(gca,'xlim',[min(I.x)-500 max(I.x)+500],'ylim',[min(I.y)-500 max(I.y)+500]); 362 | 363 | 364 | %set(gcf,'units','normalized'); 365 | %set(gcf,'position',[0.01,0.01,.35,.9]) 366 | 367 | 368 | qc=load([fileparts(fileName),'/qc.mat']); 369 | 370 | fileNames = qc.fileNames; 371 | 372 | % alter paths in database if set 373 | if ~isempty(changePath) 374 | fileNames = strrep(fileNames,'/data4',changePath); 375 | end 376 | 377 | [~,IA]=intersect(fileNames, fileName); 378 | 379 | if isempty(IA); error('this file name not matched in the qc.mat, probably need to upadate it.'); end 380 | 381 | if qc.flag(IA) ~= 4 && qc.flag(IA) ~= 0 382 | 383 | fprintf('flag previoulsy changed to %d, applying\n',qc.flag(IA)) 384 | 385 | else 386 | 387 | j=1; 388 | while j 389 | 390 | try 391 | 392 | flag=input('Enter quality flag: 0=skip,9=back, 1=good, 2=partial, 3=manual edit, 4=poor, 5=unuseable, 6=quit\n'); 393 | 394 | 395 | if ~isempty(flag) 396 | if isnumeric(flag) 397 | if flag == 0 || flag == 9 || flag == 1 || flag == 2 || flag == 3 || flag == 4 || flag == 5 || flag == 6 398 | break; 399 | end 400 | end 401 | end 402 | catch 403 | fprintf('%d not recogized, try again\n',flag); 404 | end 405 | 406 | if iscell(flag); flag=flag{1}; end 407 | 408 | fprintf('%d not recogized, try again\n',flag); 409 | 410 | end 411 | 412 | 413 | if flag == 6; clf; return; end 414 | 415 | 416 | if flag == 0; skipn = skipn+1; clf; continue; end 417 | 418 | if flag == 9; skipn = skipn-1; clf; continue; end 419 | 420 | 421 | qc.flag(IA)=flag; 422 | 423 | if flag == 1 || flag == 2 424 | qc.x{IA}=cell(1); qc.y{IA}=cell(1); 425 | 426 | end 427 | 428 | if flag == 3 429 | fprintf('entering manual edit mode\n') 430 | 431 | j=1; 432 | while j 433 | 434 | [~,qc.x{IA}{j},qc.y{IA}{j}] = roipoly; 435 | 436 | plot(qc.x{IA}{j},qc.y{IA}{j},'g','linewidth',2) 437 | 438 | 439 | while j 440 | s=input('continue editing this image? (y/n)\n','s'); 441 | 442 | if ~strcmpi(s,'y') && ~strcmpi(s,'n') 443 | fprintf('%s not recogized, try again\n',s); 444 | else 445 | break 446 | end 447 | end 448 | 449 | if strcmpi(s,'n'); break; end 450 | 451 | j=j+1; 452 | end 453 | end 454 | 455 | save([fileparts(fileName),'/qc.mat'],'-struct','qc'); 456 | 457 | end 458 | 459 | 460 | clf 461 | 462 | if qc.flag(IA) > 0 && qc.flag(IA) < 4 463 | 464 | M = I.z ~=0; 465 | 466 | j=1; 467 | for j=1:length(qc.x{IA}) 468 | M(roipoly(I.x,I.y,M,qc.x{IA}{j},qc.y{IA}{j}))=0; 469 | end 470 | 471 | M=interp2(I.x,I.y(:),single(M),x,y(:),'*nearest'); 472 | 473 | N(M == 1) = 1; 474 | 475 | recountFlag=true; 476 | 477 | % remove this file from the meta struct 478 | in=1:length(meta.f); in(n)=[]; 479 | meta = structfun(@(x) ( x(in,:) ), meta, 'UniformOutput', false); 480 | skipn = 0; 481 | 482 | elseif qc.flag(IA) == 4 || qc.flag(IA) == 5 483 | 484 | % remove this file from the meta struct 485 | in=1:length(meta.f); in(n)=[]; 486 | meta = structfun(@(x) ( x(in,:) ), meta, 'UniformOutput', false); 487 | 488 | 489 | end 490 | 491 | end 492 | 493 | 494 | % -------------------------------------------------------------------------------- /strips2tile.m: -------------------------------------------------------------------------------- 1 | function strips2tile(meta,tilex0, tilex1, tiley0, tiley1,res,outname,varargin) 2 | % STRIPS2TILE mosaic strips to rectangular tile 3 | % 4 | %[x,y,z,mt,or,dy,f,dtrans,rmse] = ... 5 | % strips2tile(meta,tilex0, tilex1, tiley0, tiley1,res,outname) 6 | % 7 | % where meta is the database structure, tilex and tiley are the coordinate 8 | % ranges of the tile, res is the tile resolution in m and outname is the 9 | % tile output file name. 10 | % 11 | %[...] = strips2tile(...,'options') where the following options are 12 | %availble: 13 | % 'disableReg' disables use of any existing GCP registration 14 | % files/data 15 | % 'disableCoregTest' disables the coregistraiton optimization test 16 | % to save time 17 | % 'mergeMethod','methodstring', specifies the mergeing method as 18 | % 'feather' (default), 'underprint' or 'warp'. 19 | % 20 | % subfunctions: readreg, addStrip2Mosaic 21 | % 22 | % Ian Howat, Ohio State University 23 | % Version 1.0; 28-Jul-2016 09:50:32 (beta versions preceeded this) 24 | % Version 2.0; 8-Oct-2016 15:19:52 25 | % - preferentially stacks in order of best coregistration fit (if 26 | % disabled, sorts by quality rank and then new data coverage) 27 | % - added redundant data check & skip 28 | % - faster existing data search using gridPointInd field 29 | % - allows for use of a qc rank list 30 | % Version 2.1; 06-Dec-2016 14:22:22 31 | % - fixed bug in which disableCoregTest resuted in no coregistration in 32 | % addStrip2Mosiac 33 | % Version 2.2: 01-Feb-2017 17:00:00 34 | % - added dtrans limit to coregisterdems.m 35 | 36 | tileVersion='2.2'; 37 | 38 | %% Set Parameters & Parse args 39 | % set Y2K as day 0 for the daynumber grid 40 | dy0 = datenum('jan 1 2000'); 41 | 42 | % option to disable using control registration files 43 | disableReg=false; 44 | 45 | % option to disable coregistration optimization test 46 | disableCoregTest=false; 47 | 48 | % default merging method 49 | mergeMethod='feather'; 50 | 51 | % test varargin for flags 52 | if ~isempty(varargin) 53 | if any(strcmpi('disableReg',varargin)) 54 | fprintf('GCP registration disabled\n') 55 | disableReg=true; 56 | end 57 | 58 | if any(strcmpi('disableCoregTest',varargin)) 59 | fprintf('Coregistration testing disabled\n') 60 | disableCoregTest=true; 61 | end 62 | 63 | 64 | if any(strcmpi('mergeMethod',varargin)) 65 | n=find(strcmpi('mergeMethod',varargin)); 66 | mergeMethod=varargin{n+1}; 67 | end 68 | 69 | end 70 | 71 | fprintf('Using merge method: %s\n',mergeMethod) 72 | 73 | %% Initialize new mosaick file if not exists 74 | if ~exist(outname,'file') 75 | 76 | % intialize outputs 77 | x=[]; y=[]; z=[]; mt=[]; or=[]; dy=[]; f=[]; dtrans=[]; rmse=[]; 78 | 79 | % make tile boundary polygon 80 | tilevx = [tilex0;tilex0;tilex1;tilex1;tilex0]; 81 | tilevy = [tiley0;tiley1;tiley1;tiley0;tiley0]; 82 | 83 | %% Search for strips within this tile 84 | 85 | % quick search: find strips within range of this tile. This does not 86 | % account for background area of around strips but just pairs them down to 87 | % speed the poly intersection loop 88 | n = meta.xmax > tilex0 & meta.xmin < tilex1 & ... 89 | meta.ymax > tiley0 & meta.ymin < tiley1; 90 | 91 | % if no overlap, return 92 | if ~any(n); fprintf('no strip overlap\n'); return; end 93 | 94 | % par down database structure to possible overlapping tiles 95 | meta = structfun(@(x) ( x(n) ), meta, 'UniformOutput', false); 96 | 97 | % search for all strip footprints overlapping this tile 98 | n=zeros(size(meta.f)); 99 | for i=1:length(n) 100 | n(i) = any(inpolygon(meta.x{i},meta.y{i},tilevx,tilevy)) | ... 101 | any(inpolygon(tilevx,tilevy,meta.x{i},meta.y{i})); 102 | end 103 | 104 | % if no overlap, return 105 | if ~any(n); fprintf('no strip overlap'); return; end 106 | 107 | % remove files with no overlap 108 | meta = structfun(@(x) ( x(logical(n)) ), meta, 'UniformOutput', false); 109 | 110 | %% Mosaic Grid Definition and Initialization 111 | 112 | % define mosaic coorinate grid. Add a 100-pixel buffer for aligning/merging 113 | % the tiles 114 | x = tilex0-100*res: res:tilex1+100*res; 115 | y = tiley1+100*res:-res:tiley0-100*res; 116 | y = y(:); 117 | 118 | % make output file 119 | save(outname,'x','y','-v7.3'); 120 | m = matfile(outname,'Writable',true); 121 | 122 | % initialize mosaic grids 123 | m.z = nan(length(y),length(x),'single'); % elevation data grid 124 | m.or =zeros(length(y),length(x),'int16'); % ortho imagery grid 125 | m.mt = zeros(length(y),length(x),'uint8'); % matchtag data grid 126 | m.dy = zeros(length(y),length(x),'int16'); % strip index grid 127 | C = zeros(length(y),length(x),'int16'); % coregistration cluster grid 128 | m.C=C; 129 | N= zeros(length(y),length(x),'uint8'); % pixel data count 130 | m.N=N; 131 | 132 | % initialize mosaic meta data variables 133 | m.f= []; m.overlap=[]; m.rmse=[]; m.dtrans=[]; 134 | 135 | % initialize cluster counter 136 | c=1; 137 | 138 | %% Attempt to restart if output file exists 139 | else %file already exists so try and pick up where it left off 140 | % WARNING not tested - may not pick up where you want it to 141 | fprintf('reading existing file and trying to pick up where it left off\n') 142 | m = matfile(outname,'Writable',true); 143 | 144 | if length(m.rmse) - length(m.f) == 1 145 | rmse=m.rmse; rmse(end) = []; m.rmse=rmse; rmse = []; 146 | dtrans=m.dtrans; dtrans(:,end) = []; m.dtrans=dtrans; dtrans = []; 147 | elseif length(m.rmse) - length(m.f) > 1 148 | error('variable sizes in the existing mosaic file not reconcileable') 149 | end 150 | 151 | % remove already added files 152 | [~,IA]= intersect(meta.f,m.f); 153 | in=1:length(meta.f); in(IA)=[]; 154 | meta = structfun(@(x) ( x(in,:) ), meta, 'UniformOutput', false); 155 | 156 | x=m.x; 157 | y=m.y; 158 | N=m.N; 159 | C=m.C; 160 | c=max(C(:)); 161 | end 162 | 163 | %% Compile Registration Data 164 | if ~disableReg % check for disable registration argument 165 | regfiles=strrep(meta.f,'meta.txt','reg.txt'); 166 | nreg= find( cellfun( @exist, regfiles) == 2); 167 | 168 | meta.dtrans=nan(size(meta.f,1),3); 169 | meta.rmse=nan(size(meta.f)); 170 | 171 | if ~isempty(nreg) 172 | regfiles=regfiles(nreg); 173 | 174 | [~,~,~,dtrans,pctile]=cellfun( @readreg,regfiles,'uniformoutput',0); 175 | pctile=cell2mat(pctile); 176 | dtrans=cell2mat(dtrans); 177 | p75=pctile(:,6); 178 | 179 | meta.dtrans(nreg,:)=dtrans; 180 | meta.rmse(nreg)=p75; 181 | end 182 | 183 | %filter out data over 4m std 184 | meta.dtrans(meta.rmse > 4,:) = NaN; 185 | meta.rmse(meta.rmse > 4) = NaN; 186 | 187 | % sort database by p75 188 | [~,n] = sort(meta.rmse,'ascend'); 189 | meta = structfun(@(x) ( x(n,:) ), meta, 'UniformOutput', false); 190 | 191 | nreg=find(~isnan(meta.rmse)); 192 | 193 | %% Add Registered Strips as Anchors 194 | if ~isempty(nreg) 195 | 196 | for i=1:length(nreg) 197 | 198 | m.overlap=[m.overlap,0]; 199 | 200 | fprintf('adding anchor strip %d of %d: %s\n',i,length(nreg),meta.f{i}) 201 | if isfield(meta,'maskPolyx') 202 | mask=[meta.maskPolyx{i}(:) meta.maskPolyy{i}(:)]; 203 | else 204 | mask=cell(1,2); 205 | end 206 | N = addStrip2Mosaic( meta.f{i},m,meta.stripDate(i)-dy0,N,... 207 | meta.dtrans(i,:)',meta.rmse(i),... 208 | 'mergeMethod','underprint',... 209 | 'mask',mask); 210 | 211 | % add this file name 212 | m.f=[m.f,meta.f(i)]; 213 | m.N = N; 214 | 215 | end 216 | 217 | % remove these files from the meta struct 218 | in=length(nreg)+1:length(meta.f); 219 | meta = structfun(@(x) ( x(in,:) ), meta, 'UniformOutput', false); 220 | 221 | C(N ~= 0)=int16(1); 222 | m.C = C; 223 | 224 | end 225 | 226 | end 227 | 228 | %% Add dummy qc field if doesnt exist 229 | if ~isfield(meta,'qc'); meta.qc = ones(size(meta.f)); end; 230 | 231 | %% Build Grid Point Index Field 232 | % make a cell for each file containing the col-wise indices of the data 233 | % points on the tile grid. This is used search for new or existing data on 234 | % the grid within each search iteration. 235 | 236 | % first make sure does not exist in case we're saving this info 237 | if ~isfield(meta,'gridPointInd') 238 | 239 | fprintf('calculating tile pixel coverage for each strip\n'); 240 | 241 | % initialize output field 242 | meta.gridPointInd=cell(size(meta.f)); 243 | 244 | % can be slow, so we'll use a counter 245 | count = 0; 246 | 247 | % file loop 248 | for i=1:length(meta.f) 249 | 250 | % counter 251 | if i>1 for p=1:count fprintf('\b'); end; %delete line before 252 | count = fprintf('strip %d of %d',i,size(meta.f,1)); 253 | end 254 | 255 | % locate grid pixels within footprint polygon 256 | BW = roipoly(x, y, N, meta.x{i}, meta.y{i}); 257 | 258 | % if mask exists, apply it 259 | if isfield(meta,'maskPolyx') 260 | mask=[meta.maskPolyx{i}(:) meta.maskPolyy{i}(:)]; 261 | 262 | for j=1:size(mask,1) 263 | if ~isempty(mask{j,1}) && ~isempty(mask{j,2}) 264 | BW(roipoly(x,y,N,mask{j,1},mask{j,2}))=0; 265 | end 266 | end 267 | 268 | end 269 | 270 | % convert BW mask to col-wise indices and save to cell 271 | meta.gridPointInd{i}=find(BW); 272 | 273 | % get rid of mask 274 | clear BW 275 | end 276 | 277 | % clear counter line 278 | for p=1:count fprintf('\b'); end; 279 | 280 | % make another field with the number of data points 281 | meta.gridPointN=cellfun(@length,meta.gridPointInd); 282 | 283 | end 284 | 285 | %% Sequential Floating Strip Addition Loop 286 | % Sequentially add strips to the mosaic by selecting the file with the most 287 | % new data coverage, with enough overlap to coregister to exisiting data. 288 | 289 | if ~isfield(meta,'rmse'); meta.rmse=nan(size(meta.f));end 290 | if ~isfield(meta,'dtrans'); meta.dtrans=nan(length(meta.f),3); end 291 | if ~isfield(meta,'overlap'); meta.overlap=zeros(size(meta.f)); end % number existing data points overlapping file 292 | 293 | while length(meta.f) >= 1 294 | 295 | fprintf('%d strips remaining\n',length(meta.f)); 296 | 297 | % loop through files and record number of nan and non-nan mosaic grid 298 | % points overlap each strip. 299 | 300 | % only find overlapping data if any data exists in the tile 301 | meta.coregTestFlag=false(size(meta.f)); % flag which files have updated overlap to retest coregistration 302 | if any(N(:)) 303 | 304 | % subset the DEMs to those that just overlap the rectangle of 305 | % existing data coverage 306 | anyN=any(N); 307 | minNx = x(find(anyN,1,'first')); 308 | maxNx = x(find(anyN,1,'last')); 309 | anyN=any(N'); 310 | maxNy=y(find(anyN,1,'first')); 311 | minNy =y(find(anyN,1,'last')); 312 | clear anyN 313 | 314 | % find files within boundary 315 | n = find(meta.xmax > minNx & meta.xmin < maxNx & ... 316 | meta.ymax > minNy & meta.ymin < maxNy); 317 | 318 | % loop through files in boundary and find # of new/existing points 319 | overlap=meta.overlap; 320 | for i=1:length(n) 321 | 322 | % subset of N at data points in this file 323 | Nsub=N(meta.gridPointInd{n(i)}); 324 | 325 | % count existing data points 326 | overlap(n(i))=sum(Nsub); 327 | 328 | % count new data points 329 | meta.gridPointN(n(i)) = sum(~Nsub); 330 | 331 | end 332 | 333 | % flag which files have updated overlap to retest coregistration 334 | meta.coregTestFlag = meta.overlap ~= overlap; 335 | 336 | % refresh overlap 337 | meta.overlap=overlap; overlap=[]; 338 | 339 | % remove rendundant files (with already 100% coverage) 340 | redundantFlag = meta.gridPointN==0; 341 | 342 | if any(redundantFlag) 343 | 344 | % append redundant file records to ouput 345 | m.f=[m.f,meta.f(redundantFlag)']; 346 | m.dtrans=[m.dtrans,nan(3,sum(redundantFlag))]; 347 | m.rmse=[m.rmse,nan(1,sum(redundantFlag))]; 348 | m.overlap = [m.overlap,meta.overlap(redundantFlag)']; 349 | 350 | % remove redundant files from lists 351 | meta = structfun(@(x) ( x(~redundantFlag,:) ), meta, 'UniformOutput', false); 352 | 353 | fprintf('%d redundant files removed\n',sum(redundantFlag)); 354 | 355 | if isempty(meta.overlap); break; end 356 | end 357 | 358 | end 359 | 360 | 361 | %% Test for coregistration rmse to use a selection criteria 362 | if ~disableCoregTest % check to see if disabled 363 | n=find(meta.coregTestFlag); %find data flagged for updating stats 364 | i= 1; % set iteration variable 365 | for i=1:length(n) 366 | 367 | fprintf('testing %d of %d\n',i,length(n)) 368 | 369 | % check for mask fields and make mask cell if exists 370 | if isfield(meta,'maskPolyx') 371 | mask=[meta.maskPolyx{n(i)}(:) meta.maskPolyy{n(i)}(:)]; 372 | else 373 | mask=cell(1,2); 374 | end 375 | 376 | [meta.dtrans(n(i),:),meta.rmse(n(i))] = coregister2tile(meta.f{n(i)},m,N,'mask',mask); 377 | 378 | if isnan(meta.rmse(n(i))); meta.overlap(n(i)) = 0; end 379 | 380 | end 381 | 382 | else 383 | meta.rmse(meta.overlap > 0)=0; 384 | meta.rmse(meta.overlap == 0)= NaN; 385 | end 386 | 387 | % while loop attempts to add data and will repeat if addition failure 388 | % results in meta data deletion, so that resorting of the data rank is 389 | % needed. Could probably be removed with a different indexing scheme. 390 | addFlag = false; 391 | while ~addFlag 392 | 393 | % buid selection array - need to invert meta.gridPointN account since ascending. 394 | A = [meta.rmse,double(meta.qc),max(meta.gridPointN)-meta.gridPointN,meta.overlap,(1:length(meta.f))']; 395 | 396 | % sort each row in priority order of columns 397 | A = sortrows(A); 398 | 399 | % while loop attempts to add data and will continue to add in A 400 | % list order as long as the meta index is not altered (e.g. in the 401 | % case where there is not enough coregistration overlap), so that 402 | % the data is just skipped but not removed from the meta list so 403 | % reording is not needed. 404 | skipFlag=false; 405 | j=1; 406 | while ~skipFlag && (j <= size(A,1)) 407 | 408 | % get the top selection index 409 | i = A(j,end); 410 | 411 | % if meta.rmse isnan, then this is a new cluster. 412 | if isnan(meta.rmse(i)) 413 | meta.rmse(i)=0; 414 | meta.dtrans(i,:) = [0,0,0]; 415 | c=c+1; 416 | end 417 | 418 | if isfield(meta,'maskPolyx') 419 | mask=[meta.maskPolyx{i}(:) meta.maskPolyy{i}(:)]; 420 | else 421 | mask=cell(1,2); 422 | end 423 | 424 | fprintf('adding strip: %s\n',meta.f{i}) 425 | fprintf(... 426 | 'quality: %d, coreg. RSME: %.2f, new data: %d points, co.cluster: %d\n',... 427 | meta.qc(i),meta.rmse(i),meta.gridPointN(i),c) 428 | 429 | [N,skipFlag] = addStrip2Mosaic( meta.f{i},m,meta.stripDate(i)-dy0,N,meta.dtrans(i,:)',meta.rmse(i),... 430 | 'mergeMethod',mergeMethod,... 431 | 'mask',mask); 432 | 433 | j=j+1; 434 | end 435 | 436 | % if skipFlag is still 0, none of the remaining DEMs will 437 | % coregister 438 | if ~skipFlag 439 | % clear meta.f to break outer loop 440 | meta.f=[]; 441 | break; 442 | end 443 | 444 | % add this file name 445 | m.f=[m.f,meta.f(i)]; 446 | 447 | % add overlap 448 | m.overlap = [m.overlap,meta.overlap(i)]; 449 | 450 | % remove this file from the meta struct 451 | in=1:length(meta.f); in(i)=[]; 452 | meta = structfun(@(x) ( x(in,:) ), meta, 'UniformOutput', false); 453 | 454 | % check for success 455 | addFlag=~isnan(m.rmse(1,end)); 456 | 457 | end 458 | % update cluster array 459 | C(C == 0 & N > 0)=c; 460 | m.C=C; 461 | 462 | % update data coverage field N in file 463 | m.N = N; 464 | 465 | end 466 | 467 | %% Generate Meta File 468 | m.version=tileVersion; 469 | tileMeta(m); 470 | --------------------------------------------------------------------------------