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.

ZXMonochromeRectangleDetector.m 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 "ZXMonochromeRectangleDetector.h"
  19. #import "ZXResultPoint.h"
  20. const int ZX_MONOCHROME_MAX_MODULES = 32;
  21. @interface ZXMonochromeRectangleDetector ()
  22. @property (nonatomic, strong, readonly) ZXBitMatrix *image;
  23. @end
  24. @implementation ZXMonochromeRectangleDetector
  25. - (id)initWithImage:(ZXBitMatrix *)image {
  26. if (self = [super init]) {
  27. _image = image;
  28. }
  29. return self;
  30. }
  31. - (NSArray *)detectWithError:(NSError **)error {
  32. int height = [self.image height];
  33. int width = [self.image width];
  34. int halfHeight = height / 2;
  35. int halfWidth = width / 2;
  36. int deltaY = MAX(1, height / (ZX_MONOCHROME_MAX_MODULES * 8) > 1);
  37. int deltaX = MAX(1, width / (ZX_MONOCHROME_MAX_MODULES * 8) > 1);
  38. int top = 0;
  39. int bottom = height;
  40. int left = 0;
  41. int right = width;
  42. ZXResultPoint *pointA = [self findCornerFromCenter:halfWidth deltaX:0 left:left right:right
  43. centerY:halfHeight deltaY:-deltaY top:top bottom:bottom maxWhiteRun:halfWidth / 2];
  44. if (!pointA) {
  45. if (error) *error = ZXNotFoundErrorInstance();
  46. return nil;
  47. }
  48. top = (int)[pointA y] - 1;
  49. ZXResultPoint *pointB = [self findCornerFromCenter:halfWidth deltaX:-deltaX left:left right:right
  50. centerY:halfHeight deltaY:0 top:top bottom:bottom maxWhiteRun:halfHeight / 2];
  51. if (!pointB) {
  52. if (error) *error = ZXNotFoundErrorInstance();
  53. return nil;
  54. }
  55. left = (int)[pointB x] - 1;
  56. ZXResultPoint *pointC = [self findCornerFromCenter:halfWidth deltaX:deltaX left:left right:right
  57. centerY:halfHeight deltaY:0 top:top bottom:bottom maxWhiteRun:halfHeight / 2];
  58. if (!pointC) {
  59. if (error) *error = ZXNotFoundErrorInstance();
  60. return nil;
  61. }
  62. right = (int)[pointC x] + 1;
  63. ZXResultPoint *pointD = [self findCornerFromCenter:halfWidth deltaX:0 left:left right:right
  64. centerY:halfHeight deltaY:deltaY top:top bottom:bottom maxWhiteRun:halfWidth / 2];
  65. if (!pointD) {
  66. if (error) *error = ZXNotFoundErrorInstance();
  67. return nil;
  68. }
  69. bottom = (int)[pointD y] + 1;
  70. pointA = [self findCornerFromCenter:halfWidth deltaX:0 left:left right:right
  71. centerY:halfHeight deltaY:-deltaY top:top bottom:bottom maxWhiteRun:halfWidth / 4];
  72. if (!pointA) {
  73. if (error) *error = ZXNotFoundErrorInstance();
  74. return nil;
  75. }
  76. return @[pointA, pointB, pointC, pointD];
  77. }
  78. /**
  79. * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
  80. * point which should be within the barcode.
  81. *
  82. * @param centerX center's x component (horizontal)
  83. * @param deltaX same as deltaY but change in x per step instead
  84. * @param left minimum value of x
  85. * @param right maximum value of x
  86. * @param centerY center's y component (vertical)
  87. * @param deltaY change in y per step. If scanning up this is negative; down, positive;
  88. * left or right, 0
  89. * @param top minimum value of y to search through (meaningless when di == 0)
  90. * @param bottom maximum value of y
  91. * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
  92. * the barcode
  93. * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
  94. * or nil if such a point cannot be found
  95. */
  96. - (ZXResultPoint *)findCornerFromCenter:(int)centerX deltaX:(int)deltaX left:(int)left right:(int)right centerY:(int)centerY deltaY:(int)deltaY top:(int)top bottom:(int)bottom maxWhiteRun:(int)maxWhiteRun {
  97. NSArray *lastRange = nil;
  98. for (int y = centerY, x = centerX; y < bottom && y >= top && x < right && x >= left; y += deltaY, x += deltaX) {
  99. NSArray *range;
  100. if (deltaX == 0) {
  101. range = [self blackWhiteRange:y maxWhiteRun:maxWhiteRun minDim:left maxDim:right horizontal:YES];
  102. } else {
  103. range = [self blackWhiteRange:x maxWhiteRun:maxWhiteRun minDim:top maxDim:bottom horizontal:NO];
  104. }
  105. if (range == nil) {
  106. if (lastRange == nil) {
  107. return nil;
  108. }
  109. if (deltaX == 0) {
  110. int lastY = y - deltaY;
  111. if ([lastRange[0] intValue] < centerX) {
  112. if ([lastRange[0] intValue] > centerX) {
  113. return [[ZXResultPoint alloc] initWithX:deltaY > 0 ? [lastRange[0] intValue] : [lastRange[1] intValue] y:lastY];
  114. }
  115. return [[ZXResultPoint alloc] initWithX:[lastRange[0] intValue] y:lastY];
  116. } else {
  117. return [[ZXResultPoint alloc] initWithX:[lastRange[1] intValue] y:lastY];
  118. }
  119. } else {
  120. int lastX = x - deltaX;
  121. if ([lastRange[0] intValue] < centerY) {
  122. if ([lastRange[1] intValue] > centerY) {
  123. return [[ZXResultPoint alloc] initWithX:lastX y:deltaX < 0 ? [lastRange[0] intValue] : [lastRange[1] intValue]];
  124. }
  125. return [[ZXResultPoint alloc] initWithX:lastX y:[lastRange[0] intValue]];
  126. } else {
  127. return [[ZXResultPoint alloc] initWithX:lastX y:[lastRange[1] intValue]];
  128. }
  129. }
  130. }
  131. lastRange = range;
  132. }
  133. return nil;
  134. }
  135. /**
  136. * Computes the start and end of a region of pixels, either horizontally or vertically, that could
  137. * be part of a Data Matrix barcode.
  138. *
  139. * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
  140. * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
  141. * @param maxWhiteRun largest run of white pixels that can still be considered part of the
  142. * barcode region
  143. * @param minDim minimum pixel location, horizontally or vertically, to consider
  144. * @param maxDim maximum pixel location, horizontally or vertically, to consider
  145. * @param horizontal if true, we're scanning left-right, instead of up-down
  146. * @return int[] with start and end of found range, or nil if no such range is found
  147. * (e.g. only white was found)
  148. */
  149. - (NSArray *)blackWhiteRange:(int)fixedDimension maxWhiteRun:(int)maxWhiteRun minDim:(int)minDim maxDim:(int)maxDim horizontal:(BOOL)horizontal {
  150. int center = (minDim + maxDim) / 2;
  151. int start = center;
  152. while (start >= minDim) {
  153. if (horizontal ? [self.image getX:start y:fixedDimension] : [self.image getX:fixedDimension y:start]) {
  154. start--;
  155. } else {
  156. int whiteRunStart = start;
  157. do {
  158. start--;
  159. } while (start >= minDim && !(horizontal ? [self.image getX:start y:fixedDimension] : [self.image getX:fixedDimension y:start]));
  160. int whiteRunSize = whiteRunStart - start;
  161. if (start < minDim || whiteRunSize > maxWhiteRun) {
  162. start = whiteRunStart;
  163. break;
  164. }
  165. }
  166. }
  167. start++;
  168. int end = center;
  169. while (end < maxDim) {
  170. if (horizontal ? [self.image getX:end y:fixedDimension] : [self.image getX:fixedDimension y:end]) {
  171. end++;
  172. } else {
  173. int whiteRunStart = end;
  174. do {
  175. end++;
  176. } while (end < maxDim && !(horizontal ? [self.image getX:end y:fixedDimension] : [self.image getX:fixedDimension y:end]));
  177. int whiteRunSize = end - whiteRunStart;
  178. if (end >= maxDim || whiteRunSize > maxWhiteRun) {
  179. end = whiteRunStart;
  180. break;
  181. }
  182. }
  183. }
  184. end--;
  185. return end > start ? @[@(start), @(end)] : nil;
  186. }
  187. @end