123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /*
- * 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 "ZXErrors.h"
- #import "ZXMathUtils.h"
- #import "ZXWhiteRectangleDetector.h"
-
- @interface ZXWhiteRectangleDetector ()
-
- @property (nonatomic, strong, readonly) ZXBitMatrix *image;
- @property (nonatomic, assign, readonly) int height;
- @property (nonatomic, assign, readonly) int width;
- @property (nonatomic, assign, readonly) int leftInit;
- @property (nonatomic, assign, readonly) int rightInit;
- @property (nonatomic, assign, readonly) int downInit;
- @property (nonatomic, assign, readonly) int upInit;
-
- @end
-
- const int ZX_INIT_SIZE = 10;
- const int ZX_CORR = 1;
-
- @implementation ZXWhiteRectangleDetector
-
- - (id)initWithImage:(ZXBitMatrix *)image error:(NSError **)error {
- return [self initWithImage:image initSize:ZX_INIT_SIZE x:image.width / 2 y:image.height / 2 error:error];
- }
-
- - (id)initWithImage:(ZXBitMatrix *)image initSize:(int)initSize x:(int)x y:(int)y error:(NSError **)error {
- if (self = [super init]) {
- _image = image;
- _height = image.height;
- _width = image.width;
- int halfsize = initSize / 2;
- _leftInit = x - halfsize;
- _rightInit = x + halfsize;
- _upInit = y - halfsize;
- _downInit = y + halfsize;
- if (_upInit < 0 || _leftInit < 0 || _downInit >= _height || _rightInit >= _width) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- }
-
- return self;
- }
-
- - (NSArray *)detectWithError:(NSError **)error {
- int left = self.leftInit;
- int right = self.rightInit;
- int up = self.upInit;
- int down = self.downInit;
- BOOL sizeExceeded = NO;
- BOOL aBlackPointFoundOnBorder = YES;
- BOOL atLeastOneBlackPointFoundOnBorder = NO;
-
- BOOL atLeastOneBlackPointFoundOnRight = NO;
- BOOL atLeastOneBlackPointFoundOnBottom = NO;
- BOOL atLeastOneBlackPointFoundOnLeft = NO;
- BOOL atLeastOneBlackPointFoundOnTop = NO;
-
- while (aBlackPointFoundOnBorder) {
- aBlackPointFoundOnBorder = NO;
-
- // .....
- // . |
- // .....
- BOOL rightBorderNotWhite = YES;
- while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < self.width) {
- rightBorderNotWhite = [self containsBlackPoint:up b:down fixed:right horizontal:NO];
- if (rightBorderNotWhite) {
- right++;
- aBlackPointFoundOnBorder = YES;
- atLeastOneBlackPointFoundOnRight = YES;
- } else if (!atLeastOneBlackPointFoundOnRight) {
- right++;
- }
- }
-
- if (right >= self.width) {
- sizeExceeded = YES;
- break;
- }
-
- // .....
- // . .
- // .___.
- BOOL bottomBorderNotWhite = YES;
- while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < self.height) {
- bottomBorderNotWhite = [self containsBlackPoint:left b:right fixed:down horizontal:YES];
- if (bottomBorderNotWhite) {
- down++;
- aBlackPointFoundOnBorder = YES;
- atLeastOneBlackPointFoundOnBottom = YES;
- } else if (!atLeastOneBlackPointFoundOnBottom) {
- down++;
- }
- }
-
- if (down >= self.height) {
- sizeExceeded = YES;
- break;
- }
-
- // .....
- // | .
- // .....
- BOOL leftBorderNotWhite = YES;
- while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) {
- leftBorderNotWhite = [self containsBlackPoint:up b:down fixed:left horizontal:NO];
- if (leftBorderNotWhite) {
- left--;
- aBlackPointFoundOnBorder = YES;
- atLeastOneBlackPointFoundOnLeft = YES;
- } else if (!atLeastOneBlackPointFoundOnLeft) {
- left--;
- }
- }
-
- if (left < 0) {
- sizeExceeded = YES;
- break;
- }
-
- // .___.
- // . .
- // .....
- BOOL topBorderNotWhite = YES;
- while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) {
- topBorderNotWhite = [self containsBlackPoint:left b:right fixed:up horizontal:YES];
- if (topBorderNotWhite) {
- up--;
- aBlackPointFoundOnBorder = YES;
- atLeastOneBlackPointFoundOnTop = YES;
- } else if (!atLeastOneBlackPointFoundOnTop) {
- up--;
- }
- }
-
- if (up < 0) {
- sizeExceeded = YES;
- break;
- }
-
- if (aBlackPointFoundOnBorder) {
- atLeastOneBlackPointFoundOnBorder = YES;
- }
- }
-
- if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
- int maxSize = right - left;
-
- ZXResultPoint *z = nil;
- for (int i = 1; i < maxSize; i++) {
- z = [self blackPointOnSegment:left aY:down - i bX:left + i bY:down];
- if (z != nil) {
- break;
- }
- }
-
- if (z == nil) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- ZXResultPoint *t = nil;
- for (int i = 1; i < maxSize; i++) {
- t = [self blackPointOnSegment:left aY:up + i bX:left + i bY:up];
- if (t != nil) {
- break;
- }
- }
-
- if (t == nil) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- ZXResultPoint *x = nil;
- for (int i = 1; i < maxSize; i++) {
- x = [self blackPointOnSegment:right aY:up + i bX:right - i bY:up];
- if (x != nil) {
- break;
- }
- }
-
- if (x == nil) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- ZXResultPoint *y = nil;
- for (int i = 1; i < maxSize; i++) {
- y = [self blackPointOnSegment:right aY:down - i bX:right - i bY:down];
- if (y != nil) {
- break;
- }
- }
-
- if (y == nil) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- return [self centerEdges:y z:z x:x t:t];
- } else {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
- }
-
-
- - (ZXResultPoint *)blackPointOnSegment:(float)aX aY:(float)aY bX:(float)bX bY:(float)bY {
- int dist = [ZXMathUtils round:[ZXMathUtils distance:aX aY:aY bX:bX bY:bY]];
- float xStep = (bX - aX) / dist;
- float yStep = (bY - aY) / dist;
-
- for (int i = 0; i < dist; i++) {
- int x = [ZXMathUtils round:aX + i * xStep];
- int y = [ZXMathUtils round:aY + i * yStep];
- if ([self.image getX:x y:y]) {
- return [[ZXResultPoint alloc] initWithX:x y:y];
- }
- }
-
- return nil;
- }
-
- /**
- * recenters the points of a constant distance towards the center
- *
- * @param y bottom most point
- * @param z left most point
- * @param x right most point
- * @param t top most point
- * @return ZXResultPoint array 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
- */
- - (NSArray *)centerEdges:(ZXResultPoint *)y z:(ZXResultPoint *)z x:(ZXResultPoint *)x t:(ZXResultPoint *)t {
- //
- // t t
- // z x
- // x OR z
- // y y
- //
-
- float yi = y.x;
- float yj = y.y;
- float zi = z.x;
- float zj = z.y;
- float xi = x.x;
- float xj = x.y;
- float ti = t.x;
- float tj = t.y;
-
- if (yi < self.width / 2.0f) {
- return @[[[ZXResultPoint alloc] initWithX:ti - ZX_CORR y:tj + ZX_CORR],
- [[ZXResultPoint alloc] initWithX:zi + ZX_CORR y:zj + ZX_CORR],
- [[ZXResultPoint alloc] initWithX:xi - ZX_CORR y:xj - ZX_CORR],
- [[ZXResultPoint alloc] initWithX:yi + ZX_CORR y:yj - ZX_CORR]];
- } else {
- return @[[[ZXResultPoint alloc] initWithX:ti + ZX_CORR y:tj + ZX_CORR],
- [[ZXResultPoint alloc] initWithX:zi + ZX_CORR y:zj - ZX_CORR],
- [[ZXResultPoint alloc] initWithX:xi - ZX_CORR y:xj + ZX_CORR],
- [[ZXResultPoint alloc] initWithX:yi - ZX_CORR y:yj - ZX_CORR]];
- }
- }
-
- /**
- * Determines whether a segment contains a black point
- *
- * @param a min value of the scanned coordinate
- * @param b max value of the scanned coordinate
- * @param fixed value of fixed coordinate
- * @param horizontal set to true if scan must be horizontal, false if vertical
- * @return true if a black point has been found, else false.
- */
- - (BOOL)containsBlackPoint:(int)a b:(int)b fixed:(int)fixed horizontal:(BOOL)horizontal {
- if (horizontal) {
- for (int x = a; x <= b; x++) {
- if ([self.image getX:x y:fixed]) {
- return YES;
- }
- }
- } else {
- for (int y = a; y <= b; y++) {
- if ([self.image getX:fixed y:y]) {
- return YES;
- }
- }
- }
-
- return NO;
- }
-
- @end
|