You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ZXQRCodeAlignmentPatternFinder.m 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. * Copyright 2012 ZXing authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "ZXBitMatrix.h"
  17. #import "ZXErrors.h"
  18. #import "ZXIntArray.h"
  19. #import "ZXQRCodeAlignmentPattern.h"
  20. #import "ZXQRCodeAlignmentPatternFinder.h"
  21. #import "ZXResultPointCallback.h"
  22. @interface ZXQRCodeAlignmentPatternFinder ()
  23. @property (nonatomic, strong, readonly) ZXBitMatrix *image;
  24. @property (nonatomic, strong, readonly) NSMutableArray *possibleCenters;
  25. @property (nonatomic, assign, readonly) int startX;
  26. @property (nonatomic, assign, readonly) int startY;
  27. @property (nonatomic, assign, readonly) int width;
  28. @property (nonatomic, assign, readonly) int height;
  29. @property (nonatomic, assign, readonly) float moduleSize;
  30. @property (nonatomic, strong, readonly) ZXIntArray *crossCheckStateCount;
  31. @property (nonatomic, weak, readonly) id<ZXResultPointCallback> resultPointCallback;
  32. @end
  33. @implementation ZXQRCodeAlignmentPatternFinder
  34. - (id)initWithImage:(ZXBitMatrix *)image startX:(int)startX startY:(int)startY width:(int)width height:(int)height moduleSize:(float)moduleSize resultPointCallback:(id<ZXResultPointCallback>)resultPointCallback {
  35. if (self = [super init]) {
  36. _image = image;
  37. _possibleCenters = [NSMutableArray arrayWithCapacity:5];
  38. _startX = startX;
  39. _startY = startY;
  40. _width = width;
  41. _height = height;
  42. _moduleSize = moduleSize;
  43. _crossCheckStateCount = [[ZXIntArray alloc] initWithLength:3];
  44. _resultPointCallback = resultPointCallback;
  45. }
  46. return self;
  47. }
  48. - (ZXQRCodeAlignmentPattern *)findWithError:(NSError **)error {
  49. int maxJ = self.startX + self.width;
  50. int middleI = self.startY + (self.height / 2);
  51. int stateCount[3];
  52. for (int iGen = 0; iGen < self.height; iGen++) {
  53. int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) / 2 : -((iGen + 1) / 2));
  54. stateCount[0] = 0;
  55. stateCount[1] = 0;
  56. stateCount[2] = 0;
  57. int j = self.startX;
  58. while (j < maxJ && ![self.image getX:j y:i]) {
  59. j++;
  60. }
  61. int currentState = 0;
  62. while (j < maxJ) {
  63. if ([self.image getX:j y:i]) {
  64. if (currentState == 1) {
  65. stateCount[currentState]++;
  66. } else {
  67. if (currentState == 2) {
  68. if ([self foundPatternCross:stateCount]) {
  69. ZXQRCodeAlignmentPattern *confirmed = [self handlePossibleCenter:stateCount i:i j:j];
  70. if (confirmed != nil) {
  71. return confirmed;
  72. }
  73. }
  74. stateCount[0] = stateCount[2];
  75. stateCount[1] = 1;
  76. stateCount[2] = 0;
  77. currentState = 1;
  78. } else {
  79. stateCount[++currentState]++;
  80. }
  81. }
  82. } else {
  83. if (currentState == 1) {
  84. currentState++;
  85. }
  86. stateCount[currentState]++;
  87. }
  88. j++;
  89. }
  90. if ([self foundPatternCross:stateCount]) {
  91. ZXQRCodeAlignmentPattern *confirmed = [self handlePossibleCenter:stateCount i:i j:maxJ];
  92. if (confirmed != nil) {
  93. return confirmed;
  94. }
  95. }
  96. }
  97. if ([self.possibleCenters count] > 0) {
  98. return self.possibleCenters[0];
  99. }
  100. if (error) *error = ZXNotFoundErrorInstance();
  101. return nil;
  102. }
  103. /**
  104. * Given a count of black/white/black pixels just seen and an end position,
  105. * figures the location of the center of this black/white/black run.
  106. */
  107. - (float)centerFromEnd:(int *)stateCount end:(int)end {
  108. return (float)(end - stateCount[2]) - stateCount[1] / 2.0f;
  109. }
  110. /**
  111. * @param stateCount count of black/white/black pixels just read
  112. * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
  113. * used by alignment patterns to be considered a match
  114. */
  115. - (BOOL)foundPatternCross:(int *)stateCount {
  116. float maxVariance = self.moduleSize / 2.0f;
  117. for (int i = 0; i < 3; i++) {
  118. if (fabsf(self.moduleSize - stateCount[i]) >= maxVariance) {
  119. return NO;
  120. }
  121. }
  122. return YES;
  123. }
  124. /**
  125. * After a horizontal scan finds a potential alignment pattern, this method
  126. * "cross-checks" by scanning down vertically through the center of the possible
  127. * alignment pattern to see if the same proportion is detected.
  128. *
  129. * @param startI row where an alignment pattern was detected
  130. * @param centerJ center of the section that appears to cross an alignment pattern
  131. * @param maxCount maximum reasonable number of modules that should be
  132. * observed in any reading state, based on the results of the horizontal scan
  133. * @return vertical center of alignment pattern, or {@link Float#NaN} if not found
  134. */
  135. - (float)crossCheckVertical:(int)startI centerJ:(int)centerJ maxCount:(int)maxCount originalStateCountTotal:(int)originalStateCountTotal {
  136. int maxI = self.image.height;
  137. [self.crossCheckStateCount clear];
  138. int32_t *stateCount = self.crossCheckStateCount.array;
  139. int i = startI;
  140. while (i >= 0 && [self.image getX:centerJ y:i] && stateCount[1] <= maxCount) {
  141. stateCount[1]++;
  142. i--;
  143. }
  144. if (i < 0 || stateCount[1] > maxCount) {
  145. return NAN;
  146. }
  147. while (i >= 0 && ![self.image getX:centerJ y:i] && stateCount[0] <= maxCount) {
  148. stateCount[0]++;
  149. i--;
  150. }
  151. if (stateCount[0] > maxCount) {
  152. return NAN;
  153. }
  154. i = startI + 1;
  155. while (i < maxI && [self.image getX:centerJ y:i] && stateCount[1] <= maxCount) {
  156. stateCount[1]++;
  157. i++;
  158. }
  159. if (i == maxI || stateCount[1] > maxCount) {
  160. return NAN;
  161. }
  162. while (i < maxI && ![self.image getX:centerJ y:i] && stateCount[2] <= maxCount) {
  163. stateCount[2]++;
  164. i++;
  165. }
  166. if (stateCount[2] > maxCount) {
  167. return NAN;
  168. }
  169. int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
  170. if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
  171. return NAN;
  172. }
  173. return [self foundPatternCross:stateCount] ? [self centerFromEnd:stateCount end:i] : NAN;
  174. }
  175. /**
  176. * This is called when a horizontal scan finds a possible alignment pattern. It will
  177. * cross check with a vertical scan, and if successful, will see if this pattern had been
  178. * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
  179. * found the alignment pattern.
  180. *
  181. * @param stateCount reading state module counts from horizontal scan
  182. * @param i row where alignment pattern may be found
  183. * @param j end of possible alignment pattern in row
  184. * @return ZXAlignmentPattern if we have found the same pattern twice, or null if not
  185. */
  186. - (ZXQRCodeAlignmentPattern *)handlePossibleCenter:(int *)stateCount i:(int)i j:(int)j {
  187. int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
  188. float centerJ = [self centerFromEnd:stateCount end:j];
  189. float centerI = [self crossCheckVertical:i centerJ:(int)centerJ maxCount:2 * stateCount[1] originalStateCountTotal:stateCountTotal];
  190. if (!isnan(centerI)) {
  191. float estimatedModuleSize = (float)(stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
  192. int max = (int)self.possibleCenters.count;
  193. for (int index = 0; index < max; index++) {
  194. ZXQRCodeAlignmentPattern *center = self.possibleCenters[index];
  195. // Look for about the same center and module size:
  196. if ([center aboutEquals:estimatedModuleSize i:centerI j:centerJ]) {
  197. return [center combineEstimateI:centerI j:centerJ newModuleSize:estimatedModuleSize];
  198. }
  199. }
  200. // Hadn't found this before; save it
  201. ZXResultPoint *point = [[ZXQRCodeAlignmentPattern alloc] initWithPosX:centerJ posY:centerI estimatedModuleSize:estimatedModuleSize];
  202. [self.possibleCenters addObject:point];
  203. if (self.resultPointCallback != nil) {
  204. [self.resultPointCallback foundPossibleResultPoint:point];
  205. }
  206. }
  207. return nil;
  208. }
  209. @end