From fbfd8a64dcb586bf385d275147b3f242972d01c7 Mon Sep 17 00:00:00 2001 From: jstanleyx Date: Fri, 17 Mar 2017 13:43:10 -0700 Subject: [PATCH] Add files via upload sampleAerielleVideoDemoPIX -- uses pixel toolbox to make pixel insts. Various tweaks and some new functions to deal with this. --- Contents.m | 6 +- buildRectProducts.m | 2 + demoInputFile.m | 4 + distortCaltech.m | 49 +++++++ findCOMRefObj.m | 4 + findUVnDOF.m | 11 +- findXYZ6dof.m | 12 +- lcpBeta2GeomCamIp.m | 48 +++++++ lcpBeta2P.m | 44 ++++++ makePixelInstsDemo.m | 67 +++++++++ sampleAerielleVideoDemoPIX.m | 254 +++++++++++++++++++++++++++++++++++ sampleDJIFrame.m | 26 +++- saveStackData.m | 49 +++++++ showInsts.m | 22 ++- undistortCaltech.m | 50 +++++++ 15 files changed, 616 insertions(+), 32 deletions(-) create mode 100644 distortCaltech.m create mode 100644 lcpBeta2GeomCamIp.m create mode 100644 lcpBeta2P.m create mode 100644 makePixelInstsDemo.m create mode 100644 sampleAerielleVideoDemoPIX.m create mode 100644 saveStackData.m create mode 100644 undistortCaltech.m diff --git a/Contents.m b/Contents.m index cdf70b9..eeecfc3 100644 --- a/Contents.m +++ b/Contents.m @@ -25,12 +25,14 @@ % % Support minor routines % findUVnDOF - find U,V for any xyz using n dof geometry -% DJIFindXYZ6dof - find XYZ for any UV using 6 dof geometry -% DJIUndistort - convert from distorted to undistorted coords +% findXYZ6dof - find XYZ for any UV using 6 dof geometry +% undistort - convert from distorted to undistorted coords +% distort - convert from undistorted to distorted % makeUAVPn - create good pathnames for CIL expectations % makefr - used in distortion, f(r). % makeRadDist - radial distance for distortion calcs % makeTangDist - tangential distance for distortion calcs +% lcpBeta2P - build a P matrix from lcp and beta (angles) % % CILroutines that are called % findXYZ - find XYZ for any UV diff --git a/buildRectProducts.m b/buildRectProducts.m index 1ddd7fa..9517956 100644 --- a/buildRectProducts.m +++ b/buildRectProducts.m @@ -78,6 +78,8 @@ images.dark(:,:,i) = bar; end images.N(good) = images.N(good)+1; + +end % % Copyright (C) 2017 Coastal Imaging Research Network diff --git a/demoInputFile.m b/demoInputFile.m index 4f3fcb7..b8b75e6 100644 --- a/demoInputFile.m +++ b/demoInputFile.m @@ -9,6 +9,10 @@ inputs.gcpFn = [pwd, filesep, 'demoGCPFile.mat']; inputs.instsFn = [pwd, filesep,'demoInstsFile']; % instrument m-file location +inputs.pnIn = '/tmp/foo/UAV-Processing-Toolbox-master/demoMovies'; +inputs.dayFn = '271_Oct.01'; +inputs.pncx = inputs.pnIn; + % 2. Geometry solution Inputs: % The six extrinsic variables, the camera location and viewing angles % in the order [ xCam yCam zCam Azimuth Tilt Roll]. diff --git a/distortCaltech.m b/distortCaltech.m new file mode 100644 index 0000000..51c8678 --- /dev/null +++ b/distortCaltech.m @@ -0,0 +1,49 @@ +function [ud,vd] = distortCaltech(u,v,lcp) +% [ud,vd] = DJIDistort(u,v,lcp) +% +% converts from undistorted to distorted pixel locations for a DJI phantom. +% This is based on equations from the Caltech lens distortion manuals. +% lcp contains all the relevant intrinsic as well as radial and tangential +% distortion coefficients. + +% written Dec 2015, updated from a now obsolete version that used a single +% lens calibration for the largest (chip size) image while converting from +% smaller image types. This was found not to work for phantom 3s. + +% find the range dependent correction factor, fr. +x = (u(:)-lcp.c0U)/lcp.fx; % normalize to tanAlpha +y = (v(:)-lcp.c0V)/lcp.fy; +r2 = x.*x + y.*y; % distortion found based on Large format units +fr = interp1(lcp.r,lcp.fr,sqrt(r2)); +dx = interp2(lcp.x,lcp.y,lcp.dx,x,y); +dy = interp2(lcp.x,lcp.y,lcp.dy,x,y); +x2 = x.*fr + dx; +y2 = y.*fr + dy; +ud = x2*lcp.fx+lcp.c0U; % answer in chip pixel units +vd = y2*lcp.fy+lcp.c0V; + + +% +% Copyright (C) 2017 Coastal Imaging Research Network +% and Oregon State University + +% This program is free software: you can redistribute it and/or +% modify it under the terms of the GNU General Public License as +% published by the Free Software Foundation, version 3 of the +% License. + +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. + +% You should have received a copy of the GNU General Public License +% along with this program. If not, see +% . + +% CIRN: https://coastal-imaging-research-network.github.io/ +% CIL: http://cil-www.coas.oregonstate.edu +% +%key UAVProcessingToolbox +% + diff --git a/findCOMRefObj.m b/findCOMRefObj.m index 09ce8ba..5abc0ff 100644 --- a/findCOMRefObj.m +++ b/findCOMRefObj.m @@ -18,6 +18,10 @@ uv = round(findUVnDOF(beta,xyz(i,:),meta.globals)); URef = [uv(1)-dUV(1,1): uv(1)+dUV(1,1)]; VRef = [uv(2)-dUV(1,2): uv(2)+dUV(1,2)]; +% you may not go off the edge! +VRef = VRef( find(VRef>0)); VRef = VRef( find(VRef0)); URef = URef( find(URefthresh(i)); diff --git a/findUVnDOF.m b/findUVnDOF.m index 2ea7153..45901ff 100644 --- a/findUVnDOF.m +++ b/findUVnDOF.m @@ -42,19 +42,12 @@ end lcp = globs.lcp; -K = [lcp.fx 0 lcp.c0U; - 0 -lcp.fy lcp.c0V; - 0 0 1]; - -R = angles2R(b2(4), b2(5), b2(6)); -IC = [eye(3) -b2(1:3)']; -P = K*R*IC; -P = P/P(3,4); % unnecessary since we will also normalize UVs +P = lcpBeta2P( lcp, b2 ); UV = P*[xyz'; ones(1,size(xyz,1))]; UV = UV./repmat(UV(3,:),3,1); -[U,V] = DJIDistort(UV(1,:),UV(2,:),lcp); +[U,V] = distort(UV(1,:),UV(2,:),lcp); UV = [U; V]; % diff --git a/findXYZ6dof.m b/findXYZ6dof.m index 02e8698..5c46c92 100644 --- a/findXYZ6dof.m +++ b/findXYZ6dof.m @@ -6,17 +6,11 @@ % extrinsic parameters [xyzCam azimuth tilt roll] where angles are in % radians. lcp and image size NU, NV are also needed. -[u2,v2] = DJIUndistort(U(:),V(:),lcp); % undistort +[u2,v2] = undistort(U(:),V(:),lcp); % undistort % build projection matrix in chip pixels -K = [lcp.fx 0 lcp.c0U; % chip scale K - 0 -lcp.fy lcp.c0V; - 0 0 1]; - -R = angles2R(b(4), b(5), b(6)); -IC = [eye(3) -b(1:3)']; -P = K*R*IC; -P = P/P(3,4); % unnecessary since we will also normalize UVs +P = lcpBeta2P( lcp, b ); + m = P2m(P); xyz = findXYZ(m,[u2 v2], z, 3); diff --git a/lcpBeta2GeomCamIp.m b/lcpBeta2GeomCamIp.m new file mode 100644 index 0000000..4002601 --- /dev/null +++ b/lcpBeta2GeomCamIp.m @@ -0,0 +1,48 @@ +function [geom, cam, ip] = lcpBeta2GeomCamIp( lcp, beta ) + +% function [cam, geom, ip] = lcpBeta2GeomCamIp( lcp, beta ) +% +% use the lcp and beta values to create the cam, geom, and ip structs +% that the PIXEL toolbox uses to build a collect (findUV, findXYX). + +% step 1, make a P, then get the m from it. +P = lcpBeta2P( lcp, beta ); +m = P2m( P ); + +cam.cameraNumber = 1; +cam.x = beta(1); +cam.y = beta(2); +cam.z = beta(3); +cam.lcp = lcp; + +geom.m = m; +geom.azimuth = beta(4); +geom.fov = -1; + +ip.width = lcp.NU; +ip.height = lcp.NV; + + +% +% Copyright (C) 2017 Coastal Imaging Research Network +% and Oregon State University + +% This program is free software: you can redistribute it and/or +% modify it under the terms of the GNU General Public License as +% published by the Free Software Foundation, version 3 of the +% License. + +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. + +% You should have received a copy of the GNU General Public License +% along with this program. If not, see +% . + +% CIRN: https://coastal-imaging-research-network.github.io/ +% CIL: http://cil-www.coas.oregonstate.edu +% +%key UAVProcessingToolbox +% diff --git a/lcpBeta2P.m b/lcpBeta2P.m new file mode 100644 index 0000000..174bac0 --- /dev/null +++ b/lcpBeta2P.m @@ -0,0 +1,44 @@ +function P = lcpBeta2P( lcp, beta ) + +% function P = lcpBeta2P( lcp, beta ) +% +% create a P matrix from the lcp and beta +% +% used by findUV6DOF and findXYZnDOF and to make things +% for pixel toolbox geometry. + +K = [lcp.fx 0 lcp.c0U; + 0 -lcp.fy lcp.c0V; + 0 0 1]; + +R = angles2R(beta(4), beta(5), beta(6)); +IC = [eye(3) -beta(1:3)']; +P = K*R*IC; +P = P/P(3,4); % unnecessary since we will also normalize UVs + +return; + + +% +% Copyright (C) 2017 Coastal Imaging Research Network +% and Oregon State University + +% This program is free software: you can redistribute it and/or +% modify it under the terms of the GNU General Public License as +% published by the Free Software Foundation, version 3 of the +% License. + +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. + +% You should have received a copy of the GNU General Public License +% along with this program. If not, see +% . + +% CIRN: https://coastal-imaging-research-network.github.io/ +% CIL: http://cil-www.coas.oregonstate.edu +% +%key UAVProcessingToolbox +% diff --git a/makePixelInstsDemo.m b/makePixelInstsDemo.m new file mode 100644 index 0000000..4de33ac --- /dev/null +++ b/makePixelInstsDemo.m @@ -0,0 +1,67 @@ +function r = makePixelInstsDemo( ) + +% Make pixel instruments using pixel toolbox. duplicate of demoInstsFile. + +PIXForget; +PIXSetStation('aerielle'); + +zmsl = 0; + +instID = []; + +% vbar insts +y = [450 700]; +x = [125:25:225]; + +for ii=1:length(x) + + name = ['vbar' num2str(x(ii))]; + tid = PIXMakeVBAR( x(ii), y(1), x(ii), y(2), zmsl, name); + instID = [instID tid]; + +end + +% runup +xshore = 125; +xdune = 70; +y = 60:50:650; +rot = 0; + +for ii=1:length(y) + [xr, yr, zr] = runupPIXArray( xshore, xdune, y(ii), zmsl, rot ); + name = ['runup' num2str(fix(y(ii)))]; + tid = PIXCreateInstrument( name, 'runup', PIXFixedZ+PIXUniqueOnly ); + PIXAddPoints( tid, xr, yr, zr, name ); + instID = [instID tid]; +end + +% cBathy +dx = 5; +dy = 5; +x1 = 80; +x2 = 400; +y1 = 450; +y2 = 900; + +tid = PIXCreateInstrument( 'mBW', 'matrix', PIXFixedZ+PIXDeBayerStack ); +PIXAddMatrix( tid, x1, y1, x2, y2, zmsl, dx, dy ); +instID = [instID tid]; + +% stability slices +tid = PIXCreateInstrument( 'x300Slice', 'line', PIXFixedZ+PIXUniqueOnly ); +PIXAddLine( tid, 300, 500, 300, 540, 7, .1, 'x300Slice' ); +instID = [instID tid]; +tid = PIXCreateInstrument( 'y517Slice', 'line', PIXFixedZ+PIXUniqueOnly ); +PIXAddLine( tid, 100, 520, 115, 520, 7, .1, 'y517Slice' ); +instID = [instID tid]; + + +% make a package of all insts +pid = PIXCreatePackage('AerielleDemo', instID); +e = matlab2Epoch(now); + +% build the initial r +r = PIXCreateR( pid, e, zmsl, 'none' ); + +end + diff --git a/sampleAerielleVideoDemoPIX.m b/sampleAerielleVideoDemoPIX.m new file mode 100644 index 0000000..65a5970 --- /dev/null +++ b/sampleAerielleVideoDemoPIX.m @@ -0,0 +1,254 @@ +% sample an example Aerielle movie. Set this up as generic for other +% movies + +% The important things are: +% - the init material below. Stored as a structure so I can preserve +% records for each UAV analysis. +% - a gcp file from a current survey +% - + +clear +close all + +% required USER INPUT material. Adapt to each collection +demoInputFile; % this file contains all of the required inputs. + +% create cx directory and load instruments +if ~exist(inputs.pncx) + mkdir(inputs.pncx) +end + +% build the instruments we want. can't go all the way yet, no geom +r = makePixelInstsDemo; + +load(inputs.gcpFn); % load gcps + +% find the input folders (may be more than one) +Nf = []; +e0 = matlab2Epoch(inputs.dn0); +clipFns = dir( [ inputs.pnIn filesep inputs.frameFn '*' ]); +NClips = length(clipFns); +for i = 1: NClips + fns{i} = dir([inputs.pnIn filesep clipFns(i).name filesep '*png']); + if isempty(fns{i}) + fns{i} = dir([inputs.pnIn filesep clipFns(i).name filesep '*jpg']); + end + Nf(i) = length(fns{i}); +end +nt = sum(Nf); +dn = datenum(inputs.dateVect) + ([1:nt]-1)*inputs.dt; + +% read the first frame and display. Do a manual geometry on it if needed. +I = imread([inputs.pnIn filesep clipFns(1).name filesep fns{1}(1).name]); +[NV, NU, NC] = size(I); +Ig = rgb2gray(I); % for later sampling. +meta.showFoundRefPoints = inputs.showFoundRefPoints; % easy way to pass. + +% Because nlinfit requires globals, we set up a variable globals (under +% metadata) that contains the lcp as well as the flags and values for +% known geometry variables (eg if we know the camera roll). To minimize +% the use of globals we pass this explicitly except for nlinfit uses when +% we declare it to be global within the calling subroutine. +meta.globals.lcp = makeLCPP3(inputs.stationStr,NU,NV); +meta.globals.knownFlags = inputs.knownFlags; +meta.globals.knowns = inputs.knowns; +clear NU NV NC + +%% + +% When geometries are solved for each frame in turn, the results are saved +% in a metadata file in the cx folder for this day. First search to see if +% such a folder already exists, in which we don't have to re-do all of the +% geometries. Allow a few second slop in time (look for first 9 out of 10 +% digits in epoch time. + +bar = num2str(e0); +foo = dir([inputs.pncx filesep bar(1:9) '*meta*']); +% report the use of an old metadata file, ask for ok +if ~isempty(foo) + yn = input(['Found old metadata file ' foo(1).name '\n' ... + 'Hit return to continue or anything else to start over...'], ... + 's'); + if ~isempty(yn) + foo = []; + end +end +if ~isempty(foo) + oldGeoms = 1; % flag that old geoms exist and load + load([inputs.pncx filesep foo(1).name]); + betas = meta.betas; +else + % if no metafile is found, I initialize one + oldGeoms = 0; + betas = nan(nt,6); % we will save all the betas + [betas(1,:),meta.refPoints] = initUAVAnalysis(I, gcp, inputs, meta); +end + +%% + +% set up instruments and stacks - always do this. +[geom, cam, ip] = lcpBeta2GeomCamIp( meta.globals.lcp, betas(1,:) ); +r = PIXParameterizeR( r, cam, geom, ip ); + +r = PIXRebuildCollect( r ); + +showInsts(I, r); + +% if you don't see what you hoped to see, stop and re-create instruments. +foo = input('Hit Ctrl-C if instruments not proper in Figure 3, otherwise '); + +% save the info in the stacks structure. +stack.r = r; +nPix = size(r.cams(1).U, 1); +stack.data = nan(nt, nPix, 1 ); + +%% + +xlabel('x (m)'); ylabel('y (m)'); title('Demo Run Time Exposure') + +% now save metadata if it wasn't already there +if oldGeoms==0 + info.time = e0; info.station = inputs.stationStr; info.camera = 'x'; + info.type = 'meta'; info.format = 'mat'; + metaFn = argusFilename(info); + meta.gcpFn = inputs.gcpFn; meta.gcpList = inputs.gcpList; + meta.betas = betas; + eval(['save ' inputs.pncx metaFn ' meta']) +end + +%% + +% if I have only ONE beta, then I'm working with a new meta file, must do +% geometries. reuse oldGeoms flag for this +if isnan(betas(2,1)) + oldGeoms = 0; +elseif max(find(~isnan(betas(:,1)))). + +% CIRN: https://coastal-imaging-research-network.github.io/ +% CIL: http://cil-www.coas.oregonstate.edu +% +%key UAVProcessingToolbox +% + diff --git a/sampleDJIFrame.m b/sampleDJIFrame.m index e52da4a..90942a8 100644 --- a/sampleDJIFrame.m +++ b/sampleDJIFrame.m @@ -3,15 +3,27 @@ % % samples pixels from a grayshade frame, I, at pixels that are determined % by the instrument descriptions in insts, created by makeDJIInsts -U = 1:globs.lcp.NU; -V = 1:globs.lcp.NV; -for i = 1: length(insts) - data(i).I = nan(1,size(insts(i).xyzAll,1)); - UV = round(findUVnDOF(beta,insts(i).xyzAll,globs)); +%%U = 1:globs.lcp.NU; +%%V = 1:globs.lcp.NV; +if( isstruct( insts ) ) % actually 'r' + xyzAll = insts.cams(1).XYZ; + data = nan(1,size(xyzAll,1)); + UV = round(findUVnDOF( beta, xyzAll, globs)); UV = reshape(UV,[],2); good = find(onScreen(UV(:,1),UV(:,2),globs.lcp.NU,globs.lcp.NV)); - ind = sub2ind([globs.lcp.NV globs.lcp.NU],UV(good,2),UV(good,1)); - data(i).I(good) = I(ind); + ind = sub2ind([globs.lcp.NV globs.lcp.NU], UV(good,2),UV(good,1)); + data(good) = I(ind); +else + + for i = 1: length(insts) + data(i).I = nan(1,size(insts(i).xyzAll,1)); + UV = round(findUVnDOF(beta,insts(i).xyzAll,globs)); + UV = reshape(UV,[],2); + good = find(onScreen(UV(:,1),UV(:,2),globs.lcp.NU,globs.lcp.NV)); + ind = sub2ind([globs.lcp.NV globs.lcp.NU],UV(good,2),UV(good,1)); + data(i).I(good) = I(ind); + end + end % diff --git a/saveStackData.m b/saveStackData.m new file mode 100644 index 0000000..576b4f7 --- /dev/null +++ b/saveStackData.m @@ -0,0 +1,49 @@ +function saveStackData( inputs, info, stack ) + +% function saveStackData( inputs, info, stack ) +% uses the information from the stack struct ('r' and 'data') +% to generate all the .mat files just like a regular Argus stack +% in .mat format. inputs provides the pncx variable, info is the +% struct with proto-filename data for argusFilename call. + +% get all the unique names from stack.r + +uname = unique( stack.r.cams(1).names ); + +% encoded UV for find. +UVencoded = stack.r.cams(1).U + stack.r.cams(1).V * 10000; + +% one T for all data. but what is it? ASSUME -- starts at info.time and +% increments by 0.5 seconds. USER? +T = 0:size(stack.data,1)-1; +T = T .* 0.5; %%% assumption!!!! +T = T + info.time; + +for ii = 1:length(uname) + + % empty data + XYZ = []; RAW = []; CAM = []; + SHUTTER = []; CORRECTED = []; + GAIN = []; + WARNING = ''; + + nind = strmatch( uname{ii}, stack.r.cams(1).names, 'exact' ); + + XYZ = stack.r.cams(1).XYZ(nind,:); + RAW = stack.data(:,nind); + + CAM = ones(length(nind),1) * stack.r.cams(1).cameraNumber; + + CORRECTED = RAW; + + info.type = uname{ii}; + name = uname{ii}; + fn = argusFilename(info); + + save( [inputs.pncx filesep fn], 'RAW', 'T', 'CAM', 'XYZ', 'GAIN', 'name', '-V7.3' ); + +end + + + + diff --git a/showInsts.m b/showInsts.m index b5e9a43..1017841 100644 --- a/showInsts.m +++ b/showInsts.m @@ -7,11 +7,23 @@ function showInsts(I,insts,beta, globs) imagesc(I) hold on -for i = 1: length(insts) - xyz = [insts(i).xyzAll]; - UV = findUVnDOF(beta,xyz, globs); - UV = reshape(UV,[],2); - plot(UV(:,1),UV(:,2),'.') + +% two potential calls to this -- with 'old' insts struct or with 'r' +% from using PIXEL toolbox. insts needs UV built, r has them + +if( isstruct( insts ) ) + + r = insts; % put it in terms John knows + plot( r.cams(1).U, r.cams(1).V, '.' ); + +else + + for i = 1: length(insts) + xyz = [insts(i).xyzAll]; + UV = findUVnDOF(beta,xyz, globs); + UV = reshape(UV,[],2); + plot(UV(:,1),UV(:,2),'.') + end end % diff --git a/undistortCaltech.m b/undistortCaltech.m new file mode 100644 index 0000000..21ecbd6 --- /dev/null +++ b/undistortCaltech.m @@ -0,0 +1,50 @@ +function [u,v] = undistortCaltech(ud,vd, lcp) +% [u,v] = DJIUnDistort(ud,vd,lcp) +% +% converts from distorted to undistorted pixel locations for a DJI phantom. +% This is based on the Caltech cam cal equations. This routine replaces +% an obsolete version that mapped to and from chip space. + +% find the range dependent correction factor, fr. +x = (ud-lcp.c0U)/lcp.fx; +y = (vd-lcp.c0V)/lcp.fy; +r = sqrt(x.*x + y.*y); % radius in distorted pixels +r2 = interp1(lcp.fr.*lcp.r,lcp.r,r); % interp into distorted r space +if r~=0 + x2 = x./r.*r2; % undistort range + y2 = y./r.*r2; + x3 = x2 - interp2(lcp.x,lcp.y,lcp.dx,x2,y2)./r.*r2; % assume small dx + y3 = y2 - interp2(lcp.x,lcp.y,lcp.dy,x2,y2)./r.*r2; + u = x3*lcp.fx + lcp.c0U; + v = y3*lcp.fy + lcp.c0V; +else + u = ud; % camera center pixel is unchanged by distortion + v = vd; +end + + + +% +% Copyright (C) 2017 Coastal Imaging Research Network +% and Oregon State University + +% This program is free software: you can redistribute it and/or +% modify it under the terms of the GNU General Public License as +% published by the Free Software Foundation, version 3 of the +% License. + +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. + +% You should have received a copy of the GNU General Public License +% along with this program. If not, see +% . + +% CIRN: https://coastal-imaging-research-network.github.io/ +% CIL: http://cil-www.coas.oregonstate.edu +% +%key UAVProcessingToolbox +% +