選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

ZXGlobalHistogramBinarizer.m 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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 "ZXGlobalHistogramBinarizer.h"
  17. #import "ZXBitArray.h"
  18. #import "ZXBitMatrix.h"
  19. #import "ZXByteArray.h"
  20. #import "ZXErrors.h"
  21. #import "ZXIntArray.h"
  22. #import "ZXLuminanceSource.h"
  23. const int ZX_LUMINANCE_BITS = 5;
  24. const int ZX_LUMINANCE_SHIFT = 8 - ZX_LUMINANCE_BITS;
  25. const int ZX_LUMINANCE_BUCKETS = 1 << ZX_LUMINANCE_BITS;
  26. @interface ZXGlobalHistogramBinarizer ()
  27. @property (nonatomic, strong) ZXByteArray *luminances;
  28. @property (nonatomic, strong) ZXIntArray *buckets;
  29. @end
  30. @implementation ZXGlobalHistogramBinarizer
  31. - (id)initWithSource:(ZXLuminanceSource *)source {
  32. if (self = [super initWithSource:source]) {
  33. _luminances = [[ZXByteArray alloc] initWithLength:0];
  34. _buckets = [[ZXIntArray alloc] initWithLength:ZX_LUMINANCE_BUCKETS];
  35. }
  36. return self;
  37. }
  38. - (ZXBitArray *)blackRow:(int)y row:(ZXBitArray *)row error:(NSError **)error {
  39. ZXLuminanceSource *source = self.luminanceSource;
  40. int width = source.width;
  41. if (row == nil || row.size < width) {
  42. row = [[ZXBitArray alloc] initWithSize:width];
  43. } else {
  44. [row clear];
  45. }
  46. [self initArrays:width];
  47. ZXByteArray *localLuminances = [source rowAtY:y row:self.luminances];
  48. ZXIntArray *localBuckets = self.buckets;
  49. for (int x = 0; x < width; x++) {
  50. int pixel = localLuminances.array[x] & 0xff;
  51. localBuckets.array[pixel >> ZX_LUMINANCE_SHIFT]++;
  52. }
  53. int blackPoint = [self estimateBlackPoint:localBuckets];
  54. if (blackPoint == -1) {
  55. if (error) *error = ZXNotFoundErrorInstance();
  56. return nil;
  57. }
  58. int left = localLuminances.array[0] & 0xff;
  59. int center = localLuminances.array[1] & 0xff;
  60. for (int x = 1; x < width - 1; x++) {
  61. int right = localLuminances.array[x + 1] & 0xff;
  62. // A simple -1 4 -1 box filter with a weight of 2.
  63. int luminance = ((center * 4) - left - right) >> 1;
  64. if (luminance < blackPoint) {
  65. [row set:x];
  66. }
  67. left = center;
  68. center = right;
  69. }
  70. return row;
  71. }
  72. - (ZXBitMatrix *)blackMatrixWithError:(NSError **)error {
  73. ZXLuminanceSource *source = self.luminanceSource;
  74. int width = source.width;
  75. int height = source.height;
  76. ZXBitMatrix *matrix = [[ZXBitMatrix alloc] initWithWidth:width height:height];
  77. // Quickly calculates the histogram by sampling four rows from the image. This proved to be
  78. // more robust on the blackbox tests than sampling a diagonal as we used to do.
  79. [self initArrays:width];
  80. // We delay reading the entire image luminance until the black point estimation succeeds.
  81. // Although we end up reading four rows twice, it is consistent with our motto of
  82. // "fail quickly" which is necessary for continuous scanning.
  83. ZXIntArray *localBuckets = self.buckets;
  84. for (int y = 1; y < 5; y++) {
  85. int row = height * y / 5;
  86. ZXByteArray *localLuminances = [source rowAtY:row row:self.luminances];
  87. int right = (width * 4) / 5;
  88. for (int x = width / 5; x < right; x++) {
  89. int pixel = localLuminances.array[x] & 0xff;
  90. localBuckets.array[pixel >> ZX_LUMINANCE_SHIFT]++;
  91. }
  92. }
  93. int blackPoint = [self estimateBlackPoint:localBuckets];
  94. if (blackPoint == -1) {
  95. if (error) *error = ZXNotFoundErrorInstance();
  96. return nil;
  97. }
  98. ZXByteArray *localLuminances = source.matrix;
  99. for (int y = 0; y < height; y++) {
  100. int offset = y * width;
  101. for (int x = 0; x < width; x++) {
  102. int pixel = localLuminances.array[offset + x] & 0xff;
  103. if (pixel < blackPoint) {
  104. [matrix setX:x y:y];
  105. }
  106. }
  107. }
  108. return matrix;
  109. }
  110. - (ZXBinarizer *)createBinarizer:(ZXLuminanceSource *)source {
  111. return [[ZXGlobalHistogramBinarizer alloc] initWithSource:source];
  112. }
  113. - (void)initArrays:(int)luminanceSize {
  114. if (self.luminances.length < luminanceSize) {
  115. self.luminances = [[ZXByteArray alloc] initWithLength:luminanceSize];
  116. }
  117. for (int x = 0; x < ZX_LUMINANCE_BUCKETS; x++) {
  118. self.buckets.array[x] = 0;
  119. }
  120. }
  121. - (int)estimateBlackPoint:(ZXIntArray *)buckets {
  122. // Find the tallest peak in the histogram.
  123. int numBuckets = buckets.length;
  124. int maxBucketCount = 0;
  125. int firstPeak = 0;
  126. int firstPeakSize = 0;
  127. for (int x = 0; x < numBuckets; x++) {
  128. if (buckets.array[x] > firstPeakSize) {
  129. firstPeak = x;
  130. firstPeakSize = buckets.array[x];
  131. }
  132. if (buckets.array[x] > maxBucketCount) {
  133. maxBucketCount = buckets.array[x];
  134. }
  135. }
  136. // Find the second-tallest peak which is somewhat far from the tallest peak.
  137. int secondPeak = 0;
  138. int secondPeakScore = 0;
  139. for (int x = 0; x < numBuckets; x++) {
  140. int distanceToBiggest = x - firstPeak;
  141. // Encourage more distant second peaks by multiplying by square of distance.
  142. int score = buckets.array[x] * distanceToBiggest * distanceToBiggest;
  143. if (score > secondPeakScore) {
  144. secondPeak = x;
  145. secondPeakScore = score;
  146. }
  147. }
  148. // Make sure firstPeak corresponds to the black peak.
  149. if (firstPeak > secondPeak) {
  150. int temp = firstPeak;
  151. firstPeak = secondPeak;
  152. secondPeak = temp;
  153. }
  154. // If there is too little contrast in the image to pick a meaningful black point, throw rather
  155. // than waste time trying to decode the image, and risk false positives.
  156. if (secondPeak - firstPeak <= numBuckets / 16) {
  157. return -1;
  158. }
  159. // Find a valley between them that is low and closer to the white peak.
  160. int bestValley = secondPeak - 1;
  161. int bestValleyScore = -1;
  162. for (int x = secondPeak - 1; x > firstPeak; x--) {
  163. int fromFirst = x - firstPeak;
  164. int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets.array[x]);
  165. if (score > bestValleyScore) {
  166. bestValley = x;
  167. bestValleyScore = score;
  168. }
  169. }
  170. return bestValley << ZX_LUMINANCE_SHIFT;
  171. }
  172. @end