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

AlignmentPatternFinder.cs 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. * Copyright 2007 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. using System;
  17. using System.Collections.Generic;
  18. using ZXing.Common;
  19. namespace ZXing.QrCode.Internal
  20. {
  21. /// <summary> <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
  22. /// patterns but are smaller and appear at regular intervals throughout the image.</p>
  23. ///
  24. /// <p>At the moment this only looks for the bottom-right alignment pattern.</p>
  25. ///
  26. /// <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
  27. /// pasted and stripped down here for maximum performance but does unfortunately duplicate
  28. /// some code.</p>
  29. ///
  30. /// <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>
  31. ///
  32. /// </summary>
  33. /// <author> Sean Owen
  34. /// </author>
  35. /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
  36. /// </author>
  37. sealed class AlignmentPatternFinder
  38. {
  39. private readonly BitMatrix image;
  40. private readonly IList<AlignmentPattern> possibleCenters;
  41. private readonly int startX;
  42. private readonly int startY;
  43. private readonly int width;
  44. private readonly int height;
  45. private readonly float moduleSize;
  46. private readonly int[] crossCheckStateCount;
  47. private readonly ResultPointCallback resultPointCallback;
  48. /// <summary> <p>Creates a finder that will look in a portion of the whole image.</p>
  49. ///
  50. /// </summary>
  51. /// <param name="image">image to search
  52. /// </param>
  53. /// <param name="startX">left column from which to start searching
  54. /// </param>
  55. /// <param name="startY">top row from which to start searching
  56. /// </param>
  57. /// <param name="width">width of region to search
  58. /// </param>
  59. /// <param name="height">height of region to search
  60. /// </param>
  61. /// <param name="moduleSize">estimated module size so far
  62. /// </param>
  63. internal AlignmentPatternFinder(BitMatrix image, int startX, int startY, int width, int height, float moduleSize, ResultPointCallback resultPointCallback)
  64. {
  65. this.image = image;
  66. this.possibleCenters = new List<AlignmentPattern>(5);
  67. this.startX = startX;
  68. this.startY = startY;
  69. this.width = width;
  70. this.height = height;
  71. this.moduleSize = moduleSize;
  72. this.crossCheckStateCount = new int[3];
  73. this.resultPointCallback = resultPointCallback;
  74. }
  75. /// <summary> <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
  76. /// it's pretty performance-critical and so is written to be fast foremost.</p>
  77. ///
  78. /// </summary>
  79. /// <returns> {@link AlignmentPattern} if found
  80. /// </returns>
  81. internal AlignmentPattern find()
  82. {
  83. int startX = this.startX;
  84. int height = this.height;
  85. int maxJ = startX + width;
  86. int middleI = startY + (height >> 1);
  87. // We are looking for black/white/black modules in 1:1:1 ratio;
  88. // this tracks the number of black/white/black modules seen so far
  89. int[] stateCount = new int[3];
  90. for (int iGen = 0; iGen < height; iGen++)
  91. {
  92. // Search from middle outwards
  93. int i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));
  94. stateCount[0] = 0;
  95. stateCount[1] = 0;
  96. stateCount[2] = 0;
  97. int j = startX;
  98. // Burn off leading white pixels before anything else; if we start in the middle of
  99. // a white run, it doesn't make sense to count its length, since we don't know if the
  100. // white run continued to the left of the start point
  101. while (j < maxJ && !image[j, i])
  102. {
  103. j++;
  104. }
  105. int currentState = 0;
  106. while (j < maxJ)
  107. {
  108. if (image[j, i])
  109. {
  110. // Black pixel
  111. if (currentState == 1)
  112. {
  113. // Counting black pixels
  114. stateCount[currentState]++;
  115. }
  116. else
  117. {
  118. // Counting white pixels
  119. if (currentState == 2)
  120. {
  121. // A winner?
  122. if (foundPatternCross(stateCount))
  123. {
  124. // Yes
  125. AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);
  126. if (confirmed != null)
  127. {
  128. return confirmed;
  129. }
  130. }
  131. stateCount[0] = stateCount[2];
  132. stateCount[1] = 1;
  133. stateCount[2] = 0;
  134. currentState = 1;
  135. }
  136. else
  137. {
  138. stateCount[++currentState]++;
  139. }
  140. }
  141. }
  142. else
  143. {
  144. // White pixel
  145. if (currentState == 1)
  146. {
  147. // Counting black pixels
  148. currentState++;
  149. }
  150. stateCount[currentState]++;
  151. }
  152. j++;
  153. }
  154. if (foundPatternCross(stateCount))
  155. {
  156. AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ);
  157. if (confirmed != null)
  158. {
  159. return confirmed;
  160. }
  161. }
  162. }
  163. // Hmm, nothing we saw was observed and confirmed twice. If we had
  164. // any guess at all, return it.
  165. if (possibleCenters.Count != 0)
  166. {
  167. return possibleCenters[0];
  168. }
  169. return null;
  170. }
  171. /// <summary> Given a count of black/white/black pixels just seen and an end position,
  172. /// figures the location of the center of this black/white/black run.
  173. /// </summary>
  174. private static float? centerFromEnd(int[] stateCount, int end)
  175. {
  176. var result = (end - stateCount[2]) - stateCount[1] / 2.0f;
  177. if (Single.IsNaN(result))
  178. return null;
  179. return result;
  180. }
  181. /// <param name="stateCount">count of black/white/black pixels just read
  182. /// </param>
  183. /// <returns> true iff the proportions of the counts is close enough to the 1/1/1 ratios
  184. /// used by alignment patterns to be considered a match
  185. /// </returns>
  186. private bool foundPatternCross(int[] stateCount)
  187. {
  188. float maxVariance = moduleSize / 2.0f;
  189. for (int i = 0; i < 3; i++)
  190. {
  191. if (Math.Abs(moduleSize - stateCount[i]) >= maxVariance)
  192. {
  193. return false;
  194. }
  195. }
  196. return true;
  197. }
  198. /// <summary>
  199. /// <p>After a horizontal scan finds a potential alignment pattern, this method
  200. /// "cross-checks" by scanning down vertically through the center of the possible
  201. /// alignment pattern to see if the same proportion is detected.</p>
  202. /// </summary>
  203. /// <param name="startI">row where an alignment pattern was detected</param>
  204. /// <param name="centerJ">center of the section that appears to cross an alignment pattern</param>
  205. /// <param name="maxCount">maximum reasonable number of modules that should be
  206. /// observed in any reading state, based on the results of the horizontal scan</param>
  207. /// <param name="originalStateCountTotal">The original state count total.</param>
  208. /// <returns>
  209. /// vertical center of alignment pattern, or null if not found
  210. /// </returns>
  211. private float? crossCheckVertical(int startI, int centerJ, int maxCount, int originalStateCountTotal)
  212. {
  213. int maxI = image.Height;
  214. int[] stateCount = crossCheckStateCount;
  215. stateCount[0] = 0;
  216. stateCount[1] = 0;
  217. stateCount[2] = 0;
  218. // Start counting up from center
  219. int i = startI;
  220. while (i >= 0 && image[centerJ, i] && stateCount[1] <= maxCount)
  221. {
  222. stateCount[1]++;
  223. i--;
  224. }
  225. // If already too many modules in this state or ran off the edge:
  226. if (i < 0 || stateCount[1] > maxCount)
  227. {
  228. return null;
  229. }
  230. while (i >= 0 && !image[centerJ, i] && stateCount[0] <= maxCount)
  231. {
  232. stateCount[0]++;
  233. i--;
  234. }
  235. if (stateCount[0] > maxCount)
  236. {
  237. return null;
  238. }
  239. // Now also count down from center
  240. i = startI + 1;
  241. while (i < maxI && image[centerJ, i] && stateCount[1] <= maxCount)
  242. {
  243. stateCount[1]++;
  244. i++;
  245. }
  246. if (i == maxI || stateCount[1] > maxCount)
  247. {
  248. return null;
  249. }
  250. while (i < maxI && !image[centerJ, i] && stateCount[2] <= maxCount)
  251. {
  252. stateCount[2]++;
  253. i++;
  254. }
  255. if (stateCount[2] > maxCount)
  256. {
  257. return null;
  258. }
  259. int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
  260. if (5 * Math.Abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
  261. {
  262. return null;
  263. }
  264. return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : null;
  265. }
  266. /// <summary> <p>This is called when a horizontal scan finds a possible alignment pattern. It will
  267. /// cross check with a vertical scan, and if successful, will see if this pattern had been
  268. /// found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
  269. /// found the alignment pattern.</p>
  270. ///
  271. /// </summary>
  272. /// <param name="stateCount">reading state module counts from horizontal scan
  273. /// </param>
  274. /// <param name="i">row where alignment pattern may be found
  275. /// </param>
  276. /// <param name="j">end of possible alignment pattern in row
  277. /// </param>
  278. /// <returns> {@link AlignmentPattern} if we have found the same pattern twice, or null if not
  279. /// </returns>
  280. private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j)
  281. {
  282. int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
  283. float? centerJ = centerFromEnd(stateCount, j);
  284. if (centerJ == null)
  285. return null;
  286. float? centerI = crossCheckVertical(i, (int)centerJ, 2 * stateCount[1], stateCountTotal);
  287. if (centerI != null)
  288. {
  289. float estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
  290. foreach (var center in possibleCenters)
  291. {
  292. // Look for about the same center and module size:
  293. if (center.aboutEquals(estimatedModuleSize, centerI.Value, centerJ.Value))
  294. {
  295. return center.combineEstimate(centerI.Value, centerJ.Value, estimatedModuleSize);
  296. }
  297. }
  298. // Hadn't found this before; save it
  299. var point = new AlignmentPattern(centerJ.Value, centerI.Value, estimatedModuleSize);
  300. possibleCenters.Add(point);
  301. if (resultPointCallback != null)
  302. {
  303. resultPointCallback(point);
  304. }
  305. }
  306. return null;
  307. }
  308. }
  309. }