Skip to main content

rxing/multi/
generic_multiple_barcode_reader.rs

1/*
2 * Copyright 2009 ZXing authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::{
18    Binarizer, BinaryBitmap, DecodeHints, Exceptions, Point, RXingResult, Reader,
19    common::{Quadrilateral, Result},
20    point,
21};
22
23use super::MultipleBarcodeReader;
24
25/**
26 * <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
27 * After one barcode is found, the areas left, above, right and below the barcode's
28 * {@link Point}s are scanned, recursively.</p>
29 *
30 * <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
31 * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
32 * detecting any one of them.</p>
33 *
34 * <p>That is, instead of passing a {@link Reader} a caller might pass
35 * {@code new ByQuadrantReader(reader)}.</p>
36 *
37 * @author Sean Owen
38 */
39#[derive(Default)]
40pub struct GenericMultipleBarcodeReader<T: Reader>(T);
41
42impl<T: Reader> MultipleBarcodeReader for GenericMultipleBarcodeReader<T> {
43    fn decode_multiple<B: Binarizer>(
44        &mut self,
45        image: &mut BinaryBitmap<B>,
46    ) -> Result<Vec<RXingResult>> {
47        self.decode_multiple_with_hints(image, &DecodeHints::default())
48    }
49
50    fn decode_multiple_with_hints<B: Binarizer>(
51        &mut self,
52        image: &mut BinaryBitmap<B>,
53        hints: &DecodeHints,
54    ) -> Result<Vec<RXingResult>> {
55        let mut results = Vec::new();
56        self.do_decode_multiple(image, hints, &mut results, 0, 0, 0);
57
58        let unique_results: Vec<RXingResult> = results
59            .iter()
60            .enumerate()
61            .filter(|(i, r)| {
62                let already_found = if r.getPoints().len() >= 4 {
63                    let q1 = Quadrilateral::new(
64                        r.getPoints()[0],
65                        r.getPoints()[1],
66                        r.getPoints()[2],
67                        r.getPoints()[3],
68                    );
69                    results.iter().skip(*i + 1).any(|e| {
70                        if e.getPoints().len() >= 4 {
71                            let q2 = Quadrilateral::new(
72                                e.getPoints()[0],
73                                e.getPoints()[1],
74                                e.getPoints()[2],
75                                e.getPoints()[3],
76                            );
77                            Quadrilateral::have_intersecting_bounding_boxes(&q1, &q2)
78                        } else {
79                            e.getPoints().iter().any(|p| q1.is_inside(*p))
80                        }
81                    })
82                } else {
83                    results.iter().skip(*i + 1).any(|e| {
84                        if e.getPoints().len() >= 4 {
85                            let q2 = Quadrilateral::new(
86                                e.getPoints()[0],
87                                e.getPoints()[1],
88                                e.getPoints()[2],
89                                e.getPoints()[3],
90                            );
91                            e.getPoints().iter().any(|p| q2.is_inside(*p))
92                        } else {
93                            e.getText() == r.getText()
94                                && e.getBarcodeFormat() == r.getBarcodeFormat()
95                        }
96                    })
97                };
98                !already_found
99            })
100            .map(|(_, r)| r)
101            .cloned()
102            .collect();
103
104        if unique_results.is_empty() {
105            return Err(Exceptions::NOT_FOUND);
106        }
107        Ok(unique_results)
108    }
109}
110impl<T: Reader> GenericMultipleBarcodeReader<T> {
111    const MIN_DIMENSION_TO_RECUR: f32 = 100.0;
112    const MAX_DEPTH: u32 = 4;
113
114    pub fn new(delegate: T) -> Self {
115        Self(delegate)
116    }
117
118    fn do_decode_multiple<B: Binarizer>(
119        &mut self,
120        image: &mut BinaryBitmap<B>,
121        hints: &DecodeHints,
122        results: &mut Vec<RXingResult>,
123        xOffset: u32,
124        yOffset: u32,
125        currentDepth: u32,
126    ) {
127        if currentDepth > Self::MAX_DEPTH {
128            return;
129        }
130
131        // let result;
132        let Ok(result) = self.0.decode_with_hints(image, hints) else {
133            return;
134        };
135
136        let resultPoints = result.getPoints().to_vec();
137
138        let possible_new_result = Self::translatePoints(result, xOffset, yOffset);
139
140        results.push(possible_new_result);
141
142        if resultPoints.is_empty() {
143            return;
144        }
145
146        let width = image.get_width();
147        let height = image.get_height();
148        let mut minX: f32 = width as f32;
149        let mut minY: f32 = height as f32;
150        let mut maxX: f32 = 0.0;
151        let mut maxY: f32 = 0.0;
152        for point in resultPoints.into_iter() {
153            let x = point.x;
154            let y = point.y;
155
156            minX = f32::min(x, minX);
157            minY = f32::min(y, minY);
158            maxX = f32::max(x, maxX);
159            maxY = f32::max(y, maxY);
160        }
161
162        // Decode left of barcode
163        if minX > Self::MIN_DIMENSION_TO_RECUR {
164            self.do_decode_multiple(
165                &mut image.crop(0, 0, minX as usize, height),
166                hints,
167                results,
168                xOffset,
169                yOffset,
170                currentDepth + 1,
171            );
172        }
173        // Decode above barcode
174        if minY > Self::MIN_DIMENSION_TO_RECUR {
175            self.do_decode_multiple(
176                &mut image.crop(0, 0, width, minY as usize),
177                hints,
178                results,
179                xOffset,
180                yOffset,
181                currentDepth + 1,
182            );
183        }
184        // Decode right of barcode
185        if maxX < (width as f32) - Self::MIN_DIMENSION_TO_RECUR {
186            self.do_decode_multiple(
187                &mut image.crop(maxX as usize, 0, width - maxX as usize, height),
188                hints,
189                results,
190                xOffset + maxX as u32,
191                yOffset,
192                currentDepth + 1,
193            );
194        }
195        // Decode below barcode
196        if maxY < (height as f32) - Self::MIN_DIMENSION_TO_RECUR {
197            self.do_decode_multiple(
198                &mut image.crop(0, maxY as usize, width, height - maxY as usize),
199                hints,
200                results,
201                xOffset,
202                yOffset + maxY as u32,
203                currentDepth + 1,
204            );
205        }
206    }
207
208    fn translatePoints(result: RXingResult, xOffset: u32, yOffset: u32) -> RXingResult {
209        let oldPoints = result.getPoints();
210        if oldPoints.is_empty() {
211            return result;
212        }
213
214        let newPoints: Vec<Point> = oldPoints
215            .iter()
216            .map(|oldPoint| point(oldPoint.x + xOffset as f32, oldPoint.y + yOffset as f32))
217            .collect();
218
219        let mut newRXingResult = RXingResult::new_complex(
220            result.getText(),
221            result.getRawBytes().to_vec(),
222            result.getNumBits(),
223            newPoints,
224            *result.getBarcodeFormat(),
225            result.getTimestamp(),
226        );
227        newRXingResult.putAllMetadata(result.getRXingResultMetadata().clone());
228
229        newRXingResult
230    }
231}