/* * 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.common.detector; use crate::{common::BitMatrix, Exceptions, RXingResultPoint, ResultPoint}; /** *

A somewhat generic detector that looks for a barcode-like rectangular region within an image. * It looks within a mostly white region of an image for a region of black and white, but mostly * black. It returns the four corners of the region, as best it can determine.

* * @author Sean Owen * @deprecated without replacement since 3.3.0 */ const MAX_MODULES: i32 = 32; #[deprecated] pub struct MonochromeRectangleDetector { image: BitMatrix, } impl MonochromeRectangleDetector { pub fn new(image: &BitMatrix) -> Self { Self { image: image.clone(), } } /** *

Detects a rectangular region of black and white -- mostly black -- with a region of mostly * white, in an image.

* * @return {@link RXingResultPoint}[] describing the corners of the rectangular region. The first and * last points are opposed on the diagonal, as are the second and third. The first point will be * the topmost point and the last, the bottommost. The second point will be leftmost and the * third, the rightmost * @throws NotFoundException if no Data Matrix Code can be found */ pub fn detect(&self) -> Result, Exceptions> { let height = self.image.getHeight() as i32; let width = self.image.getWidth() as i32; let halfHeight = height / 2; let halfWidth = width / 2; let deltaY = 1.max(height as i32 / (MAX_MODULES * 8)); let deltaX = 1.max(width as i32 / (MAX_MODULES * 8)); let mut top = 0; let mut bottom = height; let mut left = 0; let mut right = width; let mut pointA = self.findCornerFromCenter( halfWidth, 0, left, right, halfHeight, -deltaY, top, bottom, halfWidth / 2, )?; top = (pointA.getY() - 1f32) as i32; let pointB = self.findCornerFromCenter( halfWidth, -deltaX, left, right, halfHeight, 0, top, bottom, halfHeight / 2, )?; left = (pointB.getX() - 1f32) as i32; let pointC = self.findCornerFromCenter( halfWidth, deltaX, left, right, halfHeight, 0, top, bottom, halfHeight / 2, )?; right = (pointC.getX() + 1f32) as i32; let pointD = self.findCornerFromCenter( halfWidth, 0, left, right, halfHeight, deltaY, top, bottom, halfWidth / 2, )?; bottom = (pointD.getY() + 1f32) as i32; // Go try to find point A again with better information -- might have been off at first. pointA = self.findCornerFromCenter( halfWidth, 0, left, right, halfHeight, -deltaY, top, bottom, halfWidth / 4, )?; return Ok(vec![pointA, pointB, pointC, pointD]); } /** * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center * point which should be within the barcode. * * @param centerX center's x component (horizontal) * @param deltaX same as deltaY but change in x per step instead * @param left minimum value of x * @param right maximum value of x * @param centerY center's y component (vertical) * @param deltaY change in y per step. If scanning up this is negative; down, positive; * left or right, 0 * @param top minimum value of y to search through (meaningless when di == 0) * @param bottom maximum value of y * @param maxWhiteRun maximum run of white pixels that can still be considered to be within * the barcode * @return a {@link RXingResultPoint} encapsulating the corner that was found * @throws NotFoundException if such a point cannot be found */ fn findCornerFromCenter( &self, centerX: i32, deltaX: i32, left: i32, right: i32, centerY: i32, deltaY: i32, top: i32, bottom: i32, maxWhiteRun: i32, ) -> Result { let mut lastRange_z: Option> = None; let mut y: i32 = centerY; let mut x: i32 = centerX; while y < bottom && y >= top && x < right && x >= left { let range: Option>; if deltaX == 0 { // horizontal slices, up and down range = self.blackWhiteRange(y, maxWhiteRun, left, right, true); } else { // vertical slices, left and right range = self.blackWhiteRange(x, maxWhiteRun, top, bottom, false); } if range.is_none() { if let Some(lastRange) = lastRange_z { // lastRange was found if deltaX == 0 { let lastY = y - deltaY; if lastRange[0] < centerX { if lastRange[1] > centerX { // straddle, choose one or the other based on direction return Ok(RXingResultPoint::new( lastRange[if deltaY > 0 { 0 } else { 1 }] as f32, lastY as f32, )); } return Ok(RXingResultPoint::new(lastRange[0] as f32, lastY as f32)); } else { return Ok(RXingResultPoint::new(lastRange[1] as f32, lastY as f32)); } } else { let lastX = x - deltaX; if lastRange[0] < centerY { if lastRange[1] > centerY { return Ok(RXingResultPoint::new( lastX as f32, lastRange[if deltaX < 0 { 0 } else { 1 }] as f32, )); } return Ok(RXingResultPoint::new(lastX as f32, lastRange[0] as f32)); } else { return Ok(RXingResultPoint::new(lastX as f32, lastRange[1] as f32)); } } } } else { return Err(Exceptions::NotFoundException("".to_owned())); } lastRange_z = range; y += deltaY; x += deltaX } return Err(Exceptions::NotFoundException("".to_owned())); } /** * Computes the start and end of a region of pixels, either horizontally or vertically, that could * be part of a Data Matrix barcode. * * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) * where we are scanning. If scanning vertically it's the column, the fixed horizontal location * @param maxWhiteRun largest run of white pixels that can still be considered part of the * barcode region * @param minDim minimum pixel location, horizontally or vertically, to consider * @param maxDim maximum pixel location, horizontally or vertically, to consider * @param horizontal if true, we're scanning left-right, instead of up-down * @return int[] with start and end of found range, or null if no such range is found * (e.g. only white was found) */ fn blackWhiteRange( &self, fixedDimension: i32, maxWhiteRun: i32, minDim: i32, maxDim: i32, horizontal: bool, ) -> Option> { let center = (minDim + maxDim) / 2; // Scan left/up first let mut start = center; while start >= minDim { if if horizontal { self.image.get(start as u32, fixedDimension as u32) } else { self.image.get(fixedDimension as u32, start as u32) } { start = start - 1; } else { let whiteRunStart = start; start = start - 1; while start >= minDim && !(if horizontal { self.image.get(start as u32, fixedDimension as u32) } else { self.image.get(fixedDimension as u32, start as u32) }) { start = start - 1; } let whiteRunSize = whiteRunStart - start; if start < minDim || whiteRunSize > maxWhiteRun { start = whiteRunStart; break; } } } start = start + 1; // Then try right/down let mut end = center; while end < maxDim { if if horizontal { self.image.get(end as u32, fixedDimension as u32) } else { self.image.get(fixedDimension as u32, end as u32) } { end = end + 1; } else { let whiteRunStart = end; end = end + 1; while end < maxDim && !(if horizontal { self.image.get(end as u32, fixedDimension as u32) } else { self.image.get(fixedDimension as u32, end as u32) }) { end = end + 1; } let whiteRunSize = end - whiteRunStart; if end >= maxDim || whiteRunSize > maxWhiteRun { end = whiteRunStart; break; } } } end = end - 1; return if end > start { Some(vec![start, end]) } else { None }; } }