JLChen
2021-10-08 f8457b624a75bf8e41489b74f66009eee6891b8b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/*
 * Copyright 2013 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#import "ZXPDF417CodewordDecoder.h"
#import "ZXPDF417Common.h"
 
static float ZX_PDF417_RATIOS_TABLE[ZX_PDF417_SYMBOL_TABLE_LEN][ZX_PDF417_BARS_IN_MODULE];
 
@implementation ZXPDF417CodewordDecoder
 
+ (void)initialize {
  if ([self class] != [ZXPDF417CodewordDecoder class]) return;
 
  // Pre-computes the symbol ratio table.
  for (int i = 0; i < ZX_PDF417_SYMBOL_TABLE_LEN; i++) {
    int currentSymbol = ZX_PDF417_SYMBOL_TABLE[i];
    int currentBit = currentSymbol & 0x1;
    for (int j = 0; j < ZX_PDF417_BARS_IN_MODULE; j++) {
      float size = 0.0f;
      while ((currentSymbol & 0x1) == currentBit) {
        size += 1.0f;
        currentSymbol >>= 1;
      }
      currentBit = currentSymbol & 0x1;
      ZX_PDF417_RATIOS_TABLE[i][ZX_PDF417_BARS_IN_MODULE - j - 1] = size / ZX_PDF417_MODULES_IN_CODEWORD;
    }
  }
}
 
+ (int)decodedValue:(NSArray *)moduleBitCount {
  int decodedValue = [self decodedCodewordValue:[self sampleBitCounts:moduleBitCount]];
  if (decodedValue != -1) {
    return decodedValue;
  }
  return [self closestDecodedValue:moduleBitCount];
}
 
+ (NSArray *)sampleBitCounts:(NSArray *)moduleBitCount {
  float bitCountSum = [ZXPDF417Common bitCountSum:moduleBitCount];
  NSMutableArray *result = [NSMutableArray arrayWithCapacity:ZX_PDF417_BARS_IN_MODULE];
  for (int i = 0; i < ZX_PDF417_BARS_IN_MODULE; i++) {
    [result addObject:@0];
  }
 
  int bitCountIndex = 0;
  int sumPreviousBits = 0;
  for (int i = 0; i < ZX_PDF417_MODULES_IN_CODEWORD; i++) {
    float sampleIndex =
      bitCountSum / (2 * ZX_PDF417_MODULES_IN_CODEWORD) +
      (i * bitCountSum) / ZX_PDF417_MODULES_IN_CODEWORD;
    if (sumPreviousBits + [moduleBitCount[bitCountIndex] intValue] <= sampleIndex) {
      sumPreviousBits += [moduleBitCount[bitCountIndex] intValue];
      bitCountIndex++;
    }
    result[bitCountIndex] = @([result[bitCountIndex] intValue] + 1);
  }
  return result;
}
 
+ (int)decodedCodewordValue:(NSArray *)moduleBitCount {
  int decodedValue = [self bitValue:moduleBitCount];
  return [ZXPDF417Common codeword:decodedValue] == -1 ? -1 : decodedValue;
}
 
+ (int)bitValue:(NSArray *)moduleBitCount {
  long result = 0;
  for (int i = 0; i < [moduleBitCount count]; i++) {
    for (int bit = 0; bit < [moduleBitCount[i] intValue]; bit++) {
      result = (result << 1) | (i % 2 == 0 ? 1 : 0);
    }
  }
  return (int) result;
}
 
+ (int)closestDecodedValue:(NSArray *)moduleBitCount {
  int bitCountSum = [ZXPDF417Common bitCountSum:moduleBitCount];
  float bitCountRatios[ZX_PDF417_BARS_IN_MODULE];
  for (int i = 0; i < ZX_PDF417_BARS_IN_MODULE; i++) {
    bitCountRatios[i] = [moduleBitCount[i] intValue] / (float) bitCountSum;
  }
  float bestMatchError = MAXFLOAT;
  int bestMatch = -1;
  for (int j = 0; j < ZX_PDF417_SYMBOL_TABLE_LEN; j++) {
    float error = 0.0f;
    float *ratioTableRow = ZX_PDF417_RATIOS_TABLE[j];
    for (int k = 0; k < ZX_PDF417_BARS_IN_MODULE; k++) {
      float diff = ratioTableRow[k] - bitCountRatios[k];
      error += diff * diff;
      if (error >= bestMatchError) {
        break;
      }
    }
    if (error < bestMatchError) {
      bestMatchError = error;
      bestMatch = ZX_PDF417_SYMBOL_TABLE[j];
    }
  }
  return bestMatch;
}
 
@end