@@ -61,38 +61,59 @@ function radialDistortion!(
6161 src:: AbstractMatrix
6262 ) where {N}
6363
64- center = SVector {2} (cc. center)
65- k = ntuple (i -> cc. kc[i], N)
66- H, W = size (src)
67- c₁, c₂ = center
68-
69- # pre-allocate buffers for the radius powers
70- buf = Vector {T} (undef, max (H, W))
71-
72- @tturbo for h_d in 1 : H, w_d in 1 : W
73- h_ = h_d - c₁
74- w_ = w_d - c₂
75- r² = h_ * h_ + w_ * w_
76-
77- num = one (T)
78- rⁿ = r²
79- for i in 1 : N
80- num += k[i] * rⁿ
81- rⁿ *= r²
64+ h, w = size (dest)
65+ @assert size (src) == (h, w) " Sizes of src and dest must be the same"
66+ @assert (cc. height, cc. width) == (h, w) " Calibration size mismatches image size"
67+ inv_fx = inv (cc. K[1 , 1 ])
68+ inv_fy = inv (cc. K[2 , 2 ])
69+ cx = cc. K[1 , 3 ]
70+ cy = cc. K[2 , 3 ]
71+ k1, k2, p1, p2, k3 = cc. kc
72+ @tturbo for u in 1 : w
73+ # Calculate x_norm once per column
74+ x_norm = (u - cx) * inv_fx
75+ x_sq = x_norm^ 2
76+
77+ for v in 1 : h
78+ y_norm = (v - cy) * inv_fy
79+ y_sq = y_norm^ 2
80+
81+ # --- Distortion Logic (Brown-Conrady) ---
82+ r_sq = x_sq + y_sq
83+
84+ # Radial component: (1 + k1*r^2 + k2*r^4 + k3*r^6)
85+ radial_term = 1.0 + r_sq * (k1 + r_sq * (k2 + r_sq * k3))
86+ # radial_term = evalpoly(r_sq, (1, k1, k2, k3))
87+
88+ # Tangential component
89+ xy_2 = 2.0 * x_norm * y_norm
90+ x_tangential = p1 * xy_2 + p2 * (r_sq + 2.0 * x_sq)
91+ y_tangential = p1 * (r_sq + 2.0 * y_sq) + p2 * xy_2
92+
93+ # Distorted normalized coordinates
94+ x_dist = x_norm * radial_term + x_tangential
95+ y_dist = y_norm * radial_term + y_tangential
96+
97+ # --- Project back to pixel coordinates ---
98+ # Nearest neighbor interpolation
99+ u_src = round (Int, (x_dist * cc. K[1 , 1 ]) + cx)
100+ v_src = round (Int, (y_dist * cc. K[2 , 2 ]) + cy)
101+
102+ # no bounds check -> not ideal
103+
104+ @inbounds dest[v, u] = src[v_src, u_src]
105+
106+ # with bounds check :
107+ # if 1 <= u_src <= w && 1 <= v_src <= h
108+ # else
109+ # # Pixel is out of bounds (black border)
110+ # @inbounds dest[v, u] = 0
111+ # end
82112 end
83-
84- h_u = c₁ + h_ / num
85- w_u = c₂ + w_ / num
86-
87- # nearest-neighbour lookup (round + clamp)
88- hi = clamp (round (Int, h_u), 1 , H)
89- wi = clamp (round (Int, w_u), 1 , W)
90- dest[h_d, w_d] = src[hi, wi]
91113 end
92114 return nothing
93115end
94116
95-
96117"""
97118 intersectLineToPlane3D(planenorm, planepnt, raydir, raypnt) -> point
98119
@@ -130,15 +151,15 @@ end
130151"""
131152 $SIGNATURES
132153
133- Ray trace from pixel coords to a floor in local level reference which is assumed
154+ Ray trace from pixel coords to a floor in local level reference which is assumed
134155aligned with gravity. Returns intersect in local level frame (coordinates).
135156
136157Notes
137158- Implemented against local level to allow easier local or world reference usage,
138159 - Just assume world is local level, i.e. `l_nFL = w_nFL` and `l_FL = w_FL`.
139160 - User must provide (assumed dynamic) local level transform via `l_T_ex` -- see example below!
140161- Coordinate chain used is from ( pixel-array (a) --> camera (c) --> extrinsic (ex) --> level (l) )
141- - `c_H_a` from pixel-array to camera (as homography matrix)
162+ - `c_H_a` from pixel-array to camera (as homography matrix)
142163 - `a_F` feature in array pixel frame
143164 - `l_T_ex` extrinsic in body (or extrinsic to local level), SE3 Manifold element using ArrayPartition
144165 - `ex_T_c` camera in extrinsic (or camera to extrinsic)
@@ -155,7 +176,7 @@ ci,cj = 360,640 # assuming 720x1280 image
155176c_H_a = [0 1 -cj; 1 0 -ci; 0 0 f] # camera matrix
156177
157178# body to extrinsic of camera -- e.g. camera looking down 0.2 and left 0.2
158- # local level to body to extrinsic transform
179+ # local level to body to extrinsic transform
159180l_T_b = ArrayPartition([0;0;0.], R0)
160181b_T_ex = ArrayPartition([0;0;0.], exp_lie(Mr, hat(Mr, R0, [0;0.2;0.2])))
161182l_T_ex = compose(M, l_T_b, b_T_ex) # this is where body reference is folded in.
@@ -177,14 +198,14 @@ l_Forb = intersectRayToPlane(
177198See also: `CameraModels.intersectLineToPlane3D`
178199"""
179200function intersectRayToPlane (
180- c_H_a:: AbstractMatrix{<:Real} ,
181- a_F:: AbstractVector{<:Real} ,
182- l_nFL:: AbstractVector{<:Real} ,
183- l_FL:: AbstractVector{<:Real} ;
184- M = SpecialEuclideanGroup (3 ; variant = :right ),
185- l_T_ex = ArrayPartition ([0 ;0 ;0. ], exp (SpecialOrthogonalGroup (3 ), hat (LieAlgebra (SpecialOrthogonalGroup (3 )), [0 ;0.2 ;0. ]))),
186- ex_T_c = ArrayPartition ([0 ;0 ;0. ], [0 0 1 ; - 1 0 0 ; 0 - 1 0. ]),
187- )
201+ c_H_a:: AbstractMatrix{<:Real} ,
202+ a_F:: AbstractVector{<:Real} ,
203+ l_nFL:: AbstractVector{<:Real} ,
204+ l_FL:: AbstractVector{<:Real} ;
205+ M = SpecialEuclideanGroup (3 ; variant = :right ),
206+ l_T_ex = ArrayPartition ([0 ;0 ;0.0 ], exp (SpecialOrthogonalGroup (3 ), hat (LieAlgebra (SpecialOrthogonalGroup (3 )), [0 ;0.2 ;0.0 ]))),
207+ ex_T_c = ArrayPartition ([0 ;0 ;0.0 ], [0 0 1 ; - 1 0 0 ; 0 - 1 0.0 ]),
208+ )
188209 # camera in level (or camera to level) manifold element as ArrayPartition
189210 l_T_c = compose (M, l_T_ex, ex_T_c)
190211
0 commit comments