1#[allow(unused_imports)]
4use crate::math::FloatMath;
5use crate::types::Status;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum NonlinFilterType {
10 Min,
12 Max,
14 Median,
16}
17
18pub fn dct2d_f32(src: &[f32], dst: &mut [f32], rows: usize, cols: usize) -> Status {
22 let total = rows * cols;
23 if rows == 0 || cols == 0 || src.len() < total || dst.len() < total {
24 return Status::LengthError;
25 }
26
27 let pi_over_2m = core::f32::consts::PI / (2.0 * rows as f32);
28 let pi_over_2n = core::f32::consts::PI / (2.0 * cols as f32);
29 let norm_row0 = (1.0 / rows as f32).sqrt();
30 let norm_rowk = (2.0 / rows as f32).sqrt();
31 let norm_col0 = (1.0 / cols as f32).sqrt();
32 let norm_colk = (2.0 / cols as f32).sqrt();
33
34 for u in 0..rows {
35 let cu = if u == 0 { norm_row0 } else { norm_rowk };
36 let u_f = u as f32;
37
38 for v in 0..cols {
39 let cv = if v == 0 { norm_col0 } else { norm_colk };
40 let v_f = v as f32;
41
42 let mut sum = 0.0f32;
43 for x in 0..rows {
44 let cos_u = ((2 * x + 1) as f32 * u_f * pi_over_2m).cos();
45 for y in 0..cols {
46 let cos_v = ((2 * y + 1) as f32 * v_f * pi_over_2n).cos();
47 sum += src[x * cols + y] * cos_u * cos_v;
48 }
49 }
50 dst[u * cols + v] = cu * cv * sum;
51 }
52 }
53
54 Status::Success
55}
56
57pub fn idct2d_f32(src: &[f32], dst: &mut [f32], rows: usize, cols: usize) -> Status {
59 let total = rows * cols;
60 if rows == 0 || cols == 0 || src.len() < total || dst.len() < total {
61 return Status::LengthError;
62 }
63
64 let pi_over_2m = core::f32::consts::PI / (2.0 * rows as f32);
65 let pi_over_2n = core::f32::consts::PI / (2.0 * cols as f32);
66 let norm_row0 = (1.0 / rows as f32).sqrt();
67 let norm_rowk = (2.0 / rows as f32).sqrt();
68 let norm_col0 = (1.0 / cols as f32).sqrt();
69 let norm_colk = (2.0 / cols as f32).sqrt();
70
71 for x in 0..rows {
72 for y in 0..cols {
73 let mut sum = 0.0f32;
74 for u in 0..rows {
75 let cu = if u == 0 { norm_row0 } else { norm_rowk };
76 let cos_u = ((2 * x + 1) as f32 * u as f32 * pi_over_2m).cos();
77
78 for v in 0..cols {
79 let cv = if v == 0 { norm_col0 } else { norm_colk };
80 let cos_v = ((2 * y + 1) as f32 * v as f32 * pi_over_2n).cos();
81 sum += cu * cv * src[u * cols + v] * cos_u * cos_v;
82 }
83 }
84 dst[x * cols + y] = sum;
85 }
86 }
87
88 Status::Success
89}
90
91pub fn convolve2d_f32(
95 src: &[f32],
96 dst: &mut [f32],
97 rows: usize,
98 cols: usize,
99 kernel: &[f32],
100 k_rows: usize,
101 k_cols: usize,
102 normalize: bool,
103) -> Status {
104 let total = rows * cols;
105 let k_total = k_rows * k_cols;
106 if rows == 0 || cols == 0 || k_rows == 0 || k_cols == 0 {
107 return Status::ArgumentError;
108 }
109 if src.len() < total || dst.len() < total || kernel.len() < k_total {
110 return Status::LengthError;
111 }
112
113 let dead_r = k_rows / 2;
114 let dead_c = k_cols / 2;
115
116 let norm_factor: f32 = if normalize {
117 let mut sum = 0.0f32;
118 for &v in kernel {
119 sum += v.abs();
120 }
121 if sum != 0.0 { sum } else { 1.0 }
122 } else {
123 1.0
124 };
125
126 for r in 0..rows {
127 for c in 0..cols {
128 let mut acc = 0.0f32;
129 for kr in 0..k_rows {
130 let ir = (r as isize + kr as isize - dead_r as isize).clamp(0, (rows - 1) as isize)
131 as usize;
132 for kc in 0..k_cols {
133 let ic = (c as isize + kc as isize - dead_c as isize)
134 .clamp(0, (cols - 1) as isize) as usize;
135 acc += src[ir * cols + ic] * kernel[kr * k_cols + kc];
136 }
137 }
138 dst[r * cols + c] = acc / norm_factor;
139 }
140 }
141
142 Status::Success
143}
144
145pub fn nonlin2d_filter_f32(
149 src: &[f32],
150 dst: &mut [f32],
151 rows: usize,
152 cols: usize,
153 k_size: usize,
154 filtype: NonlinFilterType,
155) -> Status {
156 let total = rows * cols;
157 if rows == 0 || cols == 0 || k_size == 0 || k_size % 2 == 0 || k_size > 7 {
158 return Status::ArgumentError;
159 }
160 if src.len() < total || dst.len() < total {
161 return Status::LengthError;
162 }
163
164 let half = k_size / 2;
165 let k_len = k_size * k_size;
166 let mut sort_buf = [0.0f32; 64];
167
168 for r in 0..rows {
169 for c in 0..cols {
170 let mut count = 0;
171 for kr in 0..k_size {
172 let ir = (r as isize + kr as isize - half as isize).clamp(0, (rows - 1) as isize)
173 as usize;
174 for kc in 0..k_size {
175 let ic = (c as isize + kc as isize - half as isize)
176 .clamp(0, (cols - 1) as isize) as usize;
177 sort_buf[count] = src[ir * cols + ic];
178 count += 1;
179 }
180 }
181
182 match filtype {
183 NonlinFilterType::Min => {
184 let mut min_v = sort_buf[0];
185 for i in 1..k_len {
186 if sort_buf[i] < min_v {
187 min_v = sort_buf[i];
188 }
189 }
190 dst[r * cols + c] = min_v;
191 }
192 NonlinFilterType::Max => {
193 let mut max_v = sort_buf[0];
194 for i in 1..k_len {
195 if sort_buf[i] > max_v {
196 max_v = sort_buf[i];
197 }
198 }
199 dst[r * cols + c] = max_v;
200 }
201 NonlinFilterType::Median => {
202 for a in 1..k_len {
203 let mut b = a;
204 while b > 0 && sort_buf[b - 1] > sort_buf[b] {
205 sort_buf.swap(b - 1, b);
206 b -= 1;
207 }
208 }
209 dst[r * cols + c] = sort_buf[k_len / 2];
210 }
211 }
212 }
213 }
214
215 Status::Success
216}
217
218pub fn sobel_edge_detection_f32(
226 src: &[f32],
227 dst_edges: &mut [f32],
228 rows: usize,
229 cols: usize,
230 threshold: f32,
231) -> Status {
232 let total = rows * cols;
233 if rows < 3 || cols < 3 || src.len() < total || dst_edges.len() < total {
234 return Status::LengthError;
235 }
236
237 let h_sobel: [f32; 9] = [-1.0, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0];
238 let v_sobel: [f32; 9] = [-1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0];
239
240 for r in 0..rows {
241 for c in 0..cols {
242 let mut gx = 0.0f32;
243 let mut gy = 0.0f32;
244
245 for kr in 0..3 {
246 let ir = (r as isize + kr as isize - 1).clamp(0, (rows - 1) as isize) as usize;
247 for kc in 0..3 {
248 let ic = (c as isize + kc as isize - 1).clamp(0, (cols - 1) as isize) as usize;
249 let val = src[ir * cols + ic];
250 gx += val * h_sobel[kr * 3 + kc];
251 gy += val * v_sobel[kr * 3 + kc];
252 }
253 }
254
255 let mag = (gx * gx + gy * gy).sqrt();
256 dst_edges[r * cols + c] = if mag >= threshold { 1.0 } else { 0.0 };
257 }
258 }
259
260 Status::Success
261}
262
263pub fn histogram_2d_f32(src: &[f32], bins: &mut [usize], min_val: f32, max_val: f32) -> Status {
265 let num_bins = bins.len();
266 if src.is_empty() || num_bins == 0 || max_val <= min_val {
267 return Status::ArgumentError;
268 }
269
270 bins.fill(0);
271 let span = max_val - min_val;
272 let scale = (num_bins as f32) / span;
273
274 for &val in src {
275 let clamped = val.clamp(min_val, max_val);
276 let mut idx = ((clamped - min_val) * scale) as usize;
277 if idx >= num_bins {
278 idx = num_bins - 1;
279 }
280 bins[idx] += 1;
281 }
282
283 Status::Success
284}
285
286pub fn mse_2d_f32(img_a: &[f32], img_b: &[f32]) -> f32 {
288 let len = img_a.len().min(img_b.len());
289 if len == 0 {
290 return 0.0;
291 }
292 let mut sum_sq = 0.0f32;
293 for i in 0..len {
294 let diff = img_a[i] - img_b[i];
295 sum_sq += diff * diff;
296 }
297 sum_sq / (len as f32)
298}
299
300pub fn psnr_2d_f32(img_a: &[f32], img_b: &[f32], max_val: f32) -> f32 {
302 let mse = mse_2d_f32(img_a, img_b);
303 if mse <= 1e-12 {
304 return 99.0; }
306 10.0 * ((max_val * max_val) / mse).log10()
307}