bardecoder/
decoder.rs

1use anyhow::Error;
2use image::{GenericImageView, GrayImage, Rgba};
3
4use crate::decode::{Decode, QRDecoder, QRDecoderWithInfo};
5use crate::detect::{Detect, LineScan, Location};
6use crate::extract::{Extract, QRExtractor};
7use crate::prepare::{BlockedMean, Prepare};
8
9use crate::util::qr::{QRData, QRError, QRInfo, QRLocation};
10
11/// Struct to hold logic to do the entire decoding
12pub struct Decoder<IMG, PREPD, RESULT> {
13    prepare: Box<dyn Prepare<IMG, PREPD>>,
14    detect: Box<dyn Detect<PREPD>>,
15    qr: ExtractDecode<PREPD, QRLocation, QRData, RESULT, QRError>,
16}
17
18impl<IMG, PREPD, RESULT> Decoder<IMG, PREPD, RESULT> {
19    /// Do the actual decoding
20    ///
21    /// Logic is run in the following order:
22    /// * prepare
23    /// * detect
24    /// * per detected code the associated extract and decode functions
25    pub fn decode(&self, source: &IMG) -> Vec<Result<RESULT, Error>> {
26        let prepared = self.prepare.prepare(source);
27        let locations = self.detect.detect(&prepared);
28
29        if locations.is_empty() {
30            return vec![];
31        }
32
33        let mut all_decoded = vec![];
34
35        for location in locations {
36            match location {
37                Location::QR(qrloc) => {
38                    let extracted = self.qr.extract.extract(&prepared, qrloc);
39                    let decoded = self.qr.decode.decode(extracted);
40
41                    all_decoded.push(decoded.map_err(Error::from));
42                }
43            }
44        }
45
46        all_decoded
47    }
48}
49
50/// Create a default Decoder
51///
52/// It will use the following components:
53///
54/// * prepare: BlockedMean
55/// * detect: LineScan
56/// * extract: QRExtractor
57/// * decode: QRDecoder
58///
59/// This is meant to provide a good balance between speed and accuracy
60pub fn default_decoder<D>() -> Decoder<D, GrayImage, String> where D: GenericImageView<Pixel = Rgba<u8>> {
61    default_builder().build()
62}
63
64/// Create a default Decoder that also returns information about the decoded QR Code
65///
66/// It will use the following components:
67///
68/// * prepare: BlockedMean
69/// * detect: LineScan
70/// * extract: QRExtractor
71/// * decode: QRDecoderWithInfo
72///
73/// This is meant to provide a good balance between speed and accuracy
74pub fn default_decoder_with_info<D>() -> Decoder<D, GrayImage, (String, QRInfo)> where D: GenericImageView<Pixel = Rgba<u8>> {
75    default_builder_with_info().build()
76}
77
78/// Builder struct to create a Decoder
79///
80/// Required elements are:
81///
82/// * Prepare
83/// * Detect
84/// * Extract
85/// * Decode
86pub struct DecoderBuilder<IMG, PREPD, RESULT> {
87    prepare: Option<Box<dyn Prepare<IMG, PREPD>>>,
88    detect: Option<Box<dyn Detect<PREPD>>>,
89    qr: Option<ExtractDecode<PREPD, QRLocation, QRData, RESULT, QRError>>,
90}
91
92impl<IMG, PREPD, RESULT> DecoderBuilder<IMG, PREPD, RESULT> {
93    /// Constructor; all fields initialized as None
94    pub fn new() -> DecoderBuilder<IMG, PREPD, RESULT> {
95        DecoderBuilder {
96            prepare: None,
97            detect: None,
98            qr: None,
99        }
100    }
101
102    /// Set the prepare implementation for this Decoder
103    pub fn prepare(
104        &mut self,
105        prepare: Box<dyn Prepare<IMG, PREPD>>,
106    ) -> &mut DecoderBuilder<IMG, PREPD, RESULT> {
107        self.prepare = Some(prepare);
108        self
109    }
110
111    /// Set the detect implementation for this Decoder
112    pub fn detect(
113        &mut self,
114        detect: Box<dyn Detect<PREPD>>,
115    ) -> &mut DecoderBuilder<IMG, PREPD, RESULT> {
116        self.detect = Some(detect);
117        self
118    }
119
120    /// Set the extact and decode implementations for this Decoder for QR codes
121    pub fn qr(
122        &mut self,
123        extract: Box<dyn Extract<PREPD, QRLocation, QRData, QRError>>,
124        decode: Box<dyn Decode<QRData, RESULT, QRError>>,
125    ) -> &mut DecoderBuilder<IMG, PREPD, RESULT> {
126        self.qr = Some(ExtractDecode { extract, decode });
127        self
128    }
129
130    /// Build actual Decoder
131    ///
132    /// # Panics
133    ///
134    /// Will panic if any of the required components are missing
135    pub fn build(self) -> Decoder<IMG, PREPD, RESULT> {
136        if self.prepare.is_none() {
137            panic!("Cannot build Decoder without Prepare component");
138        }
139
140        if self.detect.is_none() {
141            panic!("Cannot build Decoder without Detect component");
142        }
143
144        Decoder {
145            prepare: self.prepare.unwrap(),
146            detect: self.detect.unwrap(),
147            qr: self.qr.unwrap(),
148        }
149    }
150}
151
152/// Create a default DecoderBuilder
153///
154/// It will use the following components:
155///
156/// * prepare: BlockedMean
157/// * locate: LineScan
158/// * extract: QRExtractor
159/// * decode: QRDecoder
160///
161/// The builder can then be customised before creating the Decoder
162pub fn default_builder<D>() -> DecoderBuilder<D, GrayImage, String> where D: GenericImageView<Pixel = Rgba<u8>> {
163    let mut db = DecoderBuilder::new();
164
165    db.prepare(Box::new(BlockedMean::new(5, 7)));
166    db.detect(Box::new(LineScan::new()));
167    db.qr(Box::new(QRExtractor::new()), Box::new(QRDecoder::new()));
168
169    db
170}
171
172/// Create a default DecoderBuilder that also returns information about the decoded QR Code
173///
174/// It will use the following components:
175///
176/// * prepare: BlockedMean
177/// * locate: LineScan
178/// * extract: QRExtractor
179/// * decode: QRDecoderWithInfo
180///
181/// The builder can then be customised before creating the Decoder
182pub fn default_builder_with_info<D>() -> DecoderBuilder<D, GrayImage, (String, QRInfo)> where D: GenericImageView<Pixel = Rgba<u8>> {
183    let mut db = DecoderBuilder::new();
184
185    db.prepare(Box::new(BlockedMean::new(5, 7)));
186    db.detect(Box::new(LineScan::new()));
187    db.qr(
188        Box::new(QRExtractor::new()),
189        Box::new(QRDecoderWithInfo::new()),
190    );
191
192    db
193}
194
195struct ExtractDecode<PREPD, LOC, DATA, RESULT, ERROR> {
196    extract: Box<dyn Extract<PREPD, LOC, DATA, ERROR>>,
197    decode: Box<dyn Decode<DATA, RESULT, ERROR>>,
198}