diff --git a/matlab/CalibratedCamera.m b/matlab/CalibratedCamera.m new file mode 100644 index 00000000..eeea82fe --- /dev/null +++ b/matlab/CalibratedCamera.m @@ -0,0 +1,30 @@ +classdef CalibratedCamera < handle + properties + ImageSize % [nr nc] + FocalLengths % [fx fy] + PrincipalPoint % [px py] + Skew % scalar + RadialDistortions % [k2 k4] + Rotation % [rx ry rz] rodrigues vec + Translation % [tx ty tz] + end + methods + function obj = CalibratedCamera(s) + for fn = fieldnames(s)', f=fn{1}; + try + obj.(f) = s.(f); + catch + warning('Ignoring unrecognized field: %s', f); + end + end + end + + function camInts = getMatlabCameraIntrinsics(obj) + camInts = cameraIntrinsics(... + obj.FocalLengths,obj.PrincipalPoint,obj.ImageSize,... + 'Skew',obj.Skew,... + 'RadialDistortion',obj.RadialDistortions... + ); + end + end +end \ No newline at end of file diff --git a/matlab/CameraSet.m b/matlab/CameraSet.m new file mode 100644 index 00000000..63df8efb --- /dev/null +++ b/matlab/CameraSet.m @@ -0,0 +1,59 @@ +classdef CameraSet < handle + properties + NumCameras + CameraNames + Cameras + end + + methods + function obj = CameraSet(s) + if ischar(s) + s = ReadYaml(s); + end + obj.NumCameras = s.NumCameras; + obj.CameraNames = s.CameraNames; + + for icam=1:s.NumCameras + cam = s.CameraNames{icam}; + camObjs(icam) = CalibratedCamera(s.(cam)); %#ok + end + fprintf(1,'Read information for %d cameras: %s.\n',s.NumCameras, ... + String.cellstr2CommaSepList(s.CameraNames)); + + obj.Cameras = camObjs; + end + + function [R,t] = getXformCams(obj,cam1,cam2) + % Get/compute matrices R,T that convert from cam1 coordsys to + % cam2coordsys; defined by + % + % [x2;y2;z2] = R*[x1;y1;z1] + t + + c1 = obj.Cameras(cam1); + c2 = obj.Cameras(cam2); + + R1 = rodrigues(c1.Rotation); + t1 = c1.Translation(:); + R2 = rodrigues(c2.Rotation); + t2 = c2.Translation(:); + + R = R2/R1; + t = -R*t1 + t2; + end + + function sp = getMatlabStereoParameters(obj) + if obj.NumCameras~=2 + error('stereoParameters conversion only avaiable when NumCameras==2.'); + end + + cams = obj.Cameras; + sp = struct(); + sp.CameraParameters1 = cams(1).getMatlabCameraIntrinsics; + sp.CameraParameters2 = cams(2).getMatlabCameraIntrinsics; + + [R,t] = obj.getXformCams(1,2); + sp.RotationOfCamera2 = R; + sp.TranslationOfCamera2 = t; + end + end +end \ No newline at end of file diff --git a/matlab/user/CalRigMLStro.m b/matlab/user/CalRigMLStro.m index 27674c76..6c62788a 100644 --- a/matlab/user/CalRigMLStro.m +++ b/matlab/user/CalRigMLStro.m @@ -3,7 +3,8 @@ % Note: ML Stro Calib App requires all ims/views to have the same size. properties - calSess % scalar Session from ML Stro calibration + nonMLinit = false; % if true, obj is not initialized from MATLAB stereo calibration + calSess % scalar Session from ML Stro calibration; or if .nonMLinit, scalar struct mirroring calSession object eplineComputeMode = 'mostaccurate'; % either 'mostaccurate' or 'fastest' end @@ -62,30 +63,50 @@ methods function obj = CalRigMLStro(calSess,varargin) - % calibSession: either a char filename for saved result from ML - % Stereo Calib App; or the object within + % obj = CalRigMLStro(calSess) + % calSess: one of: + % 1. a char filename for saved result from ML Stereo Calib App; + % 2. the ML calibration Session object saved therein; + % 3. char filename for APT-style rig config/param YAML file; + % 4. a YAML struct for such YAML contents offerSave = myparse(varargin, ... 'offerSave',true... ); + isYamlRig = false; if ischar(calSess) - s = load(calSess,'-mat','calibrationSession'); - calSessObj = s.calibrationSession; + [~,~,ext] = fileparts(calSess); + switch ext + case '.yaml' + s = ReadYaml(calSess); + calSessObj = CalRigMLStro.rigYaml2CalSess(s); + isYamlRig = true; + case '.mat' + s = load(calSess,'-mat','calibrationSession'); + calSessObj = s.calibrationSession; + end elseif isa(calSess,'vision.internal.calibration.tool.Session') calSessObj = calSess; + elseif isstruct(calSess) && isfield(calSess,'NumCameras') + calSessObj = CalRigMLStro.rigYaml2CalSess(calSess); + isYamlRig = true; else error('Input argument must be a MATLAB Stereo Calibration Session.'); end obj.calSess = calSessObj; + obj.nonMLinit = isYamlRig; obj.int = obj.getInt(); obj.eplineComputeMode = 'mostaccurate'; %obj.autoCalibrateProj2NormFuncTol(); obj.proj2NormFuncTol = nan; % AL20220302 no longer used - obj.autoCalibrateEplineZrange(); - obj.runDiagnostics(); + + if ~isYamlRig + obj.autoCalibrateEplineZrange(); + obj.runDiagnostics(); + end if offerSave if ischar(calSess) @@ -836,7 +857,12 @@ function rperrPlot(rperrcam1,rperrcam2) xp1ud = cat(1,xp1ud{:}); xp2ud = arrayfun(@(i)undistortPoints(xp2(:,i)',cp2),(1:n)','uni',0); xp2ud = cat(1,xp2ud{:}); - X1 = triangulate(xp1ud,xp2ud,sp); + cm1 = cameraMatrix(cp1,eye(3),[0 0 0]); + cm2 = cameraMatrix(cp2,sp.RotationOfCamera2,sp.TranslationOfCamera2); + X1 = triangulate(xp1ud,xp2ud,cm1,cm2); + % X1 = triangulate(...,sp); + % Don't use this sig beacuse in the case of .nonMLinit, MATLAB/Vision + % doesn't ducktype and requires a real stereoParameters type xp1rp = worldToImage(cp1,eye(3),[0;0;0],X1,'applyDistortion',true); xp2rp = worldToImage(cp2,... @@ -904,6 +930,12 @@ function rperrPlot(rperrcam1,rperrcam2) alpha_c = camParams.Skew; assert(isscalar(alpha_c)); end + function cs = rigYaml2CalSess(s) + % s: struct, rig yaml + cs = struct(); + cs.cameraSet = CameraSet(s); + cs.CameraParameters = cs.cameraSet.getMatlabStereoParameters(); + end end end \ No newline at end of file