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.

ZXDataMatrixDetector.m 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 "ZXDataMatrixDetector.h"
  17. #import "ZXDetectorResult.h"
  18. #import "ZXErrors.h"
  19. #import "ZXGridSampler.h"
  20. #import "ZXMathUtils.h"
  21. #import "ZXResultPoint.h"
  22. #import "ZXWhiteRectangleDetector.h"
  23. /**
  24. * Simply encapsulates two points and a number of transitions between them.
  25. */
  26. @interface ZXResultPointsAndTransitions : NSObject
  27. @property (nonatomic, strong, readonly) ZXResultPoint *from;
  28. @property (nonatomic, strong, readonly) ZXResultPoint *to;
  29. @property (nonatomic, assign, readonly) int transitions;
  30. @end
  31. @implementation ZXResultPointsAndTransitions
  32. - (id)initWithFrom:(ZXResultPoint *)from to:(ZXResultPoint *)to transitions:(int)transitions {
  33. if (self = [super init]) {
  34. _from = from;
  35. _to = to;
  36. _transitions = transitions;
  37. }
  38. return self;
  39. }
  40. - (NSString *)description {
  41. return [NSString stringWithFormat:@"%@/%@/%d", self.from, self.to, self.transitions];
  42. }
  43. - (NSComparisonResult)compare:(ZXResultPointsAndTransitions *)otherObject {
  44. return [@(self.transitions) compare:@(otherObject.transitions)];
  45. }
  46. @end
  47. @interface ZXDataMatrixDetector ()
  48. @property (nonatomic, strong, readonly) ZXBitMatrix *image;
  49. @property (nonatomic, strong, readonly) ZXWhiteRectangleDetector *rectangleDetector;
  50. @end
  51. @implementation ZXDataMatrixDetector
  52. - (id)initWithImage:(ZXBitMatrix *)image error:(NSError **)error {
  53. if (self = [super init]) {
  54. _image = image;
  55. _rectangleDetector = [[ZXWhiteRectangleDetector alloc] initWithImage:_image error:error];
  56. if (!_rectangleDetector) {
  57. return nil;
  58. }
  59. }
  60. return self;
  61. }
  62. - (ZXDetectorResult *)detectWithError:(NSError **)error {
  63. NSArray *cornerPoints = [self.rectangleDetector detectWithError:error];
  64. if (!cornerPoints) {
  65. return nil;
  66. }
  67. ZXResultPoint *pointA = cornerPoints[0];
  68. ZXResultPoint *pointB = cornerPoints[1];
  69. ZXResultPoint *pointC = cornerPoints[2];
  70. ZXResultPoint *pointD = cornerPoints[3];
  71. NSMutableArray *transitions = [NSMutableArray arrayWithCapacity:4];
  72. [transitions addObject:[self transitionsBetween:pointA to:pointB]];
  73. [transitions addObject:[self transitionsBetween:pointA to:pointC]];
  74. [transitions addObject:[self transitionsBetween:pointB to:pointD]];
  75. [transitions addObject:[self transitionsBetween:pointC to:pointD]];
  76. [transitions sortUsingSelector:@selector(compare:)];
  77. ZXResultPointsAndTransitions *lSideOne = (ZXResultPointsAndTransitions *)transitions[0];
  78. ZXResultPointsAndTransitions *lSideTwo = (ZXResultPointsAndTransitions *)transitions[1];
  79. NSMutableDictionary *pointCount = [NSMutableDictionary dictionary];
  80. [self increment:pointCount key:[lSideOne from]];
  81. [self increment:pointCount key:[lSideOne to]];
  82. [self increment:pointCount key:[lSideTwo from]];
  83. [self increment:pointCount key:[lSideTwo to]];
  84. ZXResultPoint *maybeTopLeft = nil;
  85. ZXResultPoint *bottomLeft = nil;
  86. ZXResultPoint *maybeBottomRight = nil;
  87. for (ZXResultPoint *point in [pointCount allKeys]) {
  88. NSNumber *value = pointCount[point];
  89. if ([value intValue] == 2) {
  90. bottomLeft = point;
  91. } else {
  92. if (maybeTopLeft == nil) {
  93. maybeTopLeft = point;
  94. } else {
  95. maybeBottomRight = point;
  96. }
  97. }
  98. }
  99. if (maybeTopLeft == nil || bottomLeft == nil || maybeBottomRight == nil) {
  100. if (error) *error = ZXNotFoundErrorInstance();
  101. return nil;
  102. }
  103. NSMutableArray *corners = [NSMutableArray arrayWithObjects:maybeTopLeft, bottomLeft, maybeBottomRight, nil];
  104. [ZXResultPoint orderBestPatterns:corners];
  105. ZXResultPoint *bottomRight = corners[0];
  106. bottomLeft = corners[1];
  107. ZXResultPoint *topLeft = corners[2];
  108. ZXResultPoint *topRight;
  109. if (!pointCount[pointA]) {
  110. topRight = pointA;
  111. } else if (!pointCount[pointB]) {
  112. topRight = pointB;
  113. } else if (!pointCount[pointC]) {
  114. topRight = pointC;
  115. } else {
  116. topRight = pointD;
  117. }
  118. int dimensionTop = [[self transitionsBetween:topLeft to:topRight] transitions];
  119. int dimensionRight = [[self transitionsBetween:bottomRight to:topRight] transitions];
  120. if ((dimensionTop & 0x01) == 1) {
  121. dimensionTop++;
  122. }
  123. dimensionTop += 2;
  124. if ((dimensionRight & 0x01) == 1) {
  125. dimensionRight++;
  126. }
  127. dimensionRight += 2;
  128. ZXBitMatrix *bits;
  129. ZXResultPoint *correctedTopRight;
  130. if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
  131. correctedTopRight = [self correctTopRightRectangular:bottomLeft bottomRight:bottomRight topLeft:topLeft topRight:topRight dimensionTop:dimensionTop dimensionRight:dimensionRight];
  132. if (correctedTopRight == nil) {
  133. correctedTopRight = topRight;
  134. }
  135. dimensionTop = [[self transitionsBetween:topLeft to:correctedTopRight] transitions];
  136. dimensionRight = [[self transitionsBetween:bottomRight to:correctedTopRight] transitions];
  137. if ((dimensionTop & 0x01) == 1) {
  138. dimensionTop++;
  139. }
  140. if ((dimensionRight & 0x01) == 1) {
  141. dimensionRight++;
  142. }
  143. bits = [self sampleGrid:self.image topLeft:topLeft bottomLeft:bottomLeft bottomRight:bottomRight topRight:correctedTopRight dimensionX:dimensionTop dimensionY:dimensionRight error:error];
  144. if (!bits) {
  145. return nil;
  146. }
  147. } else {
  148. int dimension = MIN(dimensionRight, dimensionTop);
  149. correctedTopRight = [self correctTopRight:bottomLeft bottomRight:bottomRight topLeft:topLeft topRight:topRight dimension:dimension];
  150. if (correctedTopRight == nil) {
  151. correctedTopRight = topRight;
  152. }
  153. int dimensionCorrected = MAX([[self transitionsBetween:topLeft to:correctedTopRight] transitions], [[self transitionsBetween:bottomRight to:correctedTopRight] transitions]);
  154. dimensionCorrected++;
  155. if ((dimensionCorrected & 0x01) == 1) {
  156. dimensionCorrected++;
  157. }
  158. bits = [self sampleGrid:self.image topLeft:topLeft bottomLeft:bottomLeft bottomRight:bottomRight topRight:correctedTopRight dimensionX:dimensionCorrected dimensionY:dimensionCorrected error:error];
  159. if (!bits) {
  160. return nil;
  161. }
  162. }
  163. return [[ZXDetectorResult alloc] initWithBits:bits points:@[topLeft, bottomLeft, bottomRight, correctedTopRight]];
  164. }
  165. /**
  166. * Calculates the position of the white top right module using the output of the rectangle detector
  167. * for a rectangular matrix
  168. */
  169. - (ZXResultPoint *)correctTopRightRectangular:(ZXResultPoint *)bottomLeft bottomRight:(ZXResultPoint *)bottomRight
  170. topLeft:(ZXResultPoint *)topLeft topRight:(ZXResultPoint *)topRight
  171. dimensionTop:(int)dimensionTop dimensionRight:(int)dimensionRight {
  172. float corr = [self distance:bottomLeft b:bottomRight] / (float)dimensionTop;
  173. int norm = [self distance:topLeft b:topRight];
  174. float cos = ([topRight x] - [topLeft x]) / norm;
  175. float sin = ([topRight y] - [topLeft y]) / norm;
  176. ZXResultPoint *c1 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
  177. corr = [self distance:bottomLeft b:topLeft] / (float)dimensionRight;
  178. norm = [self distance:bottomRight b:topRight];
  179. cos = ([topRight x] - [bottomRight x]) / norm;
  180. sin = ([topRight y] - [bottomRight y]) / norm;
  181. ZXResultPoint *c2 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
  182. if (![self isValid:c1]) {
  183. if ([self isValid:c2]) {
  184. return c2;
  185. }
  186. return nil;
  187. } else if (![self isValid:c2]) {
  188. return c1;
  189. }
  190. int l1 = abs(dimensionTop - [[self transitionsBetween:topLeft to:c1] transitions]) + abs(dimensionRight - [[self transitionsBetween:bottomRight to:c1] transitions]);
  191. int l2 = abs(dimensionTop - [[self transitionsBetween:topLeft to:c2] transitions]) + abs(dimensionRight - [[self transitionsBetween:bottomRight to:c2] transitions]);
  192. if (l1 <= l2) {
  193. return c1;
  194. }
  195. return c2;
  196. }
  197. /**
  198. * Calculates the position of the white top right module using the output of the rectangle detector
  199. * for a square matrix
  200. */
  201. - (ZXResultPoint *)correctTopRight:(ZXResultPoint *)bottomLeft bottomRight:(ZXResultPoint *)bottomRight
  202. topLeft:(ZXResultPoint *)topLeft topRight:(ZXResultPoint *)topRight dimension:(int)dimension {
  203. float corr = [self distance:bottomLeft b:bottomRight] / (float)dimension;
  204. int norm = [self distance:topLeft b:topRight];
  205. float cos = ([topRight x] - [topLeft x]) / norm;
  206. float sin = ([topRight y] - [topLeft y]) / norm;
  207. ZXResultPoint *c1 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
  208. corr = [self distance:bottomLeft b:topLeft] / (float)dimension;
  209. norm = [self distance:bottomRight b:topRight];
  210. cos = ([topRight x] - [bottomRight x]) / norm;
  211. sin = ([topRight y] - [bottomRight y]) / norm;
  212. ZXResultPoint *c2 = [[ZXResultPoint alloc] initWithX:[topRight x] + corr * cos y:[topRight y] + corr * sin];
  213. if (![self isValid:c1]) {
  214. if ([self isValid:c2]) {
  215. return c2;
  216. }
  217. return nil;
  218. } else if (![self isValid:c2]) {
  219. return c1;
  220. }
  221. int l1 = abs([[self transitionsBetween:topLeft to:c1] transitions] - [[self transitionsBetween:bottomRight to:c1] transitions]);
  222. int l2 = abs([[self transitionsBetween:topLeft to:c2] transitions] - [[self transitionsBetween:bottomRight to:c2] transitions]);
  223. return l1 <= l2 ? c1 : c2;
  224. }
  225. - (BOOL) isValid:(ZXResultPoint *)p {
  226. return [p x] >= 0 && [p x] < self.image.width && [p y] > 0 && [p y] < self.image.height;
  227. }
  228. - (int)distance:(ZXResultPoint *)a b:(ZXResultPoint *)b {
  229. return [ZXMathUtils round:[ZXResultPoint distance:a pattern2:b]];
  230. }
  231. /**
  232. * Increments the Integer associated with a key by one.
  233. */
  234. - (void)increment:(NSMutableDictionary *)table key:(ZXResultPoint *)key {
  235. NSNumber *value = table[key];
  236. table[key] = value == nil ? @1 : @([value intValue] + 1);
  237. }
  238. - (ZXBitMatrix *)sampleGrid:(ZXBitMatrix *)image
  239. topLeft:(ZXResultPoint *)topLeft
  240. bottomLeft:(ZXResultPoint *)bottomLeft
  241. bottomRight:(ZXResultPoint *)bottomRight
  242. topRight:(ZXResultPoint *)topRight
  243. dimensionX:(int)dimensionX
  244. dimensionY:(int)dimensionY
  245. error:(NSError **)error {
  246. ZXGridSampler *sampler = [ZXGridSampler instance];
  247. return [sampler sampleGrid:image
  248. dimensionX:dimensionX dimensionY:dimensionY
  249. p1ToX:0.5f p1ToY:0.5f
  250. p2ToX:dimensionX - 0.5f p2ToY:0.5f
  251. p3ToX:dimensionX - 0.5f p3ToY:dimensionY - 0.5f
  252. p4ToX:0.5f p4ToY:dimensionY - 0.5f
  253. p1FromX:[topLeft x] p1FromY:[topLeft y]
  254. p2FromX:[topRight x] p2FromY:[topRight y]
  255. p3FromX:[bottomRight x] p3FromY:[bottomRight y]
  256. p4FromX:[bottomLeft x] p4FromY:[bottomLeft y]
  257. error:error];
  258. }
  259. /**
  260. * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
  261. */
  262. - (ZXResultPointsAndTransitions *)transitionsBetween:(ZXResultPoint *)from to:(ZXResultPoint *)to {
  263. int fromX = (int)[from x];
  264. int fromY = (int)[from y];
  265. int toX = (int)[to x];
  266. int toY = (int)[to y];
  267. BOOL steep = abs(toY - fromY) > abs(toX - fromX);
  268. if (steep) {
  269. int temp = fromX;
  270. fromX = fromY;
  271. fromY = temp;
  272. temp = toX;
  273. toX = toY;
  274. toY = temp;
  275. }
  276. int dx = abs(toX - fromX);
  277. int dy = abs(toY - fromY);
  278. int error = -dx / 2;
  279. int ystep = fromY < toY ? 1 : -1;
  280. int xstep = fromX < toX ? 1 : -1;
  281. int transitions = 0;
  282. BOOL inBlack = [self.image getX:steep ? fromY : fromX y:steep ? fromX : fromY];
  283. for (int x = fromX, y = fromY; x != toX; x += xstep) {
  284. BOOL isBlack = [self.image getX:steep ? y : x y:steep ? x : y];
  285. if (isBlack != inBlack) {
  286. transitions++;
  287. inBlack = isBlack;
  288. }
  289. error += dy;
  290. if (error > 0) {
  291. if (y == toY) {
  292. break;
  293. }
  294. y += ystep;
  295. error -= dx;
  296. }
  297. }
  298. return [[ZXResultPointsAndTransitions alloc] initWithFrom:from to:to transitions:transitions];
  299. }
  300. @end