code wiki / (root) / nx_distcal.nx

nx_distcal.nx source

↩ module page · 233 lines · 9209 B

1// nx_distcal.nx -- Solves distortion by jointly optimizing pose, focal length, and radial lens distortion coefficient using Gauss-Newton. 2// nx_distcal.nx -- SOLVE THE DISTORTION, do not assume it: joint pose + focal + k1 Gauss-Newton. 3// 4// WHY: nx_lensdist models radial distortion, inverts it and corrects for it -- but every one of those calls 5// takes k1 as a KNOWN input. A photograph does not come with its distortion coefficient any more than it 6// comes with its focal length, so the whole correction chain currently presumes a calibration nobody has 7// performed. This adds k1 as one more column of the same joint Jacobian that already carries pose and focal. 8// 9// Parameter vector: 6*nv poses + focal + k1 = 6*nv + 2. 10// âš the k1 probe step must be LARGE ENOUGH TO MOVE WHOLE PIXELS. The residuals are integer; a probe that 11// displaces the image by a fraction of a pixel produces an all-but-zero Jacobian column, and the solve then 12// reports that k1 has no effect -- an instrument artefact that looks exactly like a genuine degeneracy. 13// license_tier: ORIGINAL 14import "nx_multical.nx" 15import "nx_lensdist.nx" 16 17const DC_DK: i64 = 512 // k1 probe step in Q14 (~0.031) -> ~3 px at the frame corner 18const DC_KMIN: i64 = 0 - 8192 // |k1| > 0.5 is not a lens, it is a diverged solve 19const DC_KMAX: i64 = 8192 20const DC_SQ: i64 = 1024 21const DC_MAXSTEP: i64 = 8 22 23// residuals for all views: project through the pinhole, THEN push through the lens, then compare to what the 24// camera actually recorded. This is the forward model of a real camera, in the order a real camera applies it. 25func dc_resid(cams: *i64, bases: *i64, f: i64, k1: i64, rref: i64, p3: *i64, obs: *i64, np: i64, nv: i64, r: *i64) -> i64 { 26 let o2: *i64 = sys_mmap(32) as *i64 27 let d2: *i64 = sys_mmap(32) as *i64 28 var sse: i64 = 0 29 var v: i64 = 0 30 while v < nv { 31 let bs: *i64 = ((bases as i64) + v * 72) as *i64 32 var k: i64 = 0 33 while k < np { 34 let zc: i64 = r3_project(cams[v * 3 + 0], cams[v * 3 + 1], cams[v * 3 + 2], bs, f, p3[k * 3 + 0], p3[k * 3 + 1], p3[k * 3 + 2], o2) 35 var du: i64 = 0 36 var dv: i64 = 0 37 if zc > 0 { 38 let ig: i64 = ld_distort(o2[0], o2[1], k1, 0, rref, d2) 39 du = d2[0] - obs[(v * np + k) * 2 + 0] 40 dv = d2[1] - obs[(v * np + k) * 2 + 1] 41 } 42 r[(v * np + k) * 2 + 0] = du 43 r[(v * np + k) * 2 + 1] = dv 44 sse = sse + du * du + dv * dv 45 k = k + 1 46 } 47 v = v + 1 48 } 49 return sse 50} 51// ONE joint step over 6*nv + 2 parameters. fp[0] = focal in/out, kp[0] = k1 in/out. 52// âš âš THE TRANSLATION PROBE MUST BE SCALED TO THE SCENE. PNP_DT is an absolute constant (32 units) chosen for a 53// scene ~1000 units away. Point a camera at a real part in real millimetres, 48000 units back, and a 32-unit 54// nudge moves the projection by UNDER ONE PIXEL -- so the forward-difference column is integer-quantised to 55// 0s and 1s, the normal equations go ill-conditioned, and the solve silently stops converging while still 56// reporting a plausible-looking answer. dc_step now sizes the probe AUTOMATICALLY from the data (see 57// pnp_probe_px); dc_step_s still takes it explicitly for a caller that wants to control it. 58func dc_step(cams: *i64, bases: *i64, fp: *i64, kp: *i64, rref: i64, p3: *i64, obs: *i64, np: i64, nv: i64, w: *i64) -> i64 { 59 return dc_step_s(cams, bases, fp, kp, rref, p3, obs, np, nv, w, pnp_probe_auto(cams, p3, np, fp[0])) 60} 61// kept as the named entry point for callers that know their working distance; the sizing rule itself lives 62// ONCE, in nx_pnprefine, so the three solvers cannot drift apart on it. 63func dc_probe_for(dist: i64, f: i64) -> i64 { 64 return pnp_probe_px(dist, f) 65} 66func dc_step_s(cams: *i64, bases: *i64, fp: *i64, kp: *i64, rref: i64, p3: *i64, obs: *i64, np: i64, nv: i64, w: *i64, dt: i64) -> i64 { 67 let npar: i64 = 6 * nv + 2 68 let nres: i64 = 2 * np * nv 69 let r0: *i64 = (w[0]) as *i64 70 let rp: *i64 = (w[1]) as *i64 71 let J: *i64 = (w[2]) as *i64 72 let A: *i64 = (w[3]) as *i64 73 let b: *i64 = (w[4]) as *i64 74 let x: *i64 = (w[5]) as *i64 75 let bb: *i64 = (w[6]) as *i64 76 let sse: i64 = dc_resid(cams, bases, fp[0], kp[0], rref, p3, obs, np, nv, r0) 77 78 var j: i64 = 0 79 while j < npar { 80 var af: i64 = fp[0] 81 var ak: i64 = kp[0] 82 var vv: i64 = 0 83 var sub: i64 = 0 84 var kind: i64 = 0 // 0 = pose, 1 = focal, 2 = k1 85 if j == npar - 2 { kind = 1 } 86 if j == npar - 1 { kind = 2 } 87 if kind == 1 { af = af + MC_DF } 88 if kind == 2 { ak = ak + DC_DK } 89 if kind == 0 { 90 vv = j / 6 91 sub = j % 6 92 } 93 var saved: i64 = 0 94 if kind == 0 { 95 if sub < 3 { 96 saved = cams[vv * 3 + sub] 97 cams[vv * 3 + sub] = saved + dt 98 } 99 if sub >= 3 { 100 var i2: i64 = 0 101 while i2 < 9 { 102 bb[i2] = bases[vv * 9 + i2] 103 i2 = i2 + 1 104 } 105 let bs: *i64 = ((bases as i64) + vv * 72) as *i64 106 if sub == 3 { pnp_rot(bs, PNP_DW, 0, 0) } 107 if sub == 4 { pnp_rot(bs, 0, PNP_DW, 0) } 108 if sub == 5 { pnp_rot(bs, 0, 0, PNP_DW) } 109 } 110 } 111 let ig: i64 = dc_resid(cams, bases, af, ak, rref, p3, obs, np, nv, rp) 112 if kind == 0 { 113 if sub < 3 { cams[vv * 3 + sub] = saved } 114 if sub >= 3 { 115 var i3: i64 = 0 116 while i3 < 9 { 117 bases[vv * 9 + i3] = bb[i3] 118 i3 = i3 + 1 119 } 120 } 121 } 122 var kk: i64 = 0 123 while kk < nres { 124 J[j * nres + kk] = rp[kk] - r0[kk] 125 kk = kk + 1 126 } 127 j = j + 1 128 } 129 var a1: i64 = 0 130 while a1 < npar { 131 var a2: i64 = 0 132 while a2 < npar { 133 var s: i64 = 0 134 var k2: i64 = 0 135 while k2 < nres { 136 s = s + J[a1 * nres + k2] * J[a2 * nres + k2] 137 k2 = k2 + 1 138 } 139 A[a1 * npar + a2] = s 140 a2 = a2 + 1 141 } 142 var sb: i64 = 0 143 var k3: i64 = 0 144 while k3 < nres { 145 sb = sb + J[a1 * nres + k3] * r0[k3] 146 k3 = k3 + 1 147 } 148 b[a1] = 0 - sb 149 a1 = a1 + 1 150 } 151 if sc_solve(A, b, x, npar) == 0 { return sse } 152 var q: i64 = 0 153 while q < npar { 154 let lim: i64 = DC_MAXSTEP * DC_SQ 155 if x[q] > lim { x[q] = lim } 156 if x[q] < 0 - lim { x[q] = 0 - lim } 157 q = q + 1 158 } 159 var v2: i64 = 0 160 while v2 < nv { 161 cams[v2 * 3 + 0] = cams[v2 * 3 + 0] + (x[v2 * 6 + 0] * dt) / DC_SQ 162 cams[v2 * 3 + 1] = cams[v2 * 3 + 1] + (x[v2 * 6 + 1] * dt) / DC_SQ 163 cams[v2 * 3 + 2] = cams[v2 * 3 + 2] + (x[v2 * 6 + 2] * dt) / DC_SQ 164 let bs2: *i64 = ((bases as i64) + v2 * 72) as *i64 165 pnp_rot(bs2, (x[v2 * 6 + 3] * PNP_DW) / DC_SQ, (x[v2 * 6 + 4] * PNP_DW) / DC_SQ, (x[v2 * 6 + 5] * PNP_DW) / DC_SQ) 166 v2 = v2 + 1 167 } 168 var nf: i64 = fp[0] + (x[npar - 2] * MC_DF) / DC_SQ 169 if nf < MC_FMIN { nf = MC_FMIN } 170 if nf > MC_FMAX { nf = MC_FMAX } 171 fp[0] = nf 172 var nk: i64 = kp[0] + (x[npar - 1] * DC_DK) / DC_SQ 173 if nk < DC_KMIN { nk = DC_KMIN } 174 if nk > DC_KMAX { nk = DC_KMAX } 175 kp[0] = nk 176 return sse 177} 178// iterate, keeping the BEST (poses, focal, k1) seen 179func dc_refine(cams: *i64, bases: *i64, fp: *i64, kp: *i64, rref: i64, p3: *i64, obs: *i64, np: i64, nv: i64, iters: i64, w: *i64) -> i64 { 180 return dc_refine_s(cams, bases, fp, kp, rref, p3, obs, np, nv, iters, w, pnp_probe_auto(cams, p3, np, fp[0])) 181} 182func dc_refine_s(cams: *i64, bases: *i64, fp: *i64, kp: *i64, rref: i64, p3: *i64, obs: *i64, np: i64, nv: i64, iters: i64, w: *i64, dt: i64) -> i64 { 183 let rtmp: *i64 = (w[0]) as *i64 184 let bcam: *i64 = (w[7]) as *i64 185 let bbas: *i64 = (w[8]) as *i64 186 var best: i64 = dc_resid(cams, bases, fp[0], kp[0], rref, p3, obs, np, nv, rtmp) 187 var bf: i64 = fp[0] 188 var bk: i64 = kp[0] 189 var i: i64 = 0 190 while i < nv * 3 { 191 bcam[i] = cams[i] 192 i = i + 1 193 } 194 i = 0 195 while i < nv * 9 { 196 bbas[i] = bases[i] 197 i = i + 1 198 } 199 var it: i64 = 0 200 while it < iters { 201 let ig: i64 = dc_step_s(cams, bases, fp, kp, rref, p3, obs, np, nv, w, dt) 202 let cur: i64 = dc_resid(cams, bases, fp[0], kp[0], rref, p3, obs, np, nv, rtmp) 203 if cur < best { 204 best = cur 205 bf = fp[0] 206 bk = kp[0] 207 i = 0 208 while i < nv * 3 { 209 bcam[i] = cams[i] 210 i = i + 1 211 } 212 i = 0 213 while i < nv * 9 { 214 bbas[i] = bases[i] 215 i = i + 1 216 } 217 } 218 it = it + 1 219 } 220 i = 0 221 while i < nv * 3 { 222 cams[i] = bcam[i] 223 i = i + 1 224 } 225 i = 0 226 while i < nv * 9 { 227 bases[i] = bbas[i] 228 i = i + 1 229 } 230 fp[0] = bf 231 kp[0] = bk 232 return best 233}