rxing/datamatrix/detector/
datamatrix_detector.rs1use crate::{
18 Exceptions, Point,
19 common::{
20 BitMatrix, DefaultGridSampler, GridSampler, Quadrilateral, Result,
21 detector::WhiteRectangleDetector,
22 },
23 point,
24};
25
26use super::DatamatrixDetectorResult;
27
28pub struct Detector<'a> {
35 image: &'a BitMatrix,
36 rectangleDetector: WhiteRectangleDetector<'a>,
37}
38impl<'a> Detector<'_> {
39 pub fn new(image: &'a BitMatrix) -> Result<Detector<'a>> {
40 Ok(Detector {
41 rectangleDetector: WhiteRectangleDetector::new_from_image(image)?,
42 image,
43 })
44 }
45
46 pub fn detect(&self) -> Result<DatamatrixDetectorResult> {
53 let cornerPoints = self.rectangleDetector.detect()?;
54
55 let mut points = self.detectSolid1(cornerPoints);
56 points = self.detectSolid2(points);
57 if let Some(point) = self.correctTopRight(&points) {
58 points[3] = point;
59 } else {
60 return Err(Exceptions::not_found_with("point 4 unfound"));
61 }
62 points = self.shiftToModuleCenter(points);
67
68 let topLeft = points[0];
69 let bottomLeft = points[1];
70 let bottomRight = points[2];
71 let topRight = points[3];
72
73 let mut dimensionTop = self.transitionsBetween(topLeft, topRight) + 1;
74 let mut dimensionRight = self.transitionsBetween(bottomRight, topRight) + 1;
75 if (dimensionTop & 0x01) == 1 {
76 dimensionTop += 1;
77 }
78 if (dimensionRight & 0x01) == 1 {
79 dimensionRight += 1;
80 }
81
82 if 4 * dimensionTop < 6 * dimensionRight && 4 * dimensionRight < 6 * dimensionTop {
83 dimensionTop = dimensionTop.max(dimensionRight);
85 dimensionRight = dimensionTop.max(dimensionRight);
86 }
87
88 let bits = Self::sampleGrid(
89 self.image,
90 topLeft,
91 bottomLeft,
92 bottomRight,
93 topRight,
94 dimensionTop,
95 dimensionRight,
96 )?;
97
98 Ok(DatamatrixDetectorResult::new(
99 bits,
100 vec![topLeft, bottomLeft, bottomRight, topRight],
101 ))
102 }
103
104 #[inline]
105 fn shiftPoint(p: Point, to: Point, div: u32) -> Point {
106 let x = (to.x - p.x) / (div as f32 + 1.0);
107 let y = (to.y - p.y) / (div as f32 + 1.0);
108 point(p.x + x, p.y + y)
109 }
110
111 #[inline]
112 fn moveAway(p: Point, fromX: f32, fromY: f32) -> Point {
113 let mut x = p.x;
114 let mut y = p.y;
115
116 if x < fromX {
117 x -= 1.0;
118 } else {
119 x += 1.0;
120 }
121
122 if y < fromY {
123 y -= 1.0;
124 } else {
125 y += 1.0;
126 }
127
128 point(x, y)
129 }
130
131 fn detectSolid1(&self, cornerPoints: [Point; 4]) -> [Point; 4] {
135 let pointA = cornerPoints[0];
138 let pointB = cornerPoints[1];
139 let pointC = cornerPoints[3];
140 let pointD = cornerPoints[2];
141
142 let trAB = self.transitionsBetween(pointA, pointB);
143 let trBC = self.transitionsBetween(pointB, pointC);
144 let trCD = self.transitionsBetween(pointC, pointD);
145 let trDA = self.transitionsBetween(pointD, pointA);
146
147 let mut min = trAB;
151 let mut points = [pointD, pointA, pointB, pointC];
152 if min > trBC {
153 min = trBC;
154 points[0] = pointA;
155 points[1] = pointB;
156 points[2] = pointC;
157 points[3] = pointD;
158 }
159 if min > trCD {
160 min = trCD;
161 points[0] = pointB;
162 points[1] = pointC;
163 points[2] = pointD;
164 points[3] = pointA;
165 }
166 if min > trDA {
167 points[0] = pointC;
168 points[1] = pointD;
169 points[2] = pointA;
170 points[3] = pointB;
171 }
172
173 points
174 }
175
176 fn detectSolid2(&self, points: [Point; 4]) -> [Point; 4] {
180 let [pointA, pointB, pointC, pointD] = points;
184
185 let tr = self.transitionsBetween(pointA, pointD);
188 let pointBs = Self::shiftPoint(pointB, pointC, (tr + 1) * 4);
189 let pointCs = Self::shiftPoint(pointC, pointB, (tr + 1) * 4);
190 let trBA = self.transitionsBetween(pointBs, pointA);
191 let trCD = self.transitionsBetween(pointCs, pointD);
192
193 if trBA < trCD {
197 [pointA, pointB, pointC, pointD]
199 } else {
204 [pointB, pointC, pointD, pointA]
206 }
211 }
212
213 fn correctTopRight(&self, points: &[Point; 4]) -> Option<Point> {
217 let pointA = points[0];
221 let pointB = points[1];
222 let pointC = points[2];
223 let pointD = points[3];
224
225 let mut trTop = self.transitionsBetween(pointA, pointD);
227 let mut trRight = self.transitionsBetween(pointB, pointD);
228 let pointAs = Self::shiftPoint(pointA, pointB, (trRight + 1) * 4);
229 let pointCs = Self::shiftPoint(pointC, pointB, (trTop + 1) * 4);
230
231 trTop = self.transitionsBetween(pointAs, pointD);
232 trRight = self.transitionsBetween(pointCs, pointD);
233
234 let candidate1 = point(
235 pointD.x + (pointC.x - pointB.x) / (trTop as f32 + 1.0),
236 pointD.y + (pointC.y - pointB.y) / (trTop as f32 + 1.0),
237 );
238 let candidate2 = point(
239 pointD.x + (pointA.x - pointB.x) / (trRight as f32 + 1.0),
240 pointD.y + (pointA.y - pointB.y) / (trRight as f32 + 1.0),
241 );
242
243 if !self.isValid(candidate1) {
244 if self.isValid(candidate2) {
245 return Some(candidate2);
246 }
247 return None;
248 }
249 if !self.isValid(candidate2) {
250 return Some(candidate1);
251 }
252
253 let sumc1 = self.transitionsBetween(pointAs, candidate1)
254 + self.transitionsBetween(pointCs, candidate1);
255 let sumc2 = self.transitionsBetween(pointAs, candidate2)
256 + self.transitionsBetween(pointCs, candidate2);
257
258 if sumc1 > sumc2 {
259 Some(candidate1)
260 } else {
261 Some(candidate2)
262 }
263 }
264
265 fn shiftToModuleCenter(&self, points: [Point; 4]) -> [Point; 4] {
269 let [mut pointA, mut pointB, mut pointC, mut pointD] = points;
273
274 let mut dimH = self.transitionsBetween(pointA, pointD) + 1;
276 let mut dimV = self.transitionsBetween(pointC, pointD) + 1;
277
278 let mut pointAs = Self::shiftPoint(pointA, pointB, dimV * 4);
280 let mut pointCs = Self::shiftPoint(pointC, pointB, dimH * 4);
281
282 dimH = self.transitionsBetween(pointAs, pointD) + 1;
284 dimV = self.transitionsBetween(pointCs, pointD) + 1;
285 if (dimH & 0x01) == 1 {
286 dimH += 1;
287 }
288 if (dimV & 0x01) == 1 {
289 dimV += 1;
290 }
291
292 let centerX = (pointA.x + pointB.x + pointC.x + pointD.x) / 4.0;
295 let centerY = (pointA.y + pointB.y + pointC.y + pointD.y) / 4.0;
296 pointA = Self::moveAway(pointA, centerX, centerY);
297 pointB = Self::moveAway(pointB, centerX, centerY);
298 pointC = Self::moveAway(pointC, centerX, centerY);
299 pointD = Self::moveAway(pointD, centerX, centerY);
300
301 let mut pointBs;
302 let mut pointDs;
303
304 pointAs = Self::shiftPoint(pointA, pointB, dimV * 4);
306 pointAs = Self::shiftPoint(pointAs, pointD, dimH * 4);
307 pointBs = Self::shiftPoint(pointB, pointA, dimV * 4);
308 pointBs = Self::shiftPoint(pointBs, pointC, dimH * 4);
309 pointCs = Self::shiftPoint(pointC, pointD, dimV * 4);
310 pointCs = Self::shiftPoint(pointCs, pointB, dimH * 4);
311 pointDs = Self::shiftPoint(pointD, pointC, dimV * 4);
312 pointDs = Self::shiftPoint(pointDs, pointA, dimH * 4);
313
314 [pointAs, pointBs, pointCs, pointDs]
315 }
316
317 #[inline]
318 fn isValid(&self, p: Point) -> bool {
319 p.x >= 0.0
320 && p.x <= self.image.getWidth() as f32 - 1.0
321 && p.y > 0.0
322 && p.y <= self.image.getHeight() as f32 - 1.0
323 }
324
325 fn sampleGrid(
326 image: &BitMatrix,
327 topLeft: Point,
328 bottomLeft: Point,
329 bottomRight: Point,
330 topRight: Point,
331 dimensionX: u32,
332 dimensionY: u32,
333 ) -> Result<BitMatrix> {
334 let sampler = DefaultGridSampler;
335
336 let dst = Quadrilateral::new(
337 point(0.5, 0.5),
338 point(dimensionX as f32 - 0.5, 0.5),
339 point(dimensionX as f32 - 0.5, dimensionY as f32 - 0.5),
340 point(0.5, dimensionY as f32 - 0.5),
341 );
342 let src = Quadrilateral::new(topRight, topLeft, bottomRight, bottomLeft);
343
344 let (res, _) = sampler.sample_grid_detailed(image, dimensionX, dimensionY, dst, src)?;
345 Ok(res)
346 }
347
348 fn transitionsBetween(&self, from: Point, to: Point) -> u32 {
352 let mut fromX = from.x.floor() as i32;
354 let mut fromY = from.y.floor() as i32;
355 let mut toX = to.x.floor() as i32;
356 let mut toY = (self.image.getHeight() - 1).min(to.y.floor() as u32) as i32;
357
358 let steep = (toY - fromY).abs() > (toX - fromX).abs();
359 if steep {
360 std::mem::swap(&mut fromX, &mut fromY);
361 std::mem::swap(&mut toX, &mut toY);
362 }
363
364 let dx = (toX - fromX).abs();
365 let dy = (toY - fromY).abs();
366 let mut error = -dx / 2;
367 let ystep = if fromY < toY { 1 } else { -1 };
368 let xstep = if fromX < toX { 1 } else { -1 };
369 let mut transitions = 0;
370 let mut inBlack = self.image.get(
371 if steep { fromY as u32 } else { fromX as u32 },
372 if steep { fromX as u32 } else { fromY as u32 },
373 );
374 let mut x = fromX;
375 let mut y = fromY;
376 while x != toX {
377 let isBlack = self.image.get(
379 if steep { y as u32 } else { x as u32 },
380 if steep { x as u32 } else { y as u32 },
381 );
382 if isBlack != inBlack {
383 transitions += 1;
384 inBlack = isBlack;
385 }
386 error += dy;
387 if error > 0 {
388 if y == toY {
389 break;
390 }
391 y += ystep;
392 error -= dx;
393 }
394
395 x += xstep;
396 }
397 transitions
398 }
399}