Detector.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
  2. /*
  3. * Detector.cpp
  4. * zxing
  5. *
  6. * Created by Christian Brunschen on 14/05/2008.
  7. * Copyright 2008 ZXing authors All rights reserved.
  8. *
  9. * Licensed under the Apache License, Version 2.0 (the "License");
  10. * you may not use this file except in compliance with the License.
  11. * You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing, software
  16. * distributed under the License is distributed on an "AS IS" BASIS,
  17. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. * See the License for the specific language governing permissions and
  19. * limitations under the License.
  20. */
  21. #include <zxing/qrcode/detector/Detector.h>
  22. #include <zxing/qrcode/detector/FinderPatternFinder.h>
  23. #include <zxing/qrcode/detector/FinderPattern.h>
  24. #include <zxing/qrcode/detector/AlignmentPattern.h>
  25. #include <zxing/qrcode/detector/AlignmentPatternFinder.h>
  26. #include <zxing/qrcode/Version.h>
  27. #include <zxing/common/GridSampler.h>
  28. #include <zxing/DecodeHints.h>
  29. #include <zxing/common/detector/MathUtils.h>
  30. #include <sstream>
  31. #include <cstdlib>
  32. #include <algorithm>
  33. using std::ostringstream;
  34. using std::abs;
  35. using std::min;
  36. using std::max;
  37. using zxing::qrcode::Detector;
  38. using zxing::Ref;
  39. using zxing::BitMatrix;
  40. using zxing::ResultPointCallback;
  41. using zxing::DetectorResult;
  42. using zxing::PerspectiveTransform;
  43. using zxing::qrcode::AlignmentPattern;
  44. using zxing::common::detector::MathUtils;
  45. // VC++
  46. using zxing::DecodeHints;
  47. using zxing::qrcode::FinderPatternFinder;
  48. using zxing::qrcode::FinderPatternInfo;
  49. using zxing::ResultPoint;
  50. Detector::Detector(Ref<BitMatrix> image) :
  51. image_(image) {
  52. }
  53. Ref<BitMatrix> Detector::getImage() const {
  54. return image_;
  55. }
  56. Ref<ResultPointCallback> Detector::getResultPointCallback() const {
  57. return callback_;
  58. }
  59. Ref<DetectorResult> Detector::detect(DecodeHints const& hints) {
  60. callback_ = hints.getResultPointCallback();
  61. FinderPatternFinder finder(image_, hints.getResultPointCallback());
  62. Ref<FinderPatternInfo> info(finder.find(hints));
  63. return processFinderPatternInfo(info);
  64. }
  65. Ref<DetectorResult> Detector::processFinderPatternInfo(Ref<FinderPatternInfo> info){
  66. Ref<FinderPattern> topLeft(info->getTopLeft());
  67. Ref<FinderPattern> topRight(info->getTopRight());
  68. Ref<FinderPattern> bottomLeft(info->getBottomLeft());
  69. float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
  70. if (moduleSize < 1.0f) {
  71. throw zxing::ReaderException("bad module size");
  72. }
  73. int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
  74. Version *provisionalVersion = Version::getProvisionalVersionForDimension(dimension);
  75. int modulesBetweenFPCenters = provisionalVersion->getDimensionForVersion() - 7;
  76. Ref<AlignmentPattern> alignmentPattern;
  77. // Anything above version 1 has an alignment pattern
  78. if (provisionalVersion->getAlignmentPatternCenters().size() > 0) {
  79. // Guess where a "bottom right" finder pattern would have been
  80. float bottomRightX = topRight->getX() - topLeft->getX() + bottomLeft->getX();
  81. float bottomRightY = topRight->getY() - topLeft->getY() + bottomLeft->getY();
  82. // Estimate that alignment pattern is closer by 3 modules
  83. // from "bottom right" to known top left location
  84. float correctionToTopLeft = 1.0f - 3.0f / (float)modulesBetweenFPCenters;
  85. int estAlignmentX = (int)(topLeft->getX() + correctionToTopLeft * (bottomRightX - topLeft->getX()));
  86. int estAlignmentY = (int)(topLeft->getY() + correctionToTopLeft * (bottomRightY - topLeft->getY()));
  87. // Kind of arbitrary -- expand search radius before giving up
  88. for (int i = 4; i <= 16; i <<= 1) {
  89. try {
  90. alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i);
  91. break;
  92. } catch (zxing::ReaderException const& re) {
  93. (void)re;
  94. // try next round
  95. }
  96. }
  97. if (alignmentPattern == 0) {
  98. // Try anyway
  99. }
  100. }
  101. Ref<PerspectiveTransform> transform = createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
  102. Ref<BitMatrix> bits(sampleGrid(image_, dimension, transform));
  103. ArrayRef< Ref<ResultPoint> > points(new Array< Ref<ResultPoint> >(alignmentPattern == 0 ? 3 : 4));
  104. points[0].reset(bottomLeft);
  105. points[1].reset(topLeft);
  106. points[2].reset(topRight);
  107. if (alignmentPattern != 0) {
  108. points[3].reset(alignmentPattern);
  109. }
  110. Ref<DetectorResult> result(new DetectorResult(bits, points));
  111. return result;
  112. }
  113. Ref<PerspectiveTransform> Detector::createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
  114. ResultPoint > bottomLeft, Ref<ResultPoint> alignmentPattern, int dimension) {
  115. float dimMinusThree = (float)dimension - 3.5f;
  116. float bottomRightX;
  117. float bottomRightY;
  118. float sourceBottomRightX;
  119. float sourceBottomRightY;
  120. if (alignmentPattern != 0) {
  121. bottomRightX = alignmentPattern->getX();
  122. bottomRightY = alignmentPattern->getY();
  123. sourceBottomRightX = dimMinusThree - 3.0f;
  124. sourceBottomRightY = sourceBottomRightX;
  125. } else {
  126. // Don't have an alignment pattern, just make up the bottom-right point
  127. bottomRightX = (topRight->getX() - topLeft->getX()) + bottomLeft->getX();
  128. bottomRightY = (topRight->getY() - topLeft->getY()) + bottomLeft->getY();
  129. sourceBottomRightX = dimMinusThree;
  130. sourceBottomRightY = dimMinusThree;
  131. }
  132. Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(3.5f, 3.5f, dimMinusThree, 3.5f, sourceBottomRightX,
  133. sourceBottomRightY, 3.5f, dimMinusThree, topLeft->getX(), topLeft->getY(), topRight->getX(),
  134. topRight->getY(), bottomRightX, bottomRightY, bottomLeft->getX(), bottomLeft->getY()));
  135. return transform;
  136. }
  137. Ref<BitMatrix> Detector::sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform) {
  138. GridSampler &sampler = GridSampler::getInstance();
  139. return sampler.sampleGrid(image, dimension, transform);
  140. }
  141. int Detector::computeDimension(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref<ResultPoint> bottomLeft,
  142. float moduleSize) {
  143. int tltrCentersDimension =
  144. MathUtils::round(ResultPoint::distance(topLeft, topRight) / moduleSize);
  145. int tlblCentersDimension =
  146. MathUtils::round(ResultPoint::distance(topLeft, bottomLeft) / moduleSize);
  147. int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
  148. switch (dimension & 0x03) { // mod 4
  149. case 0:
  150. dimension++;
  151. break;
  152. // 1? do nothing
  153. case 2:
  154. dimension--;
  155. break;
  156. case 3:
  157. ostringstream s;
  158. s << "Bad dimension: " << dimension;
  159. throw zxing::ReaderException(s.str().c_str());
  160. }
  161. return dimension;
  162. }
  163. float Detector::calculateModuleSize(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref<ResultPoint> bottomLeft) {
  164. // Take the average
  165. return (calculateModuleSizeOneWay(topLeft, topRight) + calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;
  166. }
  167. float Detector::calculateModuleSizeOneWay(Ref<ResultPoint> pattern, Ref<ResultPoint> otherPattern) {
  168. float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int)pattern->getX(), (int)pattern->getY(),
  169. (int)otherPattern->getX(), (int)otherPattern->getY());
  170. float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int)otherPattern->getX(), (int)otherPattern->getY(),
  171. (int)pattern->getX(), (int)pattern->getY());
  172. if (zxing::isnan(moduleSizeEst1)) {
  173. return moduleSizeEst2;
  174. }
  175. if (zxing::isnan(moduleSizeEst2)) {
  176. return moduleSizeEst1;
  177. }
  178. // Average them, and divide by 7 since we've counted the width of 3 black modules,
  179. // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
  180. return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
  181. }
  182. float Detector::sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {
  183. float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
  184. // Now count other way -- don't run off image though of course
  185. float scale = 1.0f;
  186. int otherToX = fromX - (toX - fromX);
  187. if (otherToX < 0) {
  188. scale = (float) fromX / (float) (fromX - otherToX);
  189. otherToX = 0;
  190. } else if (otherToX >= (int)image_->getWidth()) {
  191. scale = (float) (image_->getWidth() - 1 - fromX) / (float) (otherToX - fromX);
  192. otherToX = image_->getWidth() - 1;
  193. }
  194. int otherToY = (int) (fromY - (toY - fromY) * scale);
  195. scale = 1.0f;
  196. if (otherToY < 0) {
  197. scale = (float) fromY / (float) (fromY - otherToY);
  198. otherToY = 0;
  199. } else if (otherToY >= (int)image_->getHeight()) {
  200. scale = (float) (image_->getHeight() - 1 - fromY) / (float) (otherToY - fromY);
  201. otherToY = image_->getHeight() - 1;
  202. }
  203. otherToX = (int) (fromX + (otherToX - fromX) * scale);
  204. result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
  205. // Middle pixel is double-counted this way; subtract 1
  206. return result - 1.0f;
  207. }
  208. float Detector::sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {
  209. // Mild variant of Bresenham's algorithm;
  210. // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
  211. bool steep = abs(toY - fromY) > abs(toX - fromX);
  212. if (steep) {
  213. int temp = fromX;
  214. fromX = fromY;
  215. fromY = temp;
  216. temp = toX;
  217. toX = toY;
  218. toY = temp;
  219. }
  220. int dx = abs(toX - fromX);
  221. int dy = abs(toY - fromY);
  222. int error = -dx >> 1;
  223. int xstep = fromX < toX ? 1 : -1;
  224. int ystep = fromY < toY ? 1 : -1;
  225. // In black pixels, looking for white, first or second time.
  226. int state = 0;
  227. // Loop up until x == toX, but not beyond
  228. int xLimit = toX + xstep;
  229. for (int x = fromX, y = fromY; x != xLimit; x += xstep) {
  230. int realX = steep ? y : x;
  231. int realY = steep ? x : y;
  232. // Does current pixel mean we have moved white to black or vice versa?
  233. if (!((state == 1) ^ image_->get(realX, realY))) {
  234. if (state == 2) {
  235. return MathUtils::distance(x, y, fromX, fromY);
  236. }
  237. state++;
  238. }
  239. error += dy;
  240. if (error > 0) {
  241. if (y == toY) {
  242. break;
  243. }
  244. y += ystep;
  245. error -= dx;
  246. }
  247. }
  248. // Found black-white-black; give the benefit of the doubt that the next pixel outside the image
  249. // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a
  250. // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
  251. if (state == 2) {
  252. return MathUtils::distance(toX + xstep, toY, fromX, fromY);
  253. }
  254. // else we didn't find even black-white-black; no estimate is really possible
  255. return nan();
  256. }
  257. Ref<AlignmentPattern> Detector::findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY,
  258. float allowanceFactor) {
  259. // Look for an alignment pattern (3 modules in size) around where it
  260. // should be
  261. int allowance = (int)(allowanceFactor * overallEstModuleSize);
  262. int alignmentAreaLeftX = max(0, estAlignmentX - allowance);
  263. int alignmentAreaRightX = min((int)(image_->getWidth() - 1), estAlignmentX + allowance);
  264. if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
  265. throw zxing::ReaderException("region too small to hold alignment pattern");
  266. }
  267. int alignmentAreaTopY = max(0, estAlignmentY - allowance);
  268. int alignmentAreaBottomY = min((int)(image_->getHeight() - 1), estAlignmentY + allowance);
  269. if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) {
  270. throw zxing::ReaderException("region too small to hold alignment pattern");
  271. }
  272. AlignmentPatternFinder alignmentFinder(image_, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX
  273. - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, callback_);
  274. return alignmentFinder.find();
  275. }