123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- /*
- * 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 "ZXDataMatrixDetector.h"
- #import "ZXDetectorResult.h"
- #import "ZXErrors.h"
- #import "ZXGridSampler.h"
- #import "ZXMathUtils.h"
- #import "ZXResultPoint.h"
- #import "ZXWhiteRectangleDetector.h"
-
- /**
- * Simply encapsulates two points and a number of transitions between them.
- */
- @interface ZXResultPointsAndTransitions : NSObject
-
- @property (nonatomic, strong, readonly) ZXResultPoint *from;
- @property (nonatomic, strong, readonly) ZXResultPoint *to;
- @property (nonatomic, assign, readonly) int transitions;
-
- @end
-
- @implementation ZXResultPointsAndTransitions
-
- - (id)initWithFrom:(ZXResultPoint *)from to:(ZXResultPoint *)to transitions:(int)transitions {
- if (self = [super init]) {
- _from = from;
- _to = to;
- _transitions = transitions;
- }
-
- return self;
- }
-
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@/%@/%d", self.from, self.to, self.transitions];
- }
-
- - (NSComparisonResult)compare:(ZXResultPointsAndTransitions *)otherObject {
- return [@(self.transitions) compare:@(otherObject.transitions)];
- }
-
- @end
-
-
- @interface ZXDataMatrixDetector ()
-
- @property (nonatomic, strong, readonly) ZXBitMatrix *image;
- @property (nonatomic, strong, readonly) ZXWhiteRectangleDetector *rectangleDetector;
-
- @end
-
- @implementation ZXDataMatrixDetector
-
- - (id)initWithImage:(ZXBitMatrix *)image error:(NSError **)error {
- if (self = [super init]) {
- _image = image;
- _rectangleDetector = [[ZXWhiteRectangleDetector alloc] initWithImage:_image error:error];
- if (!_rectangleDetector) {
- return nil;
- }
- }
-
- return self;
- }
-
- - (ZXDetectorResult *)detectWithError:(NSError **)error {
- NSArray *cornerPoints = [self.rectangleDetector detectWithError:error];
- if (!cornerPoints) {
- return nil;
- }
- ZXResultPoint *pointA = cornerPoints[0];
- ZXResultPoint *pointB = cornerPoints[1];
- ZXResultPoint *pointC = cornerPoints[2];
- ZXResultPoint *pointD = cornerPoints[3];
-
- NSMutableArray *transitions = [NSMutableArray arrayWithCapacity:4];
- [transitions addObject:[self transitionsBetween:pointA to:pointB]];
- [transitions addObject:[self transitionsBetween:pointA to:pointC]];
- [transitions addObject:[self transitionsBetween:pointB to:pointD]];
- [transitions addObject:[self transitionsBetween:pointC to:pointD]];
- [transitions sortUsingSelector:@selector(compare:)];
-
- ZXResultPointsAndTransitions *lSideOne = (ZXResultPointsAndTransitions *)transitions[0];
- ZXResultPointsAndTransitions *lSideTwo = (ZXResultPointsAndTransitions *)transitions[1];
-
- NSMutableDictionary *pointCount = [NSMutableDictionary dictionary];
- [self increment:pointCount key:[lSideOne from]];
- [self increment:pointCount key:[lSideOne to]];
- [self increment:pointCount key:[lSideTwo from]];
- [self increment:pointCount key:[lSideTwo to]];
-
- ZXResultPoint *maybeTopLeft = nil;
- ZXResultPoint *bottomLeft = nil;
- ZXResultPoint *maybeBottomRight = nil;
- for (ZXResultPoint *point in [pointCount allKeys]) {
- NSNumber *value = pointCount[point];
- if ([value intValue] == 2) {
- bottomLeft = point;
- } else {
- if (maybeTopLeft == nil) {
- maybeTopLeft = point;
- } else {
- maybeBottomRight = point;
- }
- }
- }
-
- if (maybeTopLeft == nil || bottomLeft == nil || maybeBottomRight == nil) {
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- NSMutableArray *corners = [NSMutableArray arrayWithObjects:maybeTopLeft, bottomLeft, maybeBottomRight, nil];
- [ZXResultPoint orderBestPatterns:corners];
-
- ZXResultPoint *bottomRight = corners[0];
- bottomLeft = corners[1];
- ZXResultPoint *topLeft = corners[2];
-
- ZXResultPoint *topRight;
- if (!pointCount[pointA]) {
- topRight = pointA;
- } else if (!pointCount[pointB]) {
- topRight = pointB;
- } else if (!pointCount[pointC]) {
- topRight = pointC;
- } else {
- topRight = pointD;
- }
-
- int dimensionTop = [[self transitionsBetween:topLeft to:topRight] transitions];
- int dimensionRight = [[self transitionsBetween:bottomRight to:topRight] transitions];
-
- if ((dimensionTop & 0x01) == 1) {
- dimensionTop++;
- }
- dimensionTop += 2;
-
- if ((dimensionRight & 0x01) == 1) {
- dimensionRight++;
- }
- dimensionRight += 2;
-
- ZXBitMatrix *bits;
- ZXResultPoint *correctedTopRight;
-
- if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
- correctedTopRight = [self correctTopRightRectangular:bottomLeft bottomRight:bottomRight topLeft:topLeft topRight:topRight dimensionTop:dimensionTop dimensionRight:dimensionRight];
- if (correctedTopRight == nil) {
- correctedTopRight = topRight;
- }
-
- dimensionTop = [[self transitionsBetween:topLeft to:correctedTopRight] transitions];
- dimensionRight = [[self transitionsBetween:bottomRight to:correctedTopRight] transitions];
-
- if ((dimensionTop & 0x01) == 1) {
- dimensionTop++;
- }
-
- if ((dimensionRight & 0x01) == 1) {
- dimensionRight++;
- }
-
- bits = [self sampleGrid:self.image topLeft:topLeft bottomLeft:bottomLeft bottomRight:bottomRight topRight:correctedTopRight dimensionX:dimensionTop dimensionY:dimensionRight error:error];
- if (!bits) {
- return nil;
- }
- } else {
- int dimension = MIN(dimensionRight, dimensionTop);
- correctedTopRight = [self correctTopRight:bottomLeft bottomRight:bottomRight topLeft:topLeft topRight:topRight dimension:dimension];
- if (correctedTopRight == nil) {
- correctedTopRight = topRight;
- }
-
- int dimensionCorrected = MAX([[self transitionsBetween:topLeft to:correctedTopRight] transitions], [[self transitionsBetween:bottomRight to:correctedTopRight] transitions]);
- dimensionCorrected++;
- if ((dimensionCorrected & 0x01) == 1) {
- dimensionCorrected++;
- }
-
- bits = [self sampleGrid:self.image topLeft:topLeft bottomLeft:bottomLeft bottomRight:bottomRight topRight:correctedTopRight dimensionX:dimensionCorrected dimensionY:dimensionCorrected error:error];
- if (!bits) {
- return nil;
- }
- }
- return [[ZXDetectorResult alloc] initWithBits:bits points:@[topLeft, bottomLeft, bottomRight, correctedTopRight]];
- }
-
- /**
- * Calculates the position of the white top right module using the output of the rectangle detector
- * for a rectangular matrix
- */
- - (ZXResultPoint *)correctTopRightRectangular:(ZXResultPoint *)bottomLeft bottomRight:(ZXResultPoint *)bottomRight
- topLeft:(ZXResultPoint *)topLeft topRight:(ZXResultPoint *)topRight
- dimensionTop:(int)dimensionTop dimensionRight:(int)dimensionRight {
- float corr = [self distance:bottomLeft b:bottomRight] / (float)dimensionTop;
- int norm = [self distance:topLeft b:topRight];
- float cos = ([topRight x] - [topLeft x]) / norm;
- float sin = ([topRight y] - [topLeft y]) / norm;
-
- ZXResultPoint *c1 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
-
- corr = [self distance:bottomLeft b:topLeft] / (float)dimensionRight;
- norm = [self distance:bottomRight b:topRight];
- cos = ([topRight x] - [bottomRight x]) / norm;
- sin = ([topRight y] - [bottomRight y]) / norm;
-
- ZXResultPoint *c2 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
-
- if (![self isValid:c1]) {
- if ([self isValid:c2]) {
- return c2;
- }
- return nil;
- } else if (![self isValid:c2]) {
- return c1;
- }
-
- int l1 = abs(dimensionTop - [[self transitionsBetween:topLeft to:c1] transitions]) + abs(dimensionRight - [[self transitionsBetween:bottomRight to:c1] transitions]);
- int l2 = abs(dimensionTop - [[self transitionsBetween:topLeft to:c2] transitions]) + abs(dimensionRight - [[self transitionsBetween:bottomRight to:c2] transitions]);
-
- if (l1 <= l2) {
- return c1;
- }
-
- return c2;
- }
-
- /**
- * Calculates the position of the white top right module using the output of the rectangle detector
- * for a square matrix
- */
- - (ZXResultPoint *)correctTopRight:(ZXResultPoint *)bottomLeft bottomRight:(ZXResultPoint *)bottomRight
- topLeft:(ZXResultPoint *)topLeft topRight:(ZXResultPoint *)topRight dimension:(int)dimension {
- float corr = [self distance:bottomLeft b:bottomRight] / (float)dimension;
- int norm = [self distance:topLeft b:topRight];
- float cos = ([topRight x] - [topLeft x]) / norm;
- float sin = ([topRight y] - [topLeft y]) / norm;
-
- ZXResultPoint *c1 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
-
- corr = [self distance:bottomLeft b:topLeft] / (float)dimension;
- norm = [self distance:bottomRight b:topRight];
- cos = ([topRight x] - [bottomRight x]) / norm;
- sin = ([topRight y] - [bottomRight y]) / norm;
-
- ZXResultPoint *c2 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
-
- if (![self isValid:c1]) {
- if ([self isValid:c2]) {
- return c2;
- }
- return nil;
- } else if (![self isValid:c2]) {
- return c1;
- }
-
- int l1 = abs([[self transitionsBetween:topLeft to:c1] transitions] - [[self transitionsBetween:bottomRight to:c1] transitions]);
- int l2 = abs([[self transitionsBetween:topLeft to:c2] transitions] - [[self transitionsBetween:bottomRight to:c2] transitions]);
-
- return l1 <= l2 ? c1 : c2;
- }
-
- - (BOOL) isValid:(ZXResultPoint *)p {
- return [p x] >= 0 && [p x] < self.image.width && [p y] > 0 && [p y] < self.image.height;
- }
-
- - (int)distance:(ZXResultPoint *)a b:(ZXResultPoint *)b {
- return [ZXMathUtils round:[ZXResultPoint distance:a pattern2:b]];
- }
-
- /**
- * Increments the Integer associated with a key by one.
- */
- - (void)increment:(NSMutableDictionary *)table key:(ZXResultPoint *)key {
- NSNumber *value = table[key];
- table[key] = value == nil ? @1 : @([value intValue] + 1);
- }
-
- - (ZXBitMatrix *)sampleGrid:(ZXBitMatrix *)image
- topLeft:(ZXResultPoint *)topLeft
- bottomLeft:(ZXResultPoint *)bottomLeft
- bottomRight:(ZXResultPoint *)bottomRight
- topRight:(ZXResultPoint *)topRight
- dimensionX:(int)dimensionX
- dimensionY:(int)dimensionY
- error:(NSError **)error {
- ZXGridSampler *sampler = [ZXGridSampler instance];
- return [sampler sampleGrid:image
- dimensionX:dimensionX dimensionY:dimensionY
- p1ToX:0.5f p1ToY:0.5f
- p2ToX:dimensionX - 0.5f p2ToY:0.5f
- p3ToX:dimensionX - 0.5f p3ToY:dimensionY - 0.5f
- p4ToX:0.5f p4ToY:dimensionY - 0.5f
- p1FromX:[topLeft x] p1FromY:[topLeft y]
- p2FromX:[topRight x] p2FromY:[topRight y]
- p3FromX:[bottomRight x] p3FromY:[bottomRight y]
- p4FromX:[bottomLeft x] p4FromY:[bottomLeft y]
- error:error];
- }
-
- /**
- * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
- */
- - (ZXResultPointsAndTransitions *)transitionsBetween:(ZXResultPoint *)from to:(ZXResultPoint *)to {
- int fromX = (int)[from x];
- int fromY = (int)[from y];
- int toX = (int)[to x];
- int toY = (int)[to y];
- BOOL steep = abs(toY - fromY) > abs(toX - fromX);
- if (steep) {
- int temp = fromX;
- fromX = fromY;
- fromY = temp;
- temp = toX;
- toX = toY;
- toY = temp;
- }
-
- int dx = abs(toX - fromX);
- int dy = abs(toY - fromY);
- int error = -dx / 2;
- int ystep = fromY < toY ? 1 : -1;
- int xstep = fromX < toX ? 1 : -1;
- int transitions = 0;
- BOOL inBlack = [self.image getX:steep ? fromY : fromX y:steep ? fromX : fromY];
- for (int x = fromX, y = fromY; x != toX; x += xstep) {
- BOOL isBlack = [self.image getX:steep ? y : x y:steep ? x : y];
- if (isBlack != inBlack) {
- transitions++;
- inBlack = isBlack;
- }
- error += dy;
- if (error > 0) {
- if (y == toY) {
- break;
- }
- y += ystep;
- error -= dx;
- }
- }
- return [[ZXResultPointsAndTransitions alloc] initWithFrom:from to:to transitions:transitions];
- }
-
- @end
|