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.

ZXWhiteRectangleDetector.m 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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 "ZXErrors.h"
  17. #import "ZXMathUtils.h"
  18. #import "ZXWhiteRectangleDetector.h"
  19. @interface ZXWhiteRectangleDetector ()
  20. @property (nonatomic, strong, readonly) ZXBitMatrix *image;
  21. @property (nonatomic, assign, readonly) int height;
  22. @property (nonatomic, assign, readonly) int width;
  23. @property (nonatomic, assign, readonly) int leftInit;
  24. @property (nonatomic, assign, readonly) int rightInit;
  25. @property (nonatomic, assign, readonly) int downInit;
  26. @property (nonatomic, assign, readonly) int upInit;
  27. @end
  28. const int ZX_INIT_SIZE = 10;
  29. const int ZX_CORR = 1;
  30. @implementation ZXWhiteRectangleDetector
  31. - (id)initWithImage:(ZXBitMatrix *)image error:(NSError **)error {
  32. return [self initWithImage:image initSize:ZX_INIT_SIZE x:image.width / 2 y:image.height / 2 error:error];
  33. }
  34. - (id)initWithImage:(ZXBitMatrix *)image initSize:(int)initSize x:(int)x y:(int)y error:(NSError **)error {
  35. if (self = [super init]) {
  36. _image = image;
  37. _height = image.height;
  38. _width = image.width;
  39. int halfsize = initSize / 2;
  40. _leftInit = x - halfsize;
  41. _rightInit = x + halfsize;
  42. _upInit = y - halfsize;
  43. _downInit = y + halfsize;
  44. if (_upInit < 0 || _leftInit < 0 || _downInit >= _height || _rightInit >= _width) {
  45. if (error) *error = ZXNotFoundErrorInstance();
  46. return nil;
  47. }
  48. }
  49. return self;
  50. }
  51. - (NSArray *)detectWithError:(NSError **)error {
  52. int left = self.leftInit;
  53. int right = self.rightInit;
  54. int up = self.upInit;
  55. int down = self.downInit;
  56. BOOL sizeExceeded = NO;
  57. BOOL aBlackPointFoundOnBorder = YES;
  58. BOOL atLeastOneBlackPointFoundOnBorder = NO;
  59. BOOL atLeastOneBlackPointFoundOnRight = NO;
  60. BOOL atLeastOneBlackPointFoundOnBottom = NO;
  61. BOOL atLeastOneBlackPointFoundOnLeft = NO;
  62. BOOL atLeastOneBlackPointFoundOnTop = NO;
  63. while (aBlackPointFoundOnBorder) {
  64. aBlackPointFoundOnBorder = NO;
  65. // .....
  66. // . |
  67. // .....
  68. BOOL rightBorderNotWhite = YES;
  69. while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < self.width) {
  70. rightBorderNotWhite = [self containsBlackPoint:up b:down fixed:right horizontal:NO];
  71. if (rightBorderNotWhite) {
  72. right++;
  73. aBlackPointFoundOnBorder = YES;
  74. atLeastOneBlackPointFoundOnRight = YES;
  75. } else if (!atLeastOneBlackPointFoundOnRight) {
  76. right++;
  77. }
  78. }
  79. if (right >= self.width) {
  80. sizeExceeded = YES;
  81. break;
  82. }
  83. // .....
  84. // . .
  85. // .___.
  86. BOOL bottomBorderNotWhite = YES;
  87. while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < self.height) {
  88. bottomBorderNotWhite = [self containsBlackPoint:left b:right fixed:down horizontal:YES];
  89. if (bottomBorderNotWhite) {
  90. down++;
  91. aBlackPointFoundOnBorder = YES;
  92. atLeastOneBlackPointFoundOnBottom = YES;
  93. } else if (!atLeastOneBlackPointFoundOnBottom) {
  94. down++;
  95. }
  96. }
  97. if (down >= self.height) {
  98. sizeExceeded = YES;
  99. break;
  100. }
  101. // .....
  102. // | .
  103. // .....
  104. BOOL leftBorderNotWhite = YES;
  105. while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) {
  106. leftBorderNotWhite = [self containsBlackPoint:up b:down fixed:left horizontal:NO];
  107. if (leftBorderNotWhite) {
  108. left--;
  109. aBlackPointFoundOnBorder = YES;
  110. atLeastOneBlackPointFoundOnLeft = YES;
  111. } else if (!atLeastOneBlackPointFoundOnLeft) {
  112. left--;
  113. }
  114. }
  115. if (left < 0) {
  116. sizeExceeded = YES;
  117. break;
  118. }
  119. // .___.
  120. // . .
  121. // .....
  122. BOOL topBorderNotWhite = YES;
  123. while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) {
  124. topBorderNotWhite = [self containsBlackPoint:left b:right fixed:up horizontal:YES];
  125. if (topBorderNotWhite) {
  126. up--;
  127. aBlackPointFoundOnBorder = YES;
  128. atLeastOneBlackPointFoundOnTop = YES;
  129. } else if (!atLeastOneBlackPointFoundOnTop) {
  130. up--;
  131. }
  132. }
  133. if (up < 0) {
  134. sizeExceeded = YES;
  135. break;
  136. }
  137. if (aBlackPointFoundOnBorder) {
  138. atLeastOneBlackPointFoundOnBorder = YES;
  139. }
  140. }
  141. if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
  142. int maxSize = right - left;
  143. ZXResultPoint *z = nil;
  144. for (int i = 1; i < maxSize; i++) {
  145. z = [self blackPointOnSegment:left aY:down - i bX:left + i bY:down];
  146. if (z != nil) {
  147. break;
  148. }
  149. }
  150. if (z == nil) {
  151. if (error) *error = ZXNotFoundErrorInstance();
  152. return nil;
  153. }
  154. ZXResultPoint *t = nil;
  155. for (int i = 1; i < maxSize; i++) {
  156. t = [self blackPointOnSegment:left aY:up + i bX:left + i bY:up];
  157. if (t != nil) {
  158. break;
  159. }
  160. }
  161. if (t == nil) {
  162. if (error) *error = ZXNotFoundErrorInstance();
  163. return nil;
  164. }
  165. ZXResultPoint *x = nil;
  166. for (int i = 1; i < maxSize; i++) {
  167. x = [self blackPointOnSegment:right aY:up + i bX:right - i bY:up];
  168. if (x != nil) {
  169. break;
  170. }
  171. }
  172. if (x == nil) {
  173. if (error) *error = ZXNotFoundErrorInstance();
  174. return nil;
  175. }
  176. ZXResultPoint *y = nil;
  177. for (int i = 1; i < maxSize; i++) {
  178. y = [self blackPointOnSegment:right aY:down - i bX:right - i bY:down];
  179. if (y != nil) {
  180. break;
  181. }
  182. }
  183. if (y == nil) {
  184. if (error) *error = ZXNotFoundErrorInstance();
  185. return nil;
  186. }
  187. return [self centerEdges:y z:z x:x t:t];
  188. } else {
  189. if (error) *error = ZXNotFoundErrorInstance();
  190. return nil;
  191. }
  192. }
  193. - (ZXResultPoint *)blackPointOnSegment:(float)aX aY:(float)aY bX:(float)bX bY:(float)bY {
  194. int dist = [ZXMathUtils round:[ZXMathUtils distance:aX aY:aY bX:bX bY:bY]];
  195. float xStep = (bX - aX) / dist;
  196. float yStep = (bY - aY) / dist;
  197. for (int i = 0; i < dist; i++) {
  198. int x = [ZXMathUtils round:aX + i * xStep];
  199. int y = [ZXMathUtils round:aY + i * yStep];
  200. if ([self.image getX:x y:y]) {
  201. return [[ZXResultPoint alloc] initWithX:x y:y];
  202. }
  203. }
  204. return nil;
  205. }
  206. /**
  207. * recenters the points of a constant distance towards the center
  208. *
  209. * @param y bottom most point
  210. * @param z left most point
  211. * @param x right most point
  212. * @param t top most point
  213. * @return ZXResultPoint array describing the corners of the rectangular
  214. * region. The first and last points are opposed on the diagonal, as
  215. * are the second and third. The first point will be the topmost
  216. * point and the last, the bottommost. The second point will be
  217. * leftmost and the third, the rightmost
  218. */
  219. - (NSArray *)centerEdges:(ZXResultPoint *)y z:(ZXResultPoint *)z x:(ZXResultPoint *)x t:(ZXResultPoint *)t {
  220. //
  221. // t t
  222. // z x
  223. // x OR z
  224. // y y
  225. //
  226. float yi = y.x;
  227. float yj = y.y;
  228. float zi = z.x;
  229. float zj = z.y;
  230. float xi = x.x;
  231. float xj = x.y;
  232. float ti = t.x;
  233. float tj = t.y;
  234. if (yi < self.width / 2.0f) {
  235. return @[[[ZXResultPoint alloc] initWithX:ti - ZX_CORR y:tj + ZX_CORR],
  236. [[ZXResultPoint alloc] initWithX:zi + ZX_CORR y:zj + ZX_CORR],
  237. [[ZXResultPoint alloc] initWithX:xi - ZX_CORR y:xj - ZX_CORR],
  238. [[ZXResultPoint alloc] initWithX:yi + ZX_CORR y:yj - ZX_CORR]];
  239. } else {
  240. return @[[[ZXResultPoint alloc] initWithX:ti + ZX_CORR y:tj + ZX_CORR],
  241. [[ZXResultPoint alloc] initWithX:zi + ZX_CORR y:zj - ZX_CORR],
  242. [[ZXResultPoint alloc] initWithX:xi - ZX_CORR y:xj + ZX_CORR],
  243. [[ZXResultPoint alloc] initWithX:yi - ZX_CORR y:yj - ZX_CORR]];
  244. }
  245. }
  246. /**
  247. * Determines whether a segment contains a black point
  248. *
  249. * @param a min value of the scanned coordinate
  250. * @param b max value of the scanned coordinate
  251. * @param fixed value of fixed coordinate
  252. * @param horizontal set to true if scan must be horizontal, false if vertical
  253. * @return true if a black point has been found, else false.
  254. */
  255. - (BOOL)containsBlackPoint:(int)a b:(int)b fixed:(int)fixed horizontal:(BOOL)horizontal {
  256. if (horizontal) {
  257. for (int x = a; x <= b; x++) {
  258. if ([self.image getX:x y:fixed]) {
  259. return YES;
  260. }
  261. }
  262. } else {
  263. for (int y = a; y <= b; y++) {
  264. if ([self.image getX:fixed y:y]) {
  265. return YES;
  266. }
  267. }
  268. }
  269. return NO;
  270. }
  271. @end