123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- /*
- * Copyright 2012 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.
- */
-
- #import "ZXBitMatrix.h"
- #import "ZXErrors.h"
- #import "ZXMonochromeRectangleDetector.h"
- #import "ZXResultPoint.h"
-
- const int ZX_MONOCHROME_MAX_MODULES = 32;
-
- @interface ZXMonochromeRectangleDetector ()
-
- @property (nonatomic, strong, readonly) ZXBitMatrix *image;
-
- @end
-
- @implementation ZXMonochromeRectangleDetector
-
- - (id)initWithImage:(ZXBitMatrix *)image {
- if (self = [super init]) {
- _image = image;
- }
-
- return self;
- }
-
- - (NSArray *)detectWithError:(NSError **)error {
- int height = [self.image height];
- int width = [self.image width];
- int halfHeight = height / 2;
- int halfWidth = width / 2;
- int deltaY = MAX(1, height / (ZX_MONOCHROME_MAX_MODULES * 8) > 1);
- int deltaX = MAX(1, width / (ZX_MONOCHROME_MAX_MODULES * 8) > 1);
-
- int top = 0;
- int bottom = height;
- int left = 0;
- int right = width;
- ZXResultPoint *pointA = [self findCornerFromCenter:halfWidth deltaX:0 left:left right:right
- centerY:halfHeight deltaY:-deltaY top:top bottom:bottom maxWhiteRun:halfWidth / 2];
- if (!pointA) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- top = (int)[pointA y] - 1;
- ZXResultPoint *pointB = [self findCornerFromCenter:halfWidth deltaX:-deltaX left:left right:right
- centerY:halfHeight deltaY:0 top:top bottom:bottom maxWhiteRun:halfHeight / 2];
- if (!pointB) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- left = (int)[pointB x] - 1;
- ZXResultPoint *pointC = [self findCornerFromCenter:halfWidth deltaX:deltaX left:left right:right
- centerY:halfHeight deltaY:0 top:top bottom:bottom maxWhiteRun:halfHeight / 2];
- if (!pointC) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- right = (int)[pointC x] + 1;
- ZXResultPoint *pointD = [self findCornerFromCenter:halfWidth deltaX:0 left:left right:right
- centerY:halfHeight deltaY:deltaY top:top bottom:bottom maxWhiteRun:halfWidth / 2];
- if (!pointD) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- bottom = (int)[pointD y] + 1;
-
- pointA = [self findCornerFromCenter:halfWidth deltaX:0 left:left right:right
- centerY:halfHeight deltaY:-deltaY top:top bottom:bottom maxWhiteRun:halfWidth / 4];
- if (!pointA) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- return @[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 com.google.zxing.ResultPoint} encapsulating the corner that was found
- * or nil if such a point cannot be found
- */
- - (ZXResultPoint *)findCornerFromCenter:(int)centerX deltaX:(int)deltaX left:(int)left right:(int)right centerY:(int)centerY deltaY:(int)deltaY top:(int)top bottom:(int)bottom maxWhiteRun:(int)maxWhiteRun {
- NSArray *lastRange = nil;
- for (int y = centerY, x = centerX; y < bottom && y >= top && x < right && x >= left; y += deltaY, x += deltaX) {
- NSArray *range;
- if (deltaX == 0) {
- range = [self blackWhiteRange:y maxWhiteRun:maxWhiteRun minDim:left maxDim:right horizontal:YES];
- } else {
- range = [self blackWhiteRange:x maxWhiteRun:maxWhiteRun minDim:top maxDim:bottom horizontal:NO];
- }
- if (range == nil) {
- if (lastRange == nil) {
- return nil;
- }
- if (deltaX == 0) {
- int lastY = y - deltaY;
- if ([lastRange[0] intValue] < centerX) {
- if ([lastRange[0] intValue] > centerX) {
- return [[ZXResultPoint alloc] initWithX:deltaY > 0 ? [lastRange[0] intValue] : [lastRange[1] intValue] y:lastY];
- }
- return [[ZXResultPoint alloc] initWithX:[lastRange[0] intValue] y:lastY];
- } else {
- return [[ZXResultPoint alloc] initWithX:[lastRange[1] intValue] y:lastY];
- }
- } else {
- int lastX = x - deltaX;
- if ([lastRange[0] intValue] < centerY) {
- if ([lastRange[1] intValue] > centerY) {
- return [[ZXResultPoint alloc] initWithX:lastX y:deltaX < 0 ? [lastRange[0] intValue] : [lastRange[1] intValue]];
- }
- return [[ZXResultPoint alloc] initWithX:lastX y:[lastRange[0] intValue]];
- } else {
- return [[ZXResultPoint alloc] initWithX:lastX y:[lastRange[1] intValue]];
- }
- }
- }
- lastRange = range;
- }
-
- return nil;
- }
-
- /**
- * 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 nil if no such range is found
- * (e.g. only white was found)
- */
- - (NSArray *)blackWhiteRange:(int)fixedDimension maxWhiteRun:(int)maxWhiteRun minDim:(int)minDim maxDim:(int)maxDim horizontal:(BOOL)horizontal {
- int center = (minDim + maxDim) / 2;
-
- int start = center;
- while (start >= minDim) {
- if (horizontal ? [self.image getX:start y:fixedDimension] : [self.image getX:fixedDimension y:start]) {
- start--;
- } else {
- int whiteRunStart = start;
-
- do {
- start--;
- } while (start >= minDim && !(horizontal ? [self.image getX:start y:fixedDimension] : [self.image getX:fixedDimension y:start]));
- int whiteRunSize = whiteRunStart - start;
- if (start < minDim || whiteRunSize > maxWhiteRun) {
- start = whiteRunStart;
- break;
- }
- }
- }
-
- start++;
- int end = center;
-
- while (end < maxDim) {
- if (horizontal ? [self.image getX:end y:fixedDimension] : [self.image getX:fixedDimension y:end]) {
- end++;
- } else {
- int whiteRunStart = end;
-
- do {
- end++;
- } while (end < maxDim && !(horizontal ? [self.image getX:end y:fixedDimension] : [self.image getX:fixedDimension y:end]));
- int whiteRunSize = end - whiteRunStart;
- if (end >= maxDim || whiteRunSize > maxWhiteRun) {
- end = whiteRunStart;
- break;
- }
- }
- }
-
- end--;
- return end > start ? @[@(start), @(end)] : nil;
- }
-
- @end
|