mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-28 05:12:34 +00:00
multi migrated errors
This commit is contained in:
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// package com::google::zxing::multi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class attempts to decode a barcode from an image, not by scanning the whole image,
|
|
||||||
* but by scanning subsets of the image. This is important when there may be multiple barcodes in
|
|
||||||
* an image, and detecting a barcode may find parts of multiple barcode and fail to decode
|
|
||||||
* (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center
|
|
||||||
* 'quadrant' to cover the case where a barcode is found in the center.
|
|
||||||
*
|
|
||||||
* @see GenericMultipleBarcodeReader
|
|
||||||
*/
|
|
||||||
#[derive(Reader)]
|
|
||||||
pub struct ByQuadrantReader {
|
|
||||||
|
|
||||||
let delegate: Reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ByQuadrantReader {
|
|
||||||
|
|
||||||
pub fn new( delegate: &Reader) -> ByQuadrantReader {
|
|
||||||
let .delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self, image: &BinaryBitmap) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
|
||||||
return Ok(self.decode(image, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
|
||||||
let width: i32 = image.get_width();
|
|
||||||
let height: i32 = image.get_height();
|
|
||||||
let half_width: i32 = width / 2;
|
|
||||||
let half_height: i32 = height / 2;
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
// No need to call makeAbsolute as results will be relative to original top left here
|
|
||||||
return Ok(self.delegate.decode(&image.crop(0, 0, half_width, half_height), &hints));
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( re: &NotFoundException) {
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
let result: Result = self.delegate.decode(&image.crop(half_width, 0, half_width, half_height), &hints);
|
|
||||||
::make_absolute(&result.get_result_points(), half_width, 0);
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( re: &NotFoundException) {
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
let result: Result = self.delegate.decode(&image.crop(0, half_height, half_width, half_height), &hints);
|
|
||||||
::make_absolute(&result.get_result_points(), 0, half_height);
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( re: &NotFoundException) {
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
let result: Result = self.delegate.decode(&image.crop(half_width, half_height, half_width, half_height), &hints);
|
|
||||||
::make_absolute(&result.get_result_points(), half_width, half_height);
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( re: &NotFoundException) {
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
let quarter_width: i32 = half_width / 2;
|
|
||||||
let quarter_height: i32 = half_height / 2;
|
|
||||||
let center: BinaryBitmap = image.crop(quarter_width, quarter_height, half_width, half_height);
|
|
||||||
let result: Result = self.delegate.decode(center, &hints);
|
|
||||||
::make_absolute(&result.get_result_points(), quarter_width, quarter_height);
|
|
||||||
return Ok(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&self) {
|
|
||||||
self.delegate.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_absolute( points: &Vec<ResultPoint>, left_offset: i32, top_offset: i32) {
|
|
||||||
if points != null {
|
|
||||||
{
|
|
||||||
let mut i: i32 = 0;
|
|
||||||
while i < points.len() {
|
|
||||||
{
|
|
||||||
let relative: ResultPoint = points[i];
|
|
||||||
if relative != null {
|
|
||||||
points[i] = ResultPoint::new(relative.get_x() + left_offset, relative.get_y() + top_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// package com::google::zxing::multi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
|
|
||||||
* After one barcode is found, the areas left, above, right and below the barcode's
|
|
||||||
* {@link ResultPoint}s are scanned, recursively.</p>
|
|
||||||
*
|
|
||||||
* <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
|
|
||||||
* 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
|
|
||||||
* detecting any one of them.</p>
|
|
||||||
*
|
|
||||||
* <p>That is, instead of passing a {@link Reader} a caller might pass
|
|
||||||
* {@code new ByQuadrantReader(reader)}.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
*/
|
|
||||||
|
|
||||||
const MIN_DIMENSION_TO_RECUR: i32 = 100;
|
|
||||||
|
|
||||||
const MAX_DEPTH: i32 = 4;
|
|
||||||
|
|
||||||
const EMPTY_RESULT_ARRAY: [Option<Result>; 0] = [None; 0];
|
|
||||||
#[derive(MultipleBarcodeReader)]
|
|
||||||
pub struct GenericMultipleBarcodeReader {
|
|
||||||
|
|
||||||
let delegate: Reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenericMultipleBarcodeReader {
|
|
||||||
|
|
||||||
pub fn new( delegate: &Reader) -> GenericMultipleBarcodeReader {
|
|
||||||
let .delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
|
||||||
return Ok(self.decode_multiple(image, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
|
||||||
let results: List<Result> = ArrayList<>::new();
|
|
||||||
self.do_decode_multiple(image, &hints, &results, 0, 0, 0);
|
|
||||||
if results.is_empty() {
|
|
||||||
throw NotFoundException::get_not_found_instance();
|
|
||||||
}
|
|
||||||
return Ok(results.to_array(EMPTY_RESULT_ARRAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>, results: &List<Result>, x_offset: i32, y_offset: i32, current_depth: i32) {
|
|
||||||
if current_depth > MAX_DEPTH {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let mut result: Result;
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
result = self.delegate.decode(image, &hints);
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( ignored: &ReaderException) {
|
|
||||||
return;
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
let already_found: bool = false;
|
|
||||||
for let existing_result: Result in results {
|
|
||||||
if existing_result.get_text().equals(&result.get_text()) {
|
|
||||||
already_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !already_found {
|
|
||||||
results.add(&::translate_result_points(result, x_offset, y_offset));
|
|
||||||
}
|
|
||||||
let result_points: Vec<ResultPoint> = result.get_result_points();
|
|
||||||
if result_points == null || result_points.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let width: i32 = image.get_width();
|
|
||||||
let height: i32 = image.get_height();
|
|
||||||
let min_x: f32 = width;
|
|
||||||
let min_y: f32 = height;
|
|
||||||
let max_x: f32 = 0.0f;
|
|
||||||
let max_y: f32 = 0.0f;
|
|
||||||
for let point: ResultPoint in result_points {
|
|
||||||
if point == null {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let x: f32 = point.get_x();
|
|
||||||
let y: f32 = point.get_y();
|
|
||||||
if x < min_x {
|
|
||||||
min_x = x;
|
|
||||||
}
|
|
||||||
if y < min_y {
|
|
||||||
min_y = y;
|
|
||||||
}
|
|
||||||
if x > max_x {
|
|
||||||
max_x = x;
|
|
||||||
}
|
|
||||||
if y > max_y {
|
|
||||||
max_y = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Decode left of barcode
|
|
||||||
if min_x > MIN_DIMENSION_TO_RECUR {
|
|
||||||
self.do_decode_multiple(&image.crop(0, 0, min_x as i32, height), &hints, &results, x_offset, y_offset, current_depth + 1);
|
|
||||||
}
|
|
||||||
// Decode above barcode
|
|
||||||
if min_y > MIN_DIMENSION_TO_RECUR {
|
|
||||||
self.do_decode_multiple(&image.crop(0, 0, width, min_y as i32), &hints, &results, x_offset, y_offset, current_depth + 1);
|
|
||||||
}
|
|
||||||
// Decode right of barcode
|
|
||||||
if max_x < width - MIN_DIMENSION_TO_RECUR {
|
|
||||||
self.do_decode_multiple(&image.crop(max_x as i32, 0, width - max_x as i32, height), &hints, &results, x_offset + max_x as i32, y_offset, current_depth + 1);
|
|
||||||
}
|
|
||||||
// Decode below barcode
|
|
||||||
if max_y < height - MIN_DIMENSION_TO_RECUR {
|
|
||||||
self.do_decode_multiple(&image.crop(0, max_y as i32, width, height - max_y as i32), &hints, &results, x_offset, y_offset + max_y as i32, current_depth + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_result_points( result: &Result, x_offset: i32, y_offset: i32) -> Result {
|
|
||||||
let old_result_points: Vec<ResultPoint> = result.get_result_points();
|
|
||||||
if old_result_points == null {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
let new_result_points: [Option<ResultPoint>; old_result_points.len()] = [None; old_result_points.len()];
|
|
||||||
{
|
|
||||||
let mut i: i32 = 0;
|
|
||||||
while i < old_result_points.len() {
|
|
||||||
{
|
|
||||||
let old_point: ResultPoint = old_result_points[i];
|
|
||||||
if old_point != null {
|
|
||||||
new_result_points[i] = ResultPoint::new(old_point.get_x() + x_offset, old_point.get_y() + y_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_result: Result = Result::new(&result.get_text(), &result.get_raw_bytes(), &result.get_num_bits(), new_result_points, &result.get_barcode_format(), &result.get_timestamp());
|
|
||||||
new_result.put_all_metadata(&result.get_result_metadata());
|
|
||||||
return new_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// package com::google::zxing::multi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of this interface attempt to read several barcodes from one image.
|
|
||||||
*
|
|
||||||
* @see com.google.zxing.Reader
|
|
||||||
* @author Sean Owen
|
|
||||||
*/
|
|
||||||
pub trait MultipleBarcodeReader {
|
|
||||||
|
|
||||||
fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> ;
|
|
||||||
|
|
||||||
fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// package com::google::zxing::multi::qrcode::detector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
|
|
||||||
* is rotated or skewed, or partially obscured.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
* @author Hannes Erven
|
|
||||||
*/
|
|
||||||
|
|
||||||
const EMPTY_DETECTOR_RESULTS: [Option<DetectorResult>; 0] = [None; 0];
|
|
||||||
pub struct MultiDetector {
|
|
||||||
super: Detector;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MultiDetector {
|
|
||||||
|
|
||||||
pub fn new( image: &BitMatrix) -> MultiDetector {
|
|
||||||
super(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn detect_multi(&self, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<DetectorResult>, Rc<Exception>> {
|
|
||||||
let image: BitMatrix = get_image();
|
|
||||||
let result_point_callback: ResultPointCallback = if hints == null { null } else { hints.get(DecodeHintType::NEED_RESULT_POINT_CALLBACK) as ResultPointCallback };
|
|
||||||
let finder: MultiFinderPatternFinder = MultiFinderPatternFinder::new(image, result_point_callback);
|
|
||||||
let infos: Vec<FinderPatternInfo> = finder.find_multi(&hints);
|
|
||||||
if infos.len() == 0 {
|
|
||||||
throw NotFoundException::get_not_found_instance();
|
|
||||||
}
|
|
||||||
let result: List<DetectorResult> = ArrayList<>::new();
|
|
||||||
for let info: FinderPatternInfo in infos {
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
result.add(&process_finder_pattern_info(info));
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( e: &ReaderException) {
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if result.is_empty() {
|
|
||||||
return Ok(EMPTY_DETECTOR_RESULTS);
|
|
||||||
} else {
|
|
||||||
return Ok(result.to_array(EMPTY_DETECTOR_RESULTS));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// package com::google::zxing::multi::qrcode::detector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
|
|
||||||
* markers at three corners of a QR Code.</p>
|
|
||||||
*
|
|
||||||
* <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
|
|
||||||
*
|
|
||||||
* <p>In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
|
|
||||||
* QR code locations in the image.</p>
|
|
||||||
*
|
|
||||||
* <p>Use the TRY_HARDER hint to ask for a more thorough detection.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
* @author Hannes Erven
|
|
||||||
*/
|
|
||||||
|
|
||||||
const EMPTY_RESULT_ARRAY: [Option<FinderPatternInfo>; 0] = [None; 0];
|
|
||||||
|
|
||||||
const EMPTY_FP_ARRAY: [Option<FinderPattern>; 0] = [None; 0];
|
|
||||||
|
|
||||||
const EMPTY_FP_2D_ARRAY: [Option<FinderPattern>; 0] = [None; 0];
|
|
||||||
|
|
||||||
// TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
|
|
||||||
// since it limits the number of regions to decode
|
|
||||||
// max. legal count of modules per QR code edge (177)
|
|
||||||
const MAX_MODULE_COUNT_PER_EDGE: f32 = 180;
|
|
||||||
|
|
||||||
// min. legal count per modules per QR code edge (11)
|
|
||||||
const MIN_MODULE_COUNT_PER_EDGE: f32 = 9;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
|
||||||
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
|
|
||||||
* estimated modules sizes.
|
|
||||||
*/
|
|
||||||
const DIFF_MODSIZE_CUTOFF_PERCENT: f32 = 0.05f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
|
||||||
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
|
|
||||||
* estimated modules sizes.
|
|
||||||
*/
|
|
||||||
const DIFF_MODSIZE_CUTOFF: f32 = 0.5f;
|
|
||||||
pub struct MultiFinderPatternFinder {
|
|
||||||
super: FinderPatternFinder;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MultiFinderPatternFinder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A comparator that orders FinderPatterns by their estimated module size.
|
|
||||||
*/
|
|
||||||
#[derive(Comparator<FinderPattern>, Serializable)]
|
|
||||||
struct ModuleSizeComparator {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleSizeComparator {
|
|
||||||
|
|
||||||
pub fn compare(&self, center1: &FinderPattern, center2: &FinderPattern) -> i32 {
|
|
||||||
let value: f32 = center2.get_estimated_module_size() - center1.get_estimated_module_size();
|
|
||||||
return if value < 0.0 { -1 } else { if value > 0.0 { 1 } else { 0 } };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn new( image: &BitMatrix, result_point_callback: &ResultPointCallback) -> MultiFinderPatternFinder {
|
|
||||||
super(image, result_point_callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
|
|
||||||
* those that have been detected at least 2 times, and whose module
|
|
||||||
* size differs from the average among those patterns the least
|
|
||||||
* @throws NotFoundException if 3 such finder patterns do not exist
|
|
||||||
*/
|
|
||||||
fn select_multiple_best_patterns(&self) -> /* throws NotFoundException */Result<Vec<Vec<FinderPattern>>, Rc<Exception>> {
|
|
||||||
let possible_centers: List<FinderPattern> = ArrayList<>::new();
|
|
||||||
for let fp: FinderPattern in get_possible_centers() {
|
|
||||||
if fp.get_count() >= 2 {
|
|
||||||
possible_centers.add(fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let size: i32 = possible_centers.size();
|
|
||||||
if size < 3 {
|
|
||||||
// Couldn't find enough finder patterns
|
|
||||||
throw NotFoundException::get_not_found_instance();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Begin HE modifications to safely detect multiple codes of equal size
|
|
||||||
*/
|
|
||||||
if size == 3 {
|
|
||||||
return Ok( : vec![FinderPattern; 1] = vec![possible_centers.to_array(EMPTY_FP_ARRAY), ]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Sort by estimated module size to speed up the upcoming checks
|
|
||||||
Collections::sort(&possible_centers, ModuleSizeComparator::new());
|
|
||||||
/*
|
|
||||||
* Now lets start: build a list of tuples of three finder locations that
|
|
||||||
* - feature similar module sizes
|
|
||||||
* - are placed in a distance so the estimated module count is within the QR specification
|
|
||||||
* - have similar distance between upper left/right and left top/bottom finder patterns
|
|
||||||
* - form a triangle with 90° angle (checked by comparing top right/bottom left distance
|
|
||||||
* with pythagoras)
|
|
||||||
*
|
|
||||||
* Note: we allow each point to be used for more than one code region: this might seem
|
|
||||||
* counterintuitive at first, but the performance penalty is not that big. At this point,
|
|
||||||
* we cannot make a good quality decision whether the three finders actually represent
|
|
||||||
* a QR code, or are just by chance laid out so it looks like there might be a QR code there.
|
|
||||||
* So, if the layout seems right, lets have the decoder try to decode.
|
|
||||||
*/
|
|
||||||
// holder for the results
|
|
||||||
let results: List<Vec<FinderPattern>> = ArrayList<>::new();
|
|
||||||
{
|
|
||||||
let mut i1: i32 = 0;
|
|
||||||
while i1 < (size - 2) {
|
|
||||||
{
|
|
||||||
let p1: FinderPattern = possible_centers.get(i1);
|
|
||||||
if p1 == null {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let mut i2: i32 = i1 + 1;
|
|
||||||
while i2 < (size - 1) {
|
|
||||||
{
|
|
||||||
let p2: FinderPattern = possible_centers.get(i2);
|
|
||||||
if p2 == null {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Compare the expected module sizes; if they are really off, skip
|
|
||||||
let v_mod_size12: f32 = (p1.get_estimated_module_size() - p2.get_estimated_module_size()) / Math::min(&p1.get_estimated_module_size(), &p2.get_estimated_module_size());
|
|
||||||
let v_mod_size12_a: f32 = Math::abs(p1.get_estimated_module_size() - p2.get_estimated_module_size());
|
|
||||||
if v_mod_size12_a > DIFF_MODSIZE_CUTOFF && v_mod_size12 >= DIFF_MODSIZE_CUTOFF_PERCENT {
|
|
||||||
// any more interesting elements for the given p1.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let mut i3: i32 = i2 + 1;
|
|
||||||
while i3 < size {
|
|
||||||
{
|
|
||||||
let p3: FinderPattern = possible_centers.get(i3);
|
|
||||||
if p3 == null {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Compare the expected module sizes; if they are really off, skip
|
|
||||||
let v_mod_size23: f32 = (p2.get_estimated_module_size() - p3.get_estimated_module_size()) / Math::min(&p2.get_estimated_module_size(), &p3.get_estimated_module_size());
|
|
||||||
let v_mod_size23_a: f32 = Math::abs(p2.get_estimated_module_size() - p3.get_estimated_module_size());
|
|
||||||
if v_mod_size23_a > DIFF_MODSIZE_CUTOFF && v_mod_size23 >= DIFF_MODSIZE_CUTOFF_PERCENT {
|
|
||||||
// any more interesting elements for the given p1.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let test: vec![Vec<FinderPattern>; 3] = vec![p1, p2, p3, ]
|
|
||||||
;
|
|
||||||
ResultPoint::order_best_patterns(test);
|
|
||||||
// Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
|
|
||||||
let info: FinderPatternInfo = FinderPatternInfo::new(test);
|
|
||||||
let d_a: f32 = ResultPoint::distance(&info.get_top_left(), &info.get_bottom_left());
|
|
||||||
let d_c: f32 = ResultPoint::distance(&info.get_top_right(), &info.get_bottom_left());
|
|
||||||
let d_b: f32 = ResultPoint::distance(&info.get_top_left(), &info.get_top_right());
|
|
||||||
// Check the sizes
|
|
||||||
let estimated_module_count: f32 = (d_a + d_b) / (p1.get_estimated_module_size() * 2.0f);
|
|
||||||
if estimated_module_count > MAX_MODULE_COUNT_PER_EDGE || estimated_module_count < MIN_MODULE_COUNT_PER_EDGE {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Calculate the difference of the edge lengths in percent
|
|
||||||
let v_a_b_b_c: f32 = Math::abs((d_a - d_b) / Math::min(d_a, d_b));
|
|
||||||
if v_a_b_b_c >= 0.1f {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Calculate the diagonal length by assuming a 90° angle at topleft
|
|
||||||
let d_cpy: f32 = Math::sqrt(d_a as f64 * d_a + d_b as f64 * d_b) as f32;
|
|
||||||
// Compare to the real distance in %
|
|
||||||
let v_py_c: f32 = Math::abs((d_c - d_cpy) / Math::min(d_c, d_cpy));
|
|
||||||
if v_py_c >= 0.1f {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// All tests passed!
|
|
||||||
results.add(test);
|
|
||||||
}
|
|
||||||
i3 += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
i2 += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
i1 += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !results.is_empty() {
|
|
||||||
return Ok(results.to_array(EMPTY_FP_2D_ARRAY));
|
|
||||||
}
|
|
||||||
// Nothing found!
|
|
||||||
throw NotFoundException::get_not_found_instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_multi(&self, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<FinderPatternInfo>, Rc<Exception>> {
|
|
||||||
let try_harder: bool = hints != null && hints.contains_key(DecodeHintType::TRY_HARDER);
|
|
||||||
let image: BitMatrix = get_image();
|
|
||||||
let max_i: i32 = image.get_height();
|
|
||||||
let max_j: i32 = image.get_width();
|
|
||||||
// We are looking for black/white/black/white/black modules in
|
|
||||||
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
|
||||||
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
|
||||||
// image, and then account for the center being 3 modules in size. This gives the smallest
|
|
||||||
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
|
||||||
// QR versions regardless of how dense they are.
|
|
||||||
let i_skip: i32 = (3 * max_i) / (4 * MAX_MODULES);
|
|
||||||
if i_skip < MIN_SKIP || try_harder {
|
|
||||||
i_skip = MIN_SKIP;
|
|
||||||
}
|
|
||||||
let state_count: [i32; 5] = [0; 5];
|
|
||||||
{
|
|
||||||
let mut i: i32 = i_skip - 1;
|
|
||||||
while i < max_i {
|
|
||||||
{
|
|
||||||
// Get a row of black/white values
|
|
||||||
do_clear_counts(&state_count);
|
|
||||||
let current_state: i32 = 0;
|
|
||||||
{
|
|
||||||
let mut j: i32 = 0;
|
|
||||||
while j < max_j {
|
|
||||||
{
|
|
||||||
if image.get(j, i) {
|
|
||||||
// Black pixel
|
|
||||||
if (current_state & 1) == 1 {
|
|
||||||
// Counting white pixels
|
|
||||||
current_state += 1;
|
|
||||||
}
|
|
||||||
state_count[current_state] += 1;
|
|
||||||
} else {
|
|
||||||
// White pixel
|
|
||||||
if (current_state & 1) == 0 {
|
|
||||||
// Counting black pixels
|
|
||||||
if current_state == 4 {
|
|
||||||
// A winner?
|
|
||||||
if found_pattern_cross(&state_count) && handle_possible_center(&state_count, i, j) {
|
|
||||||
// Yes
|
|
||||||
// Clear state to start looking again
|
|
||||||
current_state = 0;
|
|
||||||
do_clear_counts(&state_count);
|
|
||||||
} else {
|
|
||||||
// No, shift counts back by two
|
|
||||||
do_shift_counts2(&state_count);
|
|
||||||
current_state = 3;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state_count[current_state += 1] += 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Counting white pixels
|
|
||||||
state_count[current_state] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found_pattern_cross(&state_count) {
|
|
||||||
handle_possible_center(&state_count, i, max_j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += i_skip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for i=iSkip-1 ...
|
|
||||||
let pattern_info: Vec<Vec<FinderPattern>> = self.select_multiple_best_patterns();
|
|
||||||
let result: List<FinderPatternInfo> = ArrayList<>::new();
|
|
||||||
for let pattern: Vec<FinderPattern> in pattern_info {
|
|
||||||
ResultPoint::order_best_patterns(pattern);
|
|
||||||
result.add(FinderPatternInfo::new(pattern));
|
|
||||||
}
|
|
||||||
if result.is_empty() {
|
|
||||||
return Ok(EMPTY_RESULT_ARRAY);
|
|
||||||
} else {
|
|
||||||
return Ok(result.to_array(EMPTY_RESULT_ARRAY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// package com::google::zxing::multi::qrcode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation can detect and decode multiple QR Codes in an image.
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
* @author Hannes Erven
|
|
||||||
*/
|
|
||||||
|
|
||||||
const EMPTY_RESULT_ARRAY: [Option<Result>; 0] = [None; 0];
|
|
||||||
|
|
||||||
const NO_POINTS: [Option<ResultPoint>; 0] = [None; 0];
|
|
||||||
#[derive(MultipleBarcodeReader)]
|
|
||||||
pub struct QRCodeMultiReader {
|
|
||||||
super: QRCodeReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QRCodeMultiReader {
|
|
||||||
|
|
||||||
pub fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
|
||||||
return Ok(self.decode_multiple(image, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
|
||||||
let mut results: List<Result> = ArrayList<>::new();
|
|
||||||
let detector_results: Vec<DetectorResult> = MultiDetector::new(&image.get_black_matrix()).detect_multi(&hints);
|
|
||||||
for let detector_result: DetectorResult in detector_results {
|
|
||||||
let tryResult1 = 0;
|
|
||||||
'try1: loop {
|
|
||||||
{
|
|
||||||
let decoder_result: DecoderResult = get_decoder().decode(&detector_result.get_bits(), &hints);
|
|
||||||
let points: Vec<ResultPoint> = detector_result.get_points();
|
|
||||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
|
||||||
if decoder_result.get_other() instanceof QRCodeDecoderMetaData {
|
|
||||||
(decoder_result.get_other() as QRCodeDecoderMetaData).apply_mirrored_correction(points);
|
|
||||||
}
|
|
||||||
let result: Result = Result::new(&decoder_result.get_text(), &decoder_result.get_raw_bytes(), points, BarcodeFormat::QR_CODE);
|
|
||||||
let byte_segments: List<Vec<i8>> = decoder_result.get_byte_segments();
|
|
||||||
if byte_segments != null {
|
|
||||||
result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &byte_segments);
|
|
||||||
}
|
|
||||||
let ec_level: String = decoder_result.get_e_c_level();
|
|
||||||
if ec_level != null {
|
|
||||||
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
|
||||||
}
|
|
||||||
if decoder_result.has_structured_append() {
|
|
||||||
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE, &decoder_result.get_structured_append_sequence_number());
|
|
||||||
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_PARITY, &decoder_result.get_structured_append_parity());
|
|
||||||
}
|
|
||||||
results.add(result);
|
|
||||||
}
|
|
||||||
break 'try1
|
|
||||||
}
|
|
||||||
match tryResult1 {
|
|
||||||
catch ( re: &ReaderException) {
|
|
||||||
} 0 => break
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if results.is_empty() {
|
|
||||||
return Ok(EMPTY_RESULT_ARRAY);
|
|
||||||
} else {
|
|
||||||
results = ::process_structured_append(&results);
|
|
||||||
return Ok(results.to_array(EMPTY_RESULT_ARRAY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_structured_append( results: &List<Result>) -> List<Result> {
|
|
||||||
let new_results: List<Result> = ArrayList<>::new();
|
|
||||||
let sa_results: List<Result> = ArrayList<>::new();
|
|
||||||
for let result: Result in results {
|
|
||||||
if result.get_result_metadata().contains_key(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) {
|
|
||||||
sa_results.add(result);
|
|
||||||
} else {
|
|
||||||
new_results.add(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sa_results.is_empty() {
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
// sort and concatenate the SA list items
|
|
||||||
Collections::sort(&sa_results, SAComparator::new());
|
|
||||||
let new_text: StringBuilder = StringBuilder::new();
|
|
||||||
let new_raw_bytes: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
|
||||||
let new_byte_segment: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
|
||||||
for let sa_result: Result in sa_results {
|
|
||||||
new_text.append(&sa_result.get_text());
|
|
||||||
let sa_bytes: Vec<i8> = sa_result.get_raw_bytes();
|
|
||||||
new_raw_bytes.write(&sa_bytes, 0, sa_bytes.len());
|
|
||||||
let byte_segments: Iterable<Vec<i8>> = sa_result.get_result_metadata().get(ResultMetadataType::BYTE_SEGMENTS) as Iterable<Vec<i8>>;
|
|
||||||
if byte_segments != null {
|
|
||||||
for let segment: Vec<i8> in byte_segments {
|
|
||||||
new_byte_segment.write(&segment, 0, segment.len());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let new_result: Result = Result::new(&new_text.to_string(), &new_raw_bytes.to_byte_array(), NO_POINTS, BarcodeFormat::QR_CODE);
|
|
||||||
if new_byte_segment.size() > 0 {
|
|
||||||
new_result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &Collections::singleton_list(&new_byte_segment.to_byte_array()));
|
|
||||||
}
|
|
||||||
new_results.add(new_result);
|
|
||||||
return new_results;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Comparator<Result>, Serializable)]
|
|
||||||
struct SAComparator {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SAComparator {
|
|
||||||
|
|
||||||
pub fn compare(&self, a: &Result, b: &Result) -> i32 {
|
|
||||||
let a_number: i32 = a.get_result_metadata().get(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) as i32;
|
|
||||||
let b_number: i32 = b.get_result_metadata().get(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) as i32;
|
|
||||||
return Integer::compare(a_number, b_number);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
285
src/multi.rs
285
src/multi.rs
@@ -0,0 +1,285 @@
|
|||||||
|
use crate::{BinaryBitmap,DecodeHintType,NotFoundException,RXingResult,Reader,ReaderException,ResultPoint,ChecksumException,FormatException};
|
||||||
|
|
||||||
|
// ByQuadrantReader.java
|
||||||
|
/**
|
||||||
|
* This class attempts to decode a barcode from an image, not by scanning the whole image,
|
||||||
|
* but by scanning subsets of the image. This is important when there may be multiple barcodes in
|
||||||
|
* an image, and detecting a barcode may find parts of multiple barcode and fail to decode
|
||||||
|
* (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center
|
||||||
|
* 'quadrant' to cover the case where a barcode is found in the center.
|
||||||
|
*
|
||||||
|
* @see GenericMultipleBarcodeReader
|
||||||
|
*/
|
||||||
|
pub struct ByQuadrantReader {
|
||||||
|
|
||||||
|
let delegate: Reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reader for ByQuadrantReader {
|
||||||
|
pub fn decode(&self, image: &BinaryBitmap) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||||
|
return Ok(self.decode(image, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||||
|
let width: i32 = image.get_width();
|
||||||
|
let height: i32 = image.get_height();
|
||||||
|
let half_width: i32 = width / 2;
|
||||||
|
let half_height: i32 = height / 2;
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
// No need to call makeAbsolute as results will be relative to original top left here
|
||||||
|
return Ok(self.delegate.decode(&image.crop(0, 0, half_width, half_height), &hints));
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( re: &NotFoundException) {
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
let result: Result = self.delegate.decode(&image.crop(half_width, 0, half_width, half_height), &hints);
|
||||||
|
::make_absolute(&result.get_result_points(), half_width, 0);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( re: &NotFoundException) {
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
let result: Result = self.delegate.decode(&image.crop(0, half_height, half_width, half_height), &hints);
|
||||||
|
::make_absolute(&result.get_result_points(), 0, half_height);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( re: &NotFoundException) {
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
let result: Result = self.delegate.decode(&image.crop(half_width, half_height, half_width, half_height), &hints);
|
||||||
|
::make_absolute(&result.get_result_points(), half_width, half_height);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( re: &NotFoundException) {
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
let quarter_width: i32 = half_width / 2;
|
||||||
|
let quarter_height: i32 = half_height / 2;
|
||||||
|
let center: BinaryBitmap = image.crop(quarter_width, quarter_height, half_width, half_height);
|
||||||
|
let result: Result = self.delegate.decode(center, &hints);
|
||||||
|
::make_absolute(&result.get_result_points(), quarter_width, quarter_height);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&self) {
|
||||||
|
self.delegate.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ByQuadrantReader {
|
||||||
|
|
||||||
|
pub fn new( delegate: &Reader) -> ByQuadrantReader {
|
||||||
|
let .delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn make_absolute( points: &Vec<ResultPoint>, left_offset: i32, top_offset: i32) {
|
||||||
|
if points != null {
|
||||||
|
{
|
||||||
|
let mut i: i32 = 0;
|
||||||
|
while i < points.len() {
|
||||||
|
{
|
||||||
|
let relative: ResultPoint = points[i];
|
||||||
|
if relative != null {
|
||||||
|
points[i] = ResultPoint::new(relative.get_x() + left_offset, relative.get_y() + top_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MultipleBarcodeReader.java
|
||||||
|
/**
|
||||||
|
* Implementation of this interface attempt to read several barcodes from one image.
|
||||||
|
*
|
||||||
|
* @see com.google.zxing.Reader
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
pub trait MultipleBarcodeReader {
|
||||||
|
|
||||||
|
fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> ;
|
||||||
|
|
||||||
|
fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericMultipleBarcodeReader.java
|
||||||
|
/**
|
||||||
|
* <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
|
||||||
|
* After one barcode is found, the areas left, above, right and below the barcode's
|
||||||
|
* {@link ResultPoint}s are scanned, recursively.</p>
|
||||||
|
*
|
||||||
|
* <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
|
||||||
|
* 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
|
||||||
|
* detecting any one of them.</p>
|
||||||
|
*
|
||||||
|
* <p>That is, instead of passing a {@link Reader} a caller might pass
|
||||||
|
* {@code new ByQuadrantReader(reader)}.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
|
||||||
|
const MIN_DIMENSION_TO_RECUR: i32 = 100;
|
||||||
|
|
||||||
|
const MAX_DEPTH: i32 = 4;
|
||||||
|
|
||||||
|
const EMPTY_RESULT_ARRAY: [Option<Result>; 0] = [None; 0];
|
||||||
|
|
||||||
|
pub struct GenericMultipleBarcodeReader {
|
||||||
|
|
||||||
|
let delegate: Reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultipleBarcodeReader for GenericMultipleBarcodeReader {
|
||||||
|
pub fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||||
|
return Ok(self.decode_multiple(image, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||||
|
let results: List<Result> = ArrayList<>::new();
|
||||||
|
self.do_decode_multiple(image, &hints, &results, 0, 0, 0);
|
||||||
|
if results.is_empty() {
|
||||||
|
throw NotFoundException::get_not_found_instance();
|
||||||
|
}
|
||||||
|
return Ok(results.to_array(EMPTY_RESULT_ARRAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericMultipleBarcodeReader {
|
||||||
|
|
||||||
|
pub fn new( delegate: &Reader) -> GenericMultipleBarcodeReader {
|
||||||
|
let .delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn do_decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>, results: &List<Result>, x_offset: i32, y_offset: i32, current_depth: i32) {
|
||||||
|
if current_depth > MAX_DEPTH {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut result: Result;
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
result = self.delegate.decode(image, &hints);
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( ignored: &ReaderException) {
|
||||||
|
return;
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
let already_found: bool = false;
|
||||||
|
for let existing_result: Result in results {
|
||||||
|
if existing_result.get_text().equals(&result.get_text()) {
|
||||||
|
already_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !already_found {
|
||||||
|
results.add(&::translate_result_points(result, x_offset, y_offset));
|
||||||
|
}
|
||||||
|
let result_points: Vec<ResultPoint> = result.get_result_points();
|
||||||
|
if result_points == null || result_points.len() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let width: i32 = image.get_width();
|
||||||
|
let height: i32 = image.get_height();
|
||||||
|
let min_x: f32 = width;
|
||||||
|
let min_y: f32 = height;
|
||||||
|
let max_x: f32 = 0.0f;
|
||||||
|
let max_y: f32 = 0.0f;
|
||||||
|
for let point: ResultPoint in result_points {
|
||||||
|
if point == null {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let x: f32 = point.get_x();
|
||||||
|
let y: f32 = point.get_y();
|
||||||
|
if x < min_x {
|
||||||
|
min_x = x;
|
||||||
|
}
|
||||||
|
if y < min_y {
|
||||||
|
min_y = y;
|
||||||
|
}
|
||||||
|
if x > max_x {
|
||||||
|
max_x = x;
|
||||||
|
}
|
||||||
|
if y > max_y {
|
||||||
|
max_y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decode left of barcode
|
||||||
|
if min_x > MIN_DIMENSION_TO_RECUR {
|
||||||
|
self.do_decode_multiple(&image.crop(0, 0, min_x as i32, height), &hints, &results, x_offset, y_offset, current_depth + 1);
|
||||||
|
}
|
||||||
|
// Decode above barcode
|
||||||
|
if min_y > MIN_DIMENSION_TO_RECUR {
|
||||||
|
self.do_decode_multiple(&image.crop(0, 0, width, min_y as i32), &hints, &results, x_offset, y_offset, current_depth + 1);
|
||||||
|
}
|
||||||
|
// Decode right of barcode
|
||||||
|
if max_x < width - MIN_DIMENSION_TO_RECUR {
|
||||||
|
self.do_decode_multiple(&image.crop(max_x as i32, 0, width - max_x as i32, height), &hints, &results, x_offset + max_x as i32, y_offset, current_depth + 1);
|
||||||
|
}
|
||||||
|
// Decode below barcode
|
||||||
|
if max_y < height - MIN_DIMENSION_TO_RECUR {
|
||||||
|
self.do_decode_multiple(&image.crop(0, max_y as i32, width, height - max_y as i32), &hints, &results, x_offset, y_offset + max_y as i32, current_depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_result_points( result: &Result, x_offset: i32, y_offset: i32) -> Result {
|
||||||
|
let old_result_points: Vec<ResultPoint> = result.get_result_points();
|
||||||
|
if old_result_points == null {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
let new_result_points: [Option<ResultPoint>; old_result_points.len()] = [None; old_result_points.len()];
|
||||||
|
{
|
||||||
|
let mut i: i32 = 0;
|
||||||
|
while i < old_result_points.len() {
|
||||||
|
{
|
||||||
|
let old_point: ResultPoint = old_result_points[i];
|
||||||
|
if old_point != null {
|
||||||
|
new_result_points[i] = ResultPoint::new(old_point.get_x() + x_offset, old_point.get_y() + y_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_result: Result = Result::new(&result.get_text(), &result.get_raw_bytes(), &result.get_num_bits(), new_result_points, &result.get_barcode_format(), &result.get_timestamp());
|
||||||
|
new_result.put_all_metadata(&result.get_result_metadata());
|
||||||
|
return new_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
use crate::{BarcodeFormat,BinaryBitmap,DecodeHintType,NotFoundException,ReaderException,RXingResult,ResultMetadataType,ResultPoint};
|
||||||
|
use crate::common{DecoderResult,DetectorResult};
|
||||||
|
use crate::multi::{MultipleBarcodeReader};
|
||||||
|
use crate::multi::qrcode::detector::MultiDetector;
|
||||||
|
use create::qrcode::{QRCodeReader};
|
||||||
|
use crate::multi::qrcode::decoder::QRCodeDecoderMetaData;
|
||||||
|
|
||||||
|
|
||||||
|
// QRCodeMultiReader.java
|
||||||
|
/**
|
||||||
|
* This implementation can detect and decode multiple QR Codes in an image.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author Hannes Erven
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EMPTY_RESULT_ARRAY: [Option<Result>; 0] = [None; 0];
|
||||||
|
|
||||||
|
const NO_POINTS: [Option<ResultPoint>; 0] = [None; 0];
|
||||||
|
pub struct QRCodeMultiReader {
|
||||||
|
super: QRCodeReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultipleBarcodeReader for QRCodeMultiReader {
|
||||||
|
pub fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||||
|
return Ok(self.decode_multiple(image, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||||
|
let mut results: List<Result> = ArrayList<>::new();
|
||||||
|
let detector_results: Vec<DetectorResult> = MultiDetector::new(&image.get_black_matrix()).detect_multi(&hints);
|
||||||
|
for let detector_result: DetectorResult in detector_results {
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
let decoder_result: DecoderResult = get_decoder().decode(&detector_result.get_bits(), &hints);
|
||||||
|
let points: Vec<ResultPoint> = detector_result.get_points();
|
||||||
|
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||||
|
if decoder_result.get_other() instanceof QRCodeDecoderMetaData {
|
||||||
|
(decoder_result.get_other() as QRCodeDecoderMetaData).apply_mirrored_correction(points);
|
||||||
|
}
|
||||||
|
let result: Result = Result::new(&decoder_result.get_text(), &decoder_result.get_raw_bytes(), points, BarcodeFormat::QR_CODE);
|
||||||
|
let byte_segments: List<Vec<i8>> = decoder_result.get_byte_segments();
|
||||||
|
if byte_segments != null {
|
||||||
|
result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &byte_segments);
|
||||||
|
}
|
||||||
|
let ec_level: String = decoder_result.get_e_c_level();
|
||||||
|
if ec_level != null {
|
||||||
|
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
||||||
|
}
|
||||||
|
if decoder_result.has_structured_append() {
|
||||||
|
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE, &decoder_result.get_structured_append_sequence_number());
|
||||||
|
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_PARITY, &decoder_result.get_structured_append_parity());
|
||||||
|
}
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( re: &ReaderException) {
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if results.is_empty() {
|
||||||
|
return Ok(EMPTY_RESULT_ARRAY);
|
||||||
|
} else {
|
||||||
|
results = ::process_structured_append(&results);
|
||||||
|
return Ok(results.to_array(EMPTY_RESULT_ARRAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QRCodeMultiReader {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn process_structured_append( results: &List<Result>) -> List<Result> {
|
||||||
|
let new_results: List<Result> = ArrayList<>::new();
|
||||||
|
let sa_results: List<Result> = ArrayList<>::new();
|
||||||
|
for let result: Result in results {
|
||||||
|
if result.get_result_metadata().contains_key(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) {
|
||||||
|
sa_results.add(result);
|
||||||
|
} else {
|
||||||
|
new_results.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sa_results.is_empty() {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
// sort and concatenate the SA list items
|
||||||
|
Collections::sort(&sa_results, SAComparator::new());
|
||||||
|
let new_text: StringBuilder = StringBuilder::new();
|
||||||
|
let new_raw_bytes: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
||||||
|
let new_byte_segment: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
||||||
|
for let sa_result: Result in sa_results {
|
||||||
|
new_text.append(&sa_result.get_text());
|
||||||
|
let sa_bytes: Vec<i8> = sa_result.get_raw_bytes();
|
||||||
|
new_raw_bytes.write(&sa_bytes, 0, sa_bytes.len());
|
||||||
|
let byte_segments: Iterable<Vec<i8>> = sa_result.get_result_metadata().get(ResultMetadataType::BYTE_SEGMENTS) as Iterable<Vec<i8>>;
|
||||||
|
if byte_segments != null {
|
||||||
|
for let segment: Vec<i8> in byte_segments {
|
||||||
|
new_byte_segment.write(&segment, 0, segment.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let new_result: Result = Result::new(&new_text.to_string(), &new_raw_bytes.to_byte_array(), NO_POINTS, BarcodeFormat::QR_CODE);
|
||||||
|
if new_byte_segment.size() > 0 {
|
||||||
|
new_result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &Collections::singleton_list(&new_byte_segment.to_byte_array()));
|
||||||
|
}
|
||||||
|
new_results.add(new_result);
|
||||||
|
return new_results;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Comparator<Result>, Serializable)]
|
||||||
|
struct SAComparator {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SAComparator {
|
||||||
|
|
||||||
|
pub fn compare(&self, a: &Result, b: &Result) -> i32 {
|
||||||
|
let a_number: i32 = a.get_result_metadata().get(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) as i32;
|
||||||
|
let b_number: i32 = b.get_result_metadata().get(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) as i32;
|
||||||
|
return Integer::compare(a_number, b_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,344 @@
|
|||||||
|
use crate::{DecodeHintType,NotFoundException,ReaderException,ResultPointCallback};
|
||||||
|
use crate::common::{BitMatrix,DetectorResult};
|
||||||
|
use crate::qrcode::detector::{Detector,FinderPatternInfo,FinderPattern,FinderPatternFinder};
|
||||||
|
|
||||||
|
// MultiDetector.java
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
|
||||||
|
* is rotated or skewed, or partially obscured.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author Hannes Erven
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EMPTY_DETECTOR_RESULTS: [Option<DetectorResult>; 0] = [None; 0];
|
||||||
|
pub struct MultiDetector {
|
||||||
|
super: Detector;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Detector for MultiDetector{}
|
||||||
|
|
||||||
|
impl MultiDetector {
|
||||||
|
|
||||||
|
pub fn new( image: &BitMatrix) -> MultiDetector {
|
||||||
|
super(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detect_multi(&self, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<DetectorResult>, Rc<Exception>> {
|
||||||
|
let image: BitMatrix = get_image();
|
||||||
|
let result_point_callback: ResultPointCallback = if hints == null { null } else { hints.get(DecodeHintType::NEED_RESULT_POINT_CALLBACK) as ResultPointCallback };
|
||||||
|
let finder: MultiFinderPatternFinder = MultiFinderPatternFinder::new(image, result_point_callback);
|
||||||
|
let infos: Vec<FinderPatternInfo> = finder.find_multi(&hints);
|
||||||
|
if infos.len() == 0 {
|
||||||
|
throw NotFoundException::get_not_found_instance();
|
||||||
|
}
|
||||||
|
let result: List<DetectorResult> = ArrayList<>::new();
|
||||||
|
for let info: FinderPatternInfo in infos {
|
||||||
|
let tryResult1 = 0;
|
||||||
|
'try1: loop {
|
||||||
|
{
|
||||||
|
result.add(&process_finder_pattern_info(info));
|
||||||
|
}
|
||||||
|
break 'try1
|
||||||
|
}
|
||||||
|
match tryResult1 {
|
||||||
|
catch ( e: &ReaderException) {
|
||||||
|
} 0 => break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if result.is_empty() {
|
||||||
|
return Ok(EMPTY_DETECTOR_RESULTS);
|
||||||
|
} else {
|
||||||
|
return Ok(result.to_array(EMPTY_DETECTOR_RESULTS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiFinderPatternFinder.java
|
||||||
|
/**
|
||||||
|
* <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
|
||||||
|
* markers at three corners of a QR Code.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
|
||||||
|
*
|
||||||
|
* <p>In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
|
||||||
|
* QR code locations in the image.</p>
|
||||||
|
*
|
||||||
|
* <p>Use the TRY_HARDER hint to ask for a more thorough detection.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author Hannes Erven
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EMPTY_RESULT_ARRAY: [Option<FinderPatternInfo>; 0] = [None; 0];
|
||||||
|
|
||||||
|
const EMPTY_FP_ARRAY: [Option<FinderPattern>; 0] = [None; 0];
|
||||||
|
|
||||||
|
const EMPTY_FP_2D_ARRAY: [Option<FinderPattern>; 0] = [None; 0];
|
||||||
|
|
||||||
|
// TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
|
||||||
|
// since it limits the number of regions to decode
|
||||||
|
// max. legal count of modules per QR code edge (177)
|
||||||
|
const MAX_MODULE_COUNT_PER_EDGE: f32 = 180;
|
||||||
|
|
||||||
|
// min. legal count per modules per QR code edge (11)
|
||||||
|
const MIN_MODULE_COUNT_PER_EDGE: f32 = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
||||||
|
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
|
||||||
|
* estimated modules sizes.
|
||||||
|
*/
|
||||||
|
const DIFF_MODSIZE_CUTOFF_PERCENT: f32 = 0.05f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
||||||
|
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
|
||||||
|
* estimated modules sizes.
|
||||||
|
*/
|
||||||
|
const DIFF_MODSIZE_CUTOFF: f32 = 0.5f;
|
||||||
|
pub struct MultiFinderPatternFinder {
|
||||||
|
super: FinderPatternFinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FinderPatternFinder for MultiFinderPatternFinder {}
|
||||||
|
|
||||||
|
impl MultiFinderPatternFinder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comparator that orders FinderPatterns by their estimated module size.
|
||||||
|
*/
|
||||||
|
#[derive(Comparator<FinderPattern>, Serializable)]
|
||||||
|
struct ModuleSizeComparator {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleSizeComparator {
|
||||||
|
|
||||||
|
pub fn compare(&self, center1: &FinderPattern, center2: &FinderPattern) -> i32 {
|
||||||
|
let value: f32 = center2.get_estimated_module_size() - center1.get_estimated_module_size();
|
||||||
|
return if value < 0.0 { -1 } else { if value > 0.0 { 1 } else { 0 } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn new( image: &BitMatrix, result_point_callback: &ResultPointCallback) -> MultiFinderPatternFinder {
|
||||||
|
super(image, result_point_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
|
||||||
|
* those that have been detected at least 2 times, and whose module
|
||||||
|
* size differs from the average among those patterns the least
|
||||||
|
* @throws NotFoundException if 3 such finder patterns do not exist
|
||||||
|
*/
|
||||||
|
fn select_multiple_best_patterns(&self) -> /* throws NotFoundException */Result<Vec<Vec<FinderPattern>>, Rc<Exception>> {
|
||||||
|
let possible_centers: List<FinderPattern> = ArrayList<>::new();
|
||||||
|
for let fp: FinderPattern in get_possible_centers() {
|
||||||
|
if fp.get_count() >= 2 {
|
||||||
|
possible_centers.add(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let size: i32 = possible_centers.size();
|
||||||
|
if size < 3 {
|
||||||
|
// Couldn't find enough finder patterns
|
||||||
|
throw NotFoundException::get_not_found_instance();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Begin HE modifications to safely detect multiple codes of equal size
|
||||||
|
*/
|
||||||
|
if size == 3 {
|
||||||
|
return Ok( : vec![FinderPattern; 1] = vec![possible_centers.to_array(EMPTY_FP_ARRAY), ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Sort by estimated module size to speed up the upcoming checks
|
||||||
|
Collections::sort(&possible_centers, ModuleSizeComparator::new());
|
||||||
|
/*
|
||||||
|
* Now lets start: build a list of tuples of three finder locations that
|
||||||
|
* - feature similar module sizes
|
||||||
|
* - are placed in a distance so the estimated module count is within the QR specification
|
||||||
|
* - have similar distance between upper left/right and left top/bottom finder patterns
|
||||||
|
* - form a triangle with 90° angle (checked by comparing top right/bottom left distance
|
||||||
|
* with pythagoras)
|
||||||
|
*
|
||||||
|
* Note: we allow each point to be used for more than one code region: this might seem
|
||||||
|
* counterintuitive at first, but the performance penalty is not that big. At this point,
|
||||||
|
* we cannot make a good quality decision whether the three finders actually represent
|
||||||
|
* a QR code, or are just by chance laid out so it looks like there might be a QR code there.
|
||||||
|
* So, if the layout seems right, lets have the decoder try to decode.
|
||||||
|
*/
|
||||||
|
// holder for the results
|
||||||
|
let results: List<Vec<FinderPattern>> = ArrayList<>::new();
|
||||||
|
{
|
||||||
|
let mut i1: i32 = 0;
|
||||||
|
while i1 < (size - 2) {
|
||||||
|
{
|
||||||
|
let p1: FinderPattern = possible_centers.get(i1);
|
||||||
|
if p1 == null {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut i2: i32 = i1 + 1;
|
||||||
|
while i2 < (size - 1) {
|
||||||
|
{
|
||||||
|
let p2: FinderPattern = possible_centers.get(i2);
|
||||||
|
if p2 == null {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Compare the expected module sizes; if they are really off, skip
|
||||||
|
let v_mod_size12: f32 = (p1.get_estimated_module_size() - p2.get_estimated_module_size()) / Math::min(&p1.get_estimated_module_size(), &p2.get_estimated_module_size());
|
||||||
|
let v_mod_size12_a: f32 = Math::abs(p1.get_estimated_module_size() - p2.get_estimated_module_size());
|
||||||
|
if v_mod_size12_a > DIFF_MODSIZE_CUTOFF && v_mod_size12 >= DIFF_MODSIZE_CUTOFF_PERCENT {
|
||||||
|
// any more interesting elements for the given p1.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut i3: i32 = i2 + 1;
|
||||||
|
while i3 < size {
|
||||||
|
{
|
||||||
|
let p3: FinderPattern = possible_centers.get(i3);
|
||||||
|
if p3 == null {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Compare the expected module sizes; if they are really off, skip
|
||||||
|
let v_mod_size23: f32 = (p2.get_estimated_module_size() - p3.get_estimated_module_size()) / Math::min(&p2.get_estimated_module_size(), &p3.get_estimated_module_size());
|
||||||
|
let v_mod_size23_a: f32 = Math::abs(p2.get_estimated_module_size() - p3.get_estimated_module_size());
|
||||||
|
if v_mod_size23_a > DIFF_MODSIZE_CUTOFF && v_mod_size23 >= DIFF_MODSIZE_CUTOFF_PERCENT {
|
||||||
|
// any more interesting elements for the given p1.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let test: vec![Vec<FinderPattern>; 3] = vec![p1, p2, p3, ]
|
||||||
|
;
|
||||||
|
ResultPoint::order_best_patterns(test);
|
||||||
|
// Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
|
||||||
|
let info: FinderPatternInfo = FinderPatternInfo::new(test);
|
||||||
|
let d_a: f32 = ResultPoint::distance(&info.get_top_left(), &info.get_bottom_left());
|
||||||
|
let d_c: f32 = ResultPoint::distance(&info.get_top_right(), &info.get_bottom_left());
|
||||||
|
let d_b: f32 = ResultPoint::distance(&info.get_top_left(), &info.get_top_right());
|
||||||
|
// Check the sizes
|
||||||
|
let estimated_module_count: f32 = (d_a + d_b) / (p1.get_estimated_module_size() * 2.0f);
|
||||||
|
if estimated_module_count > MAX_MODULE_COUNT_PER_EDGE || estimated_module_count < MIN_MODULE_COUNT_PER_EDGE {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Calculate the difference of the edge lengths in percent
|
||||||
|
let v_a_b_b_c: f32 = Math::abs((d_a - d_b) / Math::min(d_a, d_b));
|
||||||
|
if v_a_b_b_c >= 0.1f {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Calculate the diagonal length by assuming a 90° angle at topleft
|
||||||
|
let d_cpy: f32 = Math::sqrt(d_a as f64 * d_a + d_b as f64 * d_b) as f32;
|
||||||
|
// Compare to the real distance in %
|
||||||
|
let v_py_c: f32 = Math::abs((d_c - d_cpy) / Math::min(d_c, d_cpy));
|
||||||
|
if v_py_c >= 0.1f {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// All tests passed!
|
||||||
|
results.add(test);
|
||||||
|
}
|
||||||
|
i3 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
i2 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
i1 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !results.is_empty() {
|
||||||
|
return Ok(results.to_array(EMPTY_FP_2D_ARRAY));
|
||||||
|
}
|
||||||
|
// Nothing found!
|
||||||
|
throw NotFoundException::get_not_found_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_multi(&self, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<FinderPatternInfo>, Rc<Exception>> {
|
||||||
|
let try_harder: bool = hints != null && hints.contains_key(DecodeHintType::TRY_HARDER);
|
||||||
|
let image: BitMatrix = get_image();
|
||||||
|
let max_i: i32 = image.get_height();
|
||||||
|
let max_j: i32 = image.get_width();
|
||||||
|
// We are looking for black/white/black/white/black modules in
|
||||||
|
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
||||||
|
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
||||||
|
// image, and then account for the center being 3 modules in size. This gives the smallest
|
||||||
|
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
||||||
|
// QR versions regardless of how dense they are.
|
||||||
|
let i_skip: i32 = (3 * max_i) / (4 * MAX_MODULES);
|
||||||
|
if i_skip < MIN_SKIP || try_harder {
|
||||||
|
i_skip = MIN_SKIP;
|
||||||
|
}
|
||||||
|
let state_count: [i32; 5] = [0; 5];
|
||||||
|
{
|
||||||
|
let mut i: i32 = i_skip - 1;
|
||||||
|
while i < max_i {
|
||||||
|
{
|
||||||
|
// Get a row of black/white values
|
||||||
|
do_clear_counts(&state_count);
|
||||||
|
let current_state: i32 = 0;
|
||||||
|
{
|
||||||
|
let mut j: i32 = 0;
|
||||||
|
while j < max_j {
|
||||||
|
{
|
||||||
|
if image.get(j, i) {
|
||||||
|
// Black pixel
|
||||||
|
if (current_state & 1) == 1 {
|
||||||
|
// Counting white pixels
|
||||||
|
current_state += 1;
|
||||||
|
}
|
||||||
|
state_count[current_state] += 1;
|
||||||
|
} else {
|
||||||
|
// White pixel
|
||||||
|
if (current_state & 1) == 0 {
|
||||||
|
// Counting black pixels
|
||||||
|
if current_state == 4 {
|
||||||
|
// A winner?
|
||||||
|
if found_pattern_cross(&state_count) && handle_possible_center(&state_count, i, j) {
|
||||||
|
// Yes
|
||||||
|
// Clear state to start looking again
|
||||||
|
current_state = 0;
|
||||||
|
do_clear_counts(&state_count);
|
||||||
|
} else {
|
||||||
|
// No, shift counts back by two
|
||||||
|
do_shift_counts2(&state_count);
|
||||||
|
current_state = 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state_count[current_state += 1] += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Counting white pixels
|
||||||
|
state_count[current_state] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found_pattern_cross(&state_count) {
|
||||||
|
handle_possible_center(&state_count, i, max_j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += i_skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for i=iSkip-1 ...
|
||||||
|
let pattern_info: Vec<Vec<FinderPattern>> = self.select_multiple_best_patterns();
|
||||||
|
let result: List<FinderPatternInfo> = ArrayList<>::new();
|
||||||
|
for let pattern: Vec<FinderPattern> in pattern_info {
|
||||||
|
ResultPoint::order_best_patterns(pattern);
|
||||||
|
result.add(FinderPatternInfo::new(pattern));
|
||||||
|
}
|
||||||
|
if result.is_empty() {
|
||||||
|
return Ok(EMPTY_RESULT_ARRAY);
|
||||||
|
} else {
|
||||||
|
return Ok(result.to_array(EMPTY_RESULT_ARRAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user