blob: 5681ea47f14a0d1b45f5605eb0f2ecbe74fff5f5 [file] [log] [blame] [edit]
/*
* 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 "ZXAlignmentPattern.h"
#import "ZXAlignmentPatternFinder.h"
#import "ZXBitMatrix.h"
#import "ZXErrors.h"
#import "ZXResultPointCallback.h"
@interface ZXAlignmentPatternFinder ()
@property (nonatomic, strong) ZXBitMatrix *image;
@property (nonatomic, strong) NSMutableArray *possibleCenters;
@property (nonatomic, assign) int startX;
@property (nonatomic, assign) int startY;
@property (nonatomic, assign) int width;
@property (nonatomic, assign) int height;
@property (nonatomic, assign) float moduleSize;
@property (nonatomic, assign) int *crossCheckStateCount;
@property (nonatomic, weak) id <ZXResultPointCallback> resultPointCallback;
@end
@implementation ZXAlignmentPatternFinder
/**
* Creates a finder that will look in a portion of the whole image.
*/
- (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 = (int *)malloc(3 * sizeof(int));
memset(_crossCheckStateCount, 0, 3 * sizeof(int));
_resultPointCallback = resultPointCallback;
}
return self;
}
- (void)dealloc {
if (_crossCheckStateCount != NULL) {
free(_crossCheckStateCount);
_crossCheckStateCount = NULL;
}
}
/**
* This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
* it's pretty performance-critical and so is written to be fast foremost.
*/
- (ZXAlignmentPattern *)findWithError:(NSError **)error {
int maxJ = self.startX + self.width;
int middleI = self.startY + (self.height >> 1);
int stateCount[3];
for (int iGen = 0; iGen < self.height; iGen++) {
int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) >> 1 : -((iGen + 1) >> 1));
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]) {
ZXAlignmentPattern *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]) {
ZXAlignmentPattern *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 = NotFoundErrorInstance();
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;
}
- (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.
*/
- (float)crossCheckVertical:(int)startI centerJ:(int)centerJ maxCount:(int)maxCount originalStateCountTotal:(int)originalStateCountTotal {
int maxI = self.image.height;
int stateCount[3] = {0, 0, 0};
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.
*/
- (ZXAlignmentPattern *)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++) {
ZXAlignmentPattern *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 = [[ZXAlignmentPattern alloc] initWithPosX:centerJ posY:centerI estimatedModuleSize:estimatedModuleSize];
[self.possibleCenters addObject:point];
if (self.resultPointCallback != nil) {
[self.resultPointCallback foundPossibleResultPoint:point];
}
}
return nil;
}
@end