123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- /*
- * 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 "ZXIntArray.h"
- #import "ZXQRCodeAlignmentPattern.h"
- #import "ZXQRCodeAlignmentPatternFinder.h"
- #import "ZXResultPointCallback.h"
-
- @interface ZXQRCodeAlignmentPatternFinder ()
-
- @property (nonatomic, strong, readonly) ZXBitMatrix *image;
- @property (nonatomic, strong, readonly) NSMutableArray *possibleCenters;
- @property (nonatomic, assign, readonly) int startX;
- @property (nonatomic, assign, readonly) int startY;
- @property (nonatomic, assign, readonly) int width;
- @property (nonatomic, assign, readonly) int height;
- @property (nonatomic, assign, readonly) float moduleSize;
- @property (nonatomic, strong, readonly) ZXIntArray *crossCheckStateCount;
- @property (nonatomic, weak, readonly) id<ZXResultPointCallback> resultPointCallback;
-
- @end
-
- @implementation ZXQRCodeAlignmentPatternFinder
-
- - (id)initWithImage:(ZXBitMatrix *)image startX:(int)startX startY:(int)startY width:(int)width height:(int)height moduleSize:(float)moduleSize resultPointCallback:(id<ZXResultPointCallback>)resultPointCallback {
- if (self = [super init]) {
- _image = image;
- _possibleCenters = [NSMutableArray arrayWithCapacity:5];
- _startX = startX;
- _startY = startY;
- _width = width;
- _height = height;
- _moduleSize = moduleSize;
- _crossCheckStateCount = [[ZXIntArray alloc] initWithLength:3];
- _resultPointCallback = resultPointCallback;
- }
-
- return self;
- }
-
- - (ZXQRCodeAlignmentPattern *)findWithError:(NSError **)error {
- int maxJ = self.startX + self.width;
- int middleI = self.startY + (self.height / 2);
- int stateCount[3];
-
- for (int iGen = 0; iGen < self.height; iGen++) {
- int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) / 2 : -((iGen + 1) / 2));
- stateCount[0] = 0;
- stateCount[1] = 0;
- stateCount[2] = 0;
- int j = self.startX;
-
- while (j < maxJ && ![self.image getX:j y:i]) {
- j++;
- }
-
- int currentState = 0;
-
- while (j < maxJ) {
- if ([self.image getX:j y:i]) {
- if (currentState == 1) {
- stateCount[currentState]++;
- } else {
- if (currentState == 2) {
- if ([self foundPatternCross:stateCount]) {
- ZXQRCodeAlignmentPattern *confirmed = [self handlePossibleCenter:stateCount i:i j:j];
- if (confirmed != nil) {
- return confirmed;
- }
- }
- stateCount[0] = stateCount[2];
- stateCount[1] = 1;
- stateCount[2] = 0;
- currentState = 1;
- } else {
- stateCount[++currentState]++;
- }
- }
- } else {
- if (currentState == 1) {
- currentState++;
- }
- stateCount[currentState]++;
- }
- j++;
- }
-
- if ([self foundPatternCross:stateCount]) {
- ZXQRCodeAlignmentPattern *confirmed = [self handlePossibleCenter:stateCount i:i j:maxJ];
- if (confirmed != nil) {
- return confirmed;
- }
- }
- }
-
- if ([self.possibleCenters count] > 0) {
- return self.possibleCenters[0];
- }
- if (error) *error = ZXNotFoundErrorInstance();
- return nil;
- }
-
- /**
- * Given a count of black/white/black pixels just seen and an end position,
- * figures the location of the center of this black/white/black run.
- */
- - (float)centerFromEnd:(int *)stateCount end:(int)end {
- return (float)(end - stateCount[2]) - stateCount[1] / 2.0f;
- }
-
- /**
- * @param stateCount count of black/white/black pixels just read
- * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
- * used by alignment patterns to be considered a match
- */
- - (BOOL)foundPatternCross:(int *)stateCount {
- float maxVariance = self.moduleSize / 2.0f;
-
- for (int i = 0; i < 3; i++) {
- if (fabsf(self.moduleSize - stateCount[i]) >= maxVariance) {
- return NO;
- }
- }
-
- return YES;
- }
-
- /**
- * After a horizontal scan finds a potential alignment pattern, this method
- * "cross-checks" by scanning down vertically through the center of the possible
- * alignment pattern to see if the same proportion is detected.
- *
- * @param startI row where an alignment pattern was detected
- * @param centerJ center of the section that appears to cross an alignment pattern
- * @param maxCount maximum reasonable number of modules that should be
- * observed in any reading state, based on the results of the horizontal scan
- * @return vertical center of alignment pattern, or {@link Float#NaN} if not found
- */
- - (float)crossCheckVertical:(int)startI centerJ:(int)centerJ maxCount:(int)maxCount originalStateCountTotal:(int)originalStateCountTotal {
- int maxI = self.image.height;
- [self.crossCheckStateCount clear];
- int32_t *stateCount = self.crossCheckStateCount.array;
-
- int i = startI;
- while (i >= 0 && [self.image getX:centerJ y:i] && stateCount[1] <= maxCount) {
- stateCount[1]++;
- i--;
- }
-
- if (i < 0 || stateCount[1] > maxCount) {
- return NAN;
- }
-
- while (i >= 0 && ![self.image getX:centerJ y:i] && stateCount[0] <= maxCount) {
- stateCount[0]++;
- i--;
- }
-
- if (stateCount[0] > maxCount) {
- return NAN;
- }
- i = startI + 1;
-
- while (i < maxI && [self.image getX:centerJ y:i] && stateCount[1] <= maxCount) {
- stateCount[1]++;
- i++;
- }
-
- if (i == maxI || stateCount[1] > maxCount) {
- return NAN;
- }
-
- while (i < maxI && ![self.image getX:centerJ y:i] && stateCount[2] <= maxCount) {
- stateCount[2]++;
- i++;
- }
-
- if (stateCount[2] > maxCount) {
- return NAN;
- }
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
- if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
- return NAN;
- }
- return [self foundPatternCross:stateCount] ? [self centerFromEnd:stateCount end:i] : NAN;
- }
-
- /**
- * This is called when a horizontal scan finds a possible alignment pattern. It will
- * cross check with a vertical scan, and if successful, will see if this pattern had been
- * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
- * found the alignment pattern.
- *
- * @param stateCount reading state module counts from horizontal scan
- * @param i row where alignment pattern may be found
- * @param j end of possible alignment pattern in row
- * @return ZXAlignmentPattern if we have found the same pattern twice, or null if not
- */
- - (ZXQRCodeAlignmentPattern *)handlePossibleCenter:(int *)stateCount i:(int)i j:(int)j {
- int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
- float centerJ = [self centerFromEnd:stateCount end:j];
- float centerI = [self crossCheckVertical:i centerJ:(int)centerJ maxCount:2 * stateCount[1] originalStateCountTotal:stateCountTotal];
- if (!isnan(centerI)) {
- float estimatedModuleSize = (float)(stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
- int max = (int)self.possibleCenters.count;
-
- for (int index = 0; index < max; index++) {
- ZXQRCodeAlignmentPattern *center = self.possibleCenters[index];
- // Look for about the same center and module size:
- if ([center aboutEquals:estimatedModuleSize i:centerI j:centerJ]) {
- return [center combineEstimateI:centerI j:centerJ newModuleSize:estimatedModuleSize];
- }
- }
- // Hadn't found this before; save it
- ZXResultPoint *point = [[ZXQRCodeAlignmentPattern alloc] initWithPosX:centerJ posY:centerI estimatedModuleSize:estimatedModuleSize];
- [self.possibleCenters addObject:point];
- if (self.resultPointCallback != nil) {
- [self.resultPointCallback foundPossibleResultPoint:point];
- }
- }
- return nil;
- }
-
- @end
|