PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/silverlight/multi/qrcode/detector/MultiFinderPatternFinder.cs

https://bitbucket.org/jrasanen/silverlightzxing
C# | 367 lines | 223 code | 37 blank | 107 comment | 45 complexity | 76c9c76d9c07c83d67436b9a975ab34c MD5 | raw file
  1. /*
  2. * Copyright 2009 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 DecodeHintType = com.google.zxing.DecodeHintType;
  18. using ReaderException = com.google.zxing.ReaderException;
  19. using ResultPoint = com.google.zxing.ResultPoint;
  20. using ResultPointCallback = com.google.zxing.ResultPointCallback;
  21. using Collections = com.google.zxing.common.Collections;
  22. using Comparator = com.google.zxing.common.Comparator;
  23. using BitMatrix = com.google.zxing.common.BitMatrix;
  24. using FinderPattern = com.google.zxing.qrcode.detector.FinderPattern;
  25. using FinderPatternFinder = com.google.zxing.qrcode.detector.FinderPatternFinder;
  26. using FinderPatternInfo = com.google.zxing.qrcode.detector.FinderPatternInfo;
  27. namespace com.google.zxing.multi.qrcode.detector
  28. {
  29. /// <summary> <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
  30. /// markers at three corners of a QR Code.</p>
  31. ///
  32. /// <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
  33. ///
  34. /// <p>In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
  35. /// QR code locations in the image.</p>
  36. ///
  37. /// <p>Use the TRY_HARDER hint to ask for a more thorough detection.</p>
  38. ///
  39. /// </summary>
  40. /// <author> Sean Owen
  41. /// </author>
  42. /// <author> Hannes Erven
  43. /// </author>
  44. /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
  45. /// </author>
  46. sealed class MultiFinderPatternFinder:FinderPatternFinder
  47. {
  48. //UPGRADE_NOTE: Final was removed from the declaration of 'EMPTY_RESULT_ARRAY '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
  49. private static readonly FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0];
  50. // TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
  51. // since it limits the number of regions to decode
  52. // max. legal count of modules per QR code edge (177)
  53. private const float MAX_MODULE_COUNT_PER_EDGE = 180;
  54. // min. legal count per modules per QR code edge (11)
  55. private const float MIN_MODULE_COUNT_PER_EDGE = 9;
  56. /// <summary> More or less arbitrary cutoff point for determining if two finder patterns might belong
  57. /// to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
  58. /// estimated modules sizes.
  59. /// </summary>
  60. private const float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;
  61. /// <summary> More or less arbitrary cutoff point for determining if two finder patterns might belong
  62. /// to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
  63. /// estimated modules sizes.
  64. /// </summary>
  65. private const float DIFF_MODSIZE_CUTOFF = 0.5f;
  66. /// <summary> A comparator that orders FinderPatterns by their estimated module size.</summary>
  67. private class ModuleSizeComparator : Comparator
  68. {
  69. public int compare(System.Object center1, System.Object center2)
  70. {
  71. float value_Renamed = ((FinderPattern) center2).EstimatedModuleSize - ((FinderPattern) center1).EstimatedModuleSize;
  72. return value_Renamed < 0.0?- 1:(value_Renamed > 0.0?1:0);
  73. }
  74. }
  75. /// <summary> <p>Creates a finder that will search the image for three finder patterns.</p>
  76. ///
  77. /// </summary>
  78. /// <param name="image">image to search
  79. /// </param>
  80. internal MultiFinderPatternFinder(BitMatrix image):base(image)
  81. {
  82. }
  83. internal MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback):base(image, resultPointCallback)
  84. {
  85. }
  86. /// <returns> the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
  87. /// those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
  88. /// size differs from the average among those patterns the least
  89. /// </returns>
  90. /// <throws> ReaderException if 3 such finder patterns do not exist </throws>
  91. private FinderPattern[][] selectBestPatterns()
  92. {
  93. System.Collections.ArrayList possibleCenters = PossibleCenters;
  94. int size = possibleCenters.Count;
  95. if (size < 3)
  96. {
  97. // Couldn't find enough finder patterns
  98. throw ReaderException.Instance;
  99. }
  100. /*
  101. * Begin HE modifications to safely detect multiple codes of equal size
  102. */
  103. if (size == 3)
  104. {
  105. return new FinderPattern[][]{new FinderPattern[]{(FinderPattern) possibleCenters[0], (FinderPattern) possibleCenters[1], (FinderPattern) possibleCenters[2]}};
  106. }
  107. // Sort by estimated module size to speed up the upcoming checks
  108. Collections.insertionSort(possibleCenters, new ModuleSizeComparator());
  109. /*
  110. * Now lets start: build a list of tuples of three finder locations that
  111. * - feature similar module sizes
  112. * - are placed in a distance so the estimated module count is within the QR specification
  113. * - have similar distance between upper left/right and left top/bottom finder patterns
  114. * - form a triangle with 90° angle (checked by comparing top right/bottom left distance
  115. * with pythagoras)
  116. *
  117. * Note: we allow each point to be used for more than one code region: this might seem
  118. * counterintuitive at first, but the performance penalty is not that big. At this point,
  119. * we cannot make a good quality decision whether the three finders actually represent
  120. * a QR code, or are just by chance layouted so it looks like there might be a QR code there.
  121. * So, if the layout seems right, lets have the decoder try to decode.
  122. */
  123. System.Collections.ArrayList results = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10)); // holder for the results
  124. for (int i1 = 0; i1 < (size - 2); i1++)
  125. {
  126. FinderPattern p1 = (FinderPattern) possibleCenters[i1];
  127. if (p1 == null)
  128. {
  129. continue;
  130. }
  131. for (int i2 = i1 + 1; i2 < (size - 1); i2++)
  132. {
  133. FinderPattern p2 = (FinderPattern) possibleCenters[i2];
  134. if (p2 == null)
  135. {
  136. continue;
  137. }
  138. // Compare the expected module sizes; if they are really off, skip
  139. float vModSize12 = (p1.EstimatedModuleSize - p2.EstimatedModuleSize) / (System.Math.Min(p1.EstimatedModuleSize, p2.EstimatedModuleSize));
  140. float vModSize12A = System.Math.Abs(p1.EstimatedModuleSize - p2.EstimatedModuleSize);
  141. if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT)
  142. {
  143. // break, since elements are ordered by the module size deviation there cannot be
  144. // any more interesting elements for the given p1.
  145. break;
  146. }
  147. for (int i3 = i2 + 1; i3 < size; i3++)
  148. {
  149. FinderPattern p3 = (FinderPattern) possibleCenters[i3];
  150. if (p3 == null)
  151. {
  152. continue;
  153. }
  154. // Compare the expected module sizes; if they are really off, skip
  155. float vModSize23 = (p2.EstimatedModuleSize - p3.EstimatedModuleSize) / (System.Math.Min(p2.EstimatedModuleSize, p3.EstimatedModuleSize));
  156. float vModSize23A = System.Math.Abs(p2.EstimatedModuleSize - p3.EstimatedModuleSize);
  157. if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT)
  158. {
  159. // break, since elements are ordered by the module size deviation there cannot be
  160. // any more interesting elements for the given p1.
  161. break;
  162. }
  163. FinderPattern[] test = new FinderPattern[]{p1, p2, p3};
  164. ResultPoint.orderBestPatterns(test);
  165. // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
  166. FinderPatternInfo info = new FinderPatternInfo(test);
  167. float dA = ResultPoint.distance(info.TopLeft, info.BottomLeft);
  168. float dC = ResultPoint.distance(info.TopRight, info.BottomLeft);
  169. float dB = ResultPoint.distance(info.TopLeft, info.TopRight);
  170. // Check the sizes
  171. float estimatedModuleCount = ((dA + dB) / p1.EstimatedModuleSize) / 2;
  172. if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE)
  173. {
  174. continue;
  175. }
  176. // Calculate the difference of the edge lengths in percent
  177. float vABBC = System.Math.Abs(((dA - dB) / System.Math.Min(dA, dB)));
  178. if (vABBC >= 0.1f)
  179. {
  180. continue;
  181. }
  182. // Calculate the diagonal length by assuming a 90° angle at topleft
  183. //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
  184. float dCpy = (float) System.Math.Sqrt(dA * dA + dB * dB);
  185. // Compare to the real distance in %
  186. float vPyC = System.Math.Abs(((dC - dCpy) / System.Math.Min(dC, dCpy)));
  187. if (vPyC >= 0.1f)
  188. {
  189. continue;
  190. }
  191. // All tests passed!
  192. results.Add(test);
  193. } // end iterate p3
  194. } // end iterate p2
  195. } // end iterate p1
  196. if (!(results.Count == 0))
  197. {
  198. FinderPattern[][] resultArray = new FinderPattern[results.Count][];
  199. for (int i = 0; i < results.Count; i++)
  200. {
  201. resultArray[i] = (FinderPattern[]) results[i];
  202. }
  203. return resultArray;
  204. }
  205. // Nothing found!
  206. throw ReaderException.Instance;
  207. }
  208. public FinderPatternInfo[] findMulti(System.Collections.Hashtable hints)
  209. {
  210. bool tryHarder = hints != null && hints.ContainsKey(DecodeHintType.TRY_HARDER);
  211. BitMatrix image = Image;
  212. int maxI = image.Height;
  213. int maxJ = image.Width;
  214. // We are looking for black/white/black/white/black modules in
  215. // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
  216. // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
  217. // image, and then account for the center being 3 modules in size. This gives the smallest
  218. // number of pixels the center could be, so skip this often. When trying harder, look for all
  219. // QR versions regardless of how dense they are.
  220. //UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
  221. int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3);
  222. if (iSkip < MIN_SKIP || tryHarder)
  223. {
  224. iSkip = MIN_SKIP;
  225. }
  226. int[] stateCount = new int[5];
  227. for (int i = iSkip - 1; i < maxI; i += iSkip)
  228. {
  229. // Get a row of black/white values
  230. stateCount[0] = 0;
  231. stateCount[1] = 0;
  232. stateCount[2] = 0;
  233. stateCount[3] = 0;
  234. stateCount[4] = 0;
  235. int currentState = 0;
  236. for (int j = 0; j < maxJ; j++)
  237. {
  238. if (image.get_Renamed(j, i))
  239. {
  240. // Black pixel
  241. if ((currentState & 1) == 1)
  242. {
  243. // Counting white pixels
  244. currentState++;
  245. }
  246. stateCount[currentState]++;
  247. }
  248. else
  249. {
  250. // White pixel
  251. if ((currentState & 1) == 0)
  252. {
  253. // Counting black pixels
  254. if (currentState == 4)
  255. {
  256. // A winner?
  257. if (foundPatternCross(stateCount))
  258. {
  259. // Yes
  260. bool confirmed = handlePossibleCenter(stateCount, i, j);
  261. if (!confirmed)
  262. {
  263. do
  264. {
  265. // Advance to next black pixel
  266. j++;
  267. }
  268. while (j < maxJ && !image.get_Renamed(j, i));
  269. j--; // back up to that last white pixel
  270. }
  271. // Clear state to start looking again
  272. currentState = 0;
  273. stateCount[0] = 0;
  274. stateCount[1] = 0;
  275. stateCount[2] = 0;
  276. stateCount[3] = 0;
  277. stateCount[4] = 0;
  278. }
  279. else
  280. {
  281. // No, shift counts back by two
  282. stateCount[0] = stateCount[2];
  283. stateCount[1] = stateCount[3];
  284. stateCount[2] = stateCount[4];
  285. stateCount[3] = 1;
  286. stateCount[4] = 0;
  287. currentState = 3;
  288. }
  289. }
  290. else
  291. {
  292. stateCount[++currentState]++;
  293. }
  294. }
  295. else
  296. {
  297. // Counting white pixels
  298. stateCount[currentState]++;
  299. }
  300. }
  301. } // for j=...
  302. if (foundPatternCross(stateCount))
  303. {
  304. handlePossibleCenter(stateCount, i, maxJ);
  305. } // end if foundPatternCross
  306. } // for i=iSkip-1 ...
  307. FinderPattern[][] patternInfo = selectBestPatterns();
  308. System.Collections.ArrayList result = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  309. for (int i = 0; i < patternInfo.Length; i++)
  310. {
  311. FinderPattern[] pattern = patternInfo[i];
  312. ResultPoint.orderBestPatterns(pattern);
  313. result.Add(new FinderPatternInfo(pattern));
  314. }
  315. if ((result.Count == 0))
  316. {
  317. return EMPTY_RESULT_ARRAY;
  318. }
  319. else
  320. {
  321. FinderPatternInfo[] resultArray = new FinderPatternInfo[result.Count];
  322. for (int i = 0; i < result.Count; i++)
  323. {
  324. resultArray[i] = (FinderPatternInfo) result[i];
  325. }
  326. return resultArray;
  327. }
  328. }
  329. }
  330. }