1use crate::types::{ARMatrixCodeType, ARdouble, ARHandle, ARMarkerInfo, ARMarkerInfo2};
30use crate::marker::ar_get_line;
31use log::{debug, trace};
32
33#[derive(Debug, Clone, PartialEq)]
35pub struct MatrixCodeResult {
36 pub id: i32,
37 pub dir: i32,
38 pub cf: f64,
39 pub error_corrected: i32,
40}
41
42pub fn ar_matrix_code_get_id(
84 image: &[u8],
85 xsize: i32,
86 ysize: i32,
87 vertex: &[[ARdouble; 2]; 4],
88 code_type: ARMatrixCodeType,
89 pixel_format: crate::types::ARPixelFormat,
90 patt_ratio: f64,
91 id: &mut i32,
92 dir: &mut i32,
93 cf: &mut f64,
94 error_corrected: &mut i32,
95) -> Result<(), &'static str> {
96 let dim = (code_type as i32) & 0xFF;
97 if dim < 3 || dim > 6 {
98 return Err("Unsupported matrix dimension");
99 }
100
101 let grid_size = dim;
102 let mut bits = vec![0u8; (grid_size * grid_size) as usize];
103
104 sample_grid(image, xsize, ysize, vertex, grid_size, pixel_format, patt_ratio, &mut bits)?;
105
106 debug!("ar_matrix_code_get_id: vertices v0=({:.1},{:.1}) v1=({:.1},{:.1}) v2=({:.1},{:.1}) v3=({:.1},{:.1})",
107 vertex[0][0], vertex[0][1], vertex[1][0], vertex[1][1],
108 vertex[2][0], vertex[2][1], vertex[3][0], vertex[3][1]);
109
110
111 let mut min_val = 255u8;
112 let mut max_val = 0u8;
113 for &b in &bits {
114 if b < min_val { min_val = b; }
115 if b > max_val { max_val = b; }
116 }
117
118 let mid_thresh = ((min_val as u32 + max_val as u32) / 2) as u8;
119 debug!("ar_matrix_code_get_id: min={}, max={}, thresh={}", min_val, max_val, mid_thresh);
120
121 if (max_val as i32 - min_val as i32) < 30 {
122 return Err("Low contrast in matrix grid");
123 }
124
125 let mut core_bits = vec![0u8; (grid_size * grid_size) as usize];
126 for i in 0..bits.len() {
127 core_bits[i] = if bits[i] < mid_thresh { 1 } else { 0 };
128 }
129
130 trace!("ar_matrix_code_get_id: grid_size={}, core_bits={:?}", grid_size, core_bits);
131 debug!("ar_matrix_code_get_id: core_bits={:?}", core_bits);
132
133 let size = dim;
139 let corners = [
140 0, (size - 1) * size, size * size - 1, size - 1, ];
145
146 let mut dir_code = [0u8; 4];
147 for i in 0..4 {
148 dir_code[i] = core_bits[corners[i] as usize];
149 }
150
151 let mut matched_dir = -1;
152 for i in 0..4 {
153 if dir_code[i] == 1 && dir_code[(i+1)%4] == 1 && dir_code[(i+2)%4] == 0 {
154 matched_dir = i as i32;
155 break;
156 }
157 }
158
159 if matched_dir == -1 {
160 return Err("Barcode locator pattern not found in grid corners");
161 }
162
163 let mut code_raw = 0u64;
166
167 if matched_dir == 0 {
168 for j in 0..size {
169 for i in 0..size {
170 if i == 0 && j == 0 { continue; }
171 if i == 0 && j == size - 1 { continue; }
172 if i == size - 1 && j == size - 1 { continue; }
173 code_raw <<= 1;
174 if core_bits[(j * size + i) as usize] == 1 { code_raw += 1; }
175 }
176 }
177 } else if matched_dir == 1 {
178 for i in 0..size {
179 for j in (0..size).rev() {
180 if i == 0 && j == size - 1 { continue; }
181 if i == size - 1 && j == size - 1 { continue; }
182 if i == size - 1 && j == 0 { continue; }
183 code_raw <<= 1;
184 if core_bits[(j * size + i) as usize] == 1 { code_raw += 1; }
185 }
186 }
187 } else if matched_dir == 2 {
188 for j in (0..size).rev() {
189 for i in (0..size).rev() {
190 if i == size - 1 && j == size - 1 { continue; }
191 if i == size - 1 && j == 0 { continue; }
192 if i == 0 && j == 0 { continue; }
193 code_raw <<= 1;
194 if core_bits[(j * size + i) as usize] == 1 { code_raw += 1; }
195 }
196 }
197 } else if matched_dir == 3 {
198 for i in (0..size).rev() {
199 for j in 0..size {
200 if i == size - 1 && j == 0 { continue; }
201 if i == 0 && j == 0 { continue; }
202 if i == 0 && j == size - 1 { continue; }
203 code_raw <<= 1;
204 if core_bits[(j * size + i) as usize] == 1 { code_raw += 1; }
205 }
206 }
207 }
208
209 debug!("ar_matrix_code_get_id: code_raw={}, dir={}", code_raw, matched_dir);
210
211 *dir = matched_dir;
212 *cf = 1.0;
213
214 if let Ok((decoded_id, err)) = decode_matrix_raw(code_raw, code_type) {
216 *id = decoded_id;
217 *error_corrected = err;
218 Ok(())
219 } else {
220 Err("Failed to decode extracted matrix code string")
221 }
222}
223
224
225pub fn ar_get_barcode_marker(
237 image: &[u8],
238 ar_handle: &mut ARHandle,
239 marker_info2: &mut ARMarkerInfo2
240) -> Result<ARMarkerInfo, &'static str> {
241 trace!("ar_get_barcode_marker: started for area={}, coord_num={}", marker_info2.area, marker_info2.coord_num);
242
243 let mut marker_info = ARMarkerInfo::default();
244 let mut id = -1;
245 let mut dir = -1;
246 let mut cf = 0.0;
247 let mut error_corrected = 0;
248
249 let mut resolved_vertex = [[0.0; 2]; 4];
251 let mut line = [[0.0; 3]; 4];
252
253 if ar_handle.ar_param_lt.is_null() {
254 return Err("arParamLT is null");
255 }
256 let param_lt = unsafe { &*ar_handle.ar_param_lt };
257
258 ar_get_line(
259 &marker_info2.x_coord,
260 &marker_info2.y_coord,
261 marker_info2.coord_num as usize,
262 &marker_info2.vertex,
263 ¶m_lt.param_ltf,
264 &mut line,
265 &mut resolved_vertex
266 )?;
267
268 ar_matrix_code_get_id(
269 image,
270 ar_handle.xsize,
271 ar_handle.ysize,
272 &resolved_vertex,
273 ar_handle.get_matrix_code_type(),
274 ar_handle.ar_pixel_format,
275 ar_handle.patt_ratio,
276 &mut id,
277 &mut dir,
278 &mut cf,
279 &mut error_corrected
280 )?;
281
282 marker_info.id_matrix = id;
283 marker_info.dir_matrix = dir;
284 marker_info.cf_matrix = cf;
285 marker_info.area = marker_info2.area;
286 marker_info.pos = marker_info2.pos;
287 marker_info.vertex = resolved_vertex;
288 marker_info.line = line;
289 marker_info.error_corrected = error_corrected;
290
291 Ok(marker_info)
292}
293
294
295fn sample_grid(
312 image: &[u8],
313 xsize: i32,
314 ysize: i32,
315 vertex: &[[ARdouble; 2]; 4],
316 grid_size: i32,
317 pixel_format: crate::types::ARPixelFormat,
318 patt_ratio: f64,
319 bits: &mut [u8],
320) -> Result<(), &'static str> {
321 let nc = match pixel_format {
322 crate::types::ARPixelFormat::MONO => 1,
323 crate::types::ARPixelFormat::RGB | crate::types::ARPixelFormat::BGR => 3,
324 crate::types::ARPixelFormat::RGBA | crate::types::ARPixelFormat::BGRA | crate::types::ARPixelFormat::ARGB => 4,
325 _ => return Err("Unsupported pixel format in sample_grid"),
326 };
327
328 let mut world = [[0.0; 2]; 4];
329 let mut para = [[0.0; 3]; 3];
330
331 world[0][0] = 100.0; world[0][1] = 100.0;
333 world[1][0] = 110.0; world[1][1] = 100.0;
334 world[2][0] = 110.0; world[2][1] = 110.0;
335 world[3][0] = 100.0; world[3][1] = 110.0;
336
337 crate::pattern::get_cpara(&world, vertex, &mut para)?;
338
339 let patt_ratio1 = (1.0 - patt_ratio) / 2.0 * 10.0;
340 let patt_ratio2 = patt_ratio * 10.0;
341
342 for y in 0..grid_size {
343 let yw = (100.0 + patt_ratio1) + patt_ratio2 * (y as f64 + 0.5) / grid_size as f64;
344 for x in 0..grid_size {
345 let xw = (100.0 + patt_ratio1) + patt_ratio2 * (x as f64 + 0.5) / grid_size as f64;
346
347 let d = para[2][0] * xw + para[2][1] * yw + para[2][2];
348 if d == 0.0 { continue; }
349
350 let xc = ((para[0][0] * xw + para[0][1] * yw + para[0][2]) / d) as i32;
351 let yc = ((para[1][0] * xw + para[1][1] * yw + para[1][2]) / d) as i32;
352
353 if xc >= 0 && xc < xsize && yc >= 0 && yc < ysize {
354 if y == 0 && (x == 0 || x == grid_size - 1) || y == grid_size - 1 && (x == 0 || x == grid_size - 1) {
355 trace!("sample_grid: grid({},{}) -> image({},{})", x, y, xc, yc);
356 }
357 let idx = ((yc * xsize + xc) * nc) as usize;
358 if idx < image.len() {
359 bits[(y * grid_size + x) as usize] = if nc >= 3 {
362 image[idx + 1]
365 } else {
366 image[idx]
367 };
368 }
369 }
370 }
371 }
372
373 Ok(())
374}
375
376
377
378#[allow(dead_code)]
387fn rotate_bits(bits: &[u8], dim: i32, dir: i32) -> Vec<u8> {
388 let mut rotated = vec![0u8; bits.len()];
389 for y in 0..dim {
390 for x in 0..dim {
391 let (nx, ny) = match dir {
392 0 => (x, y),
393 1 => (y, dim - 1 - x),
394 2 => (dim - 1 - x, dim - 1 - y),
395 3 => (dim - 1 - y, x),
396 _ => (x, y),
397 };
398 rotated[(ny * dim + nx) as usize] = bits[(y * dim + x) as usize];
399 }
400 }
401 rotated
402}
403
404fn decode_matrix_raw(code_raw: u64, code_type: ARMatrixCodeType) -> Result<(i32, i32), &'static str> {
416 match code_type {
417 ARMatrixCodeType::Code3x3 => {
418 Ok((code_raw as i32, 0))
421 },
422 ARMatrixCodeType::Code3x3Parity65 => {
423 let code = crate::bch::decode_parity65(code_raw)?;
424 Ok((code as i32, 0))
425 },
426 ARMatrixCodeType::Code3x3Hamming63 => {
427 let (code, err) = crate::bch::decode_hamming63(code_raw)?;
428 Ok((code as i32, err))
429 },
430 ARMatrixCodeType::Code4x4BCH1393 | ARMatrixCodeType::Code4x4BCH1355 |
431 ARMatrixCodeType::Code5x5BCH22125 | ARMatrixCodeType::Code5x5BCH2277 => {
432 let (code, err) = crate::bch::decode_bch(code_type, code_raw)?;
433 Ok((code as i32, err))
434 },
435 _ => {
436 Ok((code_raw as i32, 0))
438 }
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn test_rotate_bits() {
448 let dim = 3;
449 let bits = vec![
450 1, 0, 0,
451 1, 1, 0,
452 1, 0, 0,
453 ];
454
455 let rot1 = rotate_bits(&bits, dim, 1);
456 let expected1 = vec![
457 0, 0, 0,
458 0, 1, 0,
459 1, 1, 1,
460 ];
461 assert_eq!(rot1, expected1);
462 }
463}