手动解码 QR 码 - 如何正确提取长度和编码?
Manually decoding a QR code - how to properly extract length and encoding?
考虑以下代码:
#!/usr/bin/env python
import sys
m = [' ',
' XXXXXXX X X XXXXXXX ',
' X X XX X X ',
' X XXX X X XX X XXX X ',
' X XXX X XX X XXX X ',
' X XXX X XXX X XXX X ',
' X X XXX X X ',
' XXXXXXX X X X XXXXXXX ',
' XXXX ',
' XX XX X X XX ',
' XX XX X XXX XXXX X ',
' XXX XX X X X X X ',
' X XX X XXXX X ',
' X XXXXXX X XXXXXXXX ',
' XXXX XX XX ',
' XXXXXXX XXX XX ',
' X X X XXX ',
' X XXX X X XX X X X X ',
' X XXX X XX XXX ',
' X XXX X XX XXX XX ',
' X X XXX X X X XX ',
' XXXXXXX X X XX X ',
' ']
m = map(lambda l: l.replace('X', '1').replace(' ', '0'), m)
def flatten_arr(arr):
return (''.join(str(c) for c in arr))
def arr_to_b2(arr):
return int(flatten_arr(arr), 2)
def main():
if len(m) != len(m[0]):
raise ValueError("QR code is not square")
if len(m) != 23:
raise NotImplementedError("This program can currently"
" parse only 23x23 QR codes.")
ECC_EXPLAIN = {0b00: 'H (30%)', 0b01: 'Q (25%)',
0b10: 'M (15%)', 0b11: 'L (7%)'}
ecc_arr = m[9][1:3]
ecc_lvl = arr_to_b2(ecc_arr)
print("Error correction: %s" % ECC_EXPLAIN.get(ecc_lvl, ecc_lvl))
# TODO: check if this is consistent with its horizontal equivalent
mask_arr = m[9][3:6]
mask = arr_to_b2(mask_arr)
print("Mask: %s" % flatten_arr(mask_arr))
ENCODING_EXPLAIN = {
0b0000: 'End of message',
0b0001: 'Numeric encoding (10 bits per 3 digits)',
0b0010: 'Alphanumeric encoding (11 bits per 2 characters)',
0b0011: 'Structured append (used to split a message across multiple QR symbols)',
0b0100: 'Byte encoding (8 bits per character)',
0b0101: 'FNC1 in first position (see Code 128 for more information)',
0b0111: 'Extended Channel Interpretation (select alternate character set or encoding)',
0b1000: 'Kanji encoding (13 bits per character)',
0b1001: 'FNC1 in second position',
}
encoding_arr = [m[21][21], m[21][20], m[20][21], m[20][20]]
print("Encoding: %s" % flatten_arr(encoding_arr))
if __name__ == '__main__':
main()
我正在尝试根据维基百科文章解码示例 QR 码,但结果似乎没有意义 - 编码似乎是“0011”,而我希望是字母数字编码。我看错了图表吗?如何正确提取二维码的编码和长度?
看来我在阅读时没有屏蔽这些位。这是我解决它的方法:
#!/usr/bin/env python
import sys
m = [[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1],
[0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1],
[0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0],
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0]]
def flatten_arr(arr):
return (''.join(str(c) for c in arr))
def arr_to_b2(arr):
return int(flatten_arr(arr), 2)
def mask(m, i, j):
if ((i*j)%3 + i*j) % 2 == 0:
return int(not(m[i][j]))
return m[i][j]
def main(m):
if len(m) != len(m[0]):
raise ValueError("QR code is not square")
if len(m) != 21:
raise NotImplementedError("This program can currently"
" parse only 21x21 QR codes.")
ECC_EXPLAIN = {0b00: 'H (30%)', 0b01: 'Q (25%)',
0b10: 'M (15%)', 0b11: 'L (7%)'}
ecc_arr = m[8][0:2]
ecc_lvl = arr_to_b2(ecc_arr)
print("Error correction: %s" % ECC_EXPLAIN.get(ecc_lvl, ecc_lvl))
# TODO: check if this is consistent with its horizontal equivalent
MASKS = {
0b000: lambda i, j: (i * j) % 2 + (i * j) % 3 == 0,
0b001: lambda i, j: (i / 2 + j / 3) % 2 == 0,
0b010: lambda i, j: ((i * j) % 3 + i + j) % 2 == 0,
0b011: lambda i, j: ((i * j) % 3 + i * j) % 2 == 0,
0b100: lambda i, j: i % 2 == 0,
0b101: lambda i, j: (i + j) % 2 == 0,
0b110: lambda i, j: (i + j) % 3 == 0,
0b111: lambda i, j: j % 3 == 0,
}
mask_arr = m[8][2:5]
mask_id = arr_to_b2(mask_arr)
print("Mask: %s" % flatten_arr(mask_arr))
masked = MASKS[mask_id]
mask = lambda m, i, j: int(not m[i][j]) if masked(i,j) else m[i][j]
ENCODING_EXPLAIN = {
0b0000: 'End of message',
0b0001: 'Numeric encoding (10 bits per 3 digits)',
0b0010: 'Alphanumeric encoding (11 bits per 2 characters)',
0b0011: 'Structured append (used to split a message across multiple QR symbols)',
0b0100: 'Byte encoding (8 bits per character)',
0b0101: 'FNC1 in first position (see Code 128 for more information)',
0b0111: 'Extended Channel Interpretation (select alternate character set or encoding)',
0b1000: 'Kanji encoding (13 bits per character)',
0b1001: 'FNC1 in second position',
}
encoding_arr = [mask(m, 20, 20), mask(m, 20, 19),
mask(m, 19, 20), mask(m, 19, 19)]
encoding_id = arr_to_b2(encoding_arr)
length_arr = [mask(m, 18, 20), mask(m, 18, 19),
mask(m, 17, 20), mask(m, 17, 19),
mask(m, 16, 20), mask(m, 16, 19),
mask(m, 15, 20), mask(m, 15, 19)]
length = arr_to_b2(length_arr)
print("Length: %s" % length)
print("Encoding: %s (%s)" % (ENCODING_EXPLAIN[encoding_id],
flatten_arr(encoding_arr)))
if 'ipython' in sys.argv:
import IPython
IPython.embed()
if __name__ == '__main__':
main(m)
考虑以下代码:
#!/usr/bin/env python
import sys
m = [' ',
' XXXXXXX X X XXXXXXX ',
' X X XX X X ',
' X XXX X X XX X XXX X ',
' X XXX X XX X XXX X ',
' X XXX X XXX X XXX X ',
' X X XXX X X ',
' XXXXXXX X X X XXXXXXX ',
' XXXX ',
' XX XX X X XX ',
' XX XX X XXX XXXX X ',
' XXX XX X X X X X ',
' X XX X XXXX X ',
' X XXXXXX X XXXXXXXX ',
' XXXX XX XX ',
' XXXXXXX XXX XX ',
' X X X XXX ',
' X XXX X X XX X X X X ',
' X XXX X XX XXX ',
' X XXX X XX XXX XX ',
' X X XXX X X X XX ',
' XXXXXXX X X XX X ',
' ']
m = map(lambda l: l.replace('X', '1').replace(' ', '0'), m)
def flatten_arr(arr):
return (''.join(str(c) for c in arr))
def arr_to_b2(arr):
return int(flatten_arr(arr), 2)
def main():
if len(m) != len(m[0]):
raise ValueError("QR code is not square")
if len(m) != 23:
raise NotImplementedError("This program can currently"
" parse only 23x23 QR codes.")
ECC_EXPLAIN = {0b00: 'H (30%)', 0b01: 'Q (25%)',
0b10: 'M (15%)', 0b11: 'L (7%)'}
ecc_arr = m[9][1:3]
ecc_lvl = arr_to_b2(ecc_arr)
print("Error correction: %s" % ECC_EXPLAIN.get(ecc_lvl, ecc_lvl))
# TODO: check if this is consistent with its horizontal equivalent
mask_arr = m[9][3:6]
mask = arr_to_b2(mask_arr)
print("Mask: %s" % flatten_arr(mask_arr))
ENCODING_EXPLAIN = {
0b0000: 'End of message',
0b0001: 'Numeric encoding (10 bits per 3 digits)',
0b0010: 'Alphanumeric encoding (11 bits per 2 characters)',
0b0011: 'Structured append (used to split a message across multiple QR symbols)',
0b0100: 'Byte encoding (8 bits per character)',
0b0101: 'FNC1 in first position (see Code 128 for more information)',
0b0111: 'Extended Channel Interpretation (select alternate character set or encoding)',
0b1000: 'Kanji encoding (13 bits per character)',
0b1001: 'FNC1 in second position',
}
encoding_arr = [m[21][21], m[21][20], m[20][21], m[20][20]]
print("Encoding: %s" % flatten_arr(encoding_arr))
if __name__ == '__main__':
main()
我正在尝试根据维基百科文章解码示例 QR 码,但结果似乎没有意义 - 编码似乎是“0011”,而我希望是字母数字编码。我看错了图表吗?如何正确提取二维码的编码和长度?
看来我在阅读时没有屏蔽这些位。这是我解决它的方法:
#!/usr/bin/env python
import sys
m = [[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1],
[0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1],
[0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0],
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0]]
def flatten_arr(arr):
return (''.join(str(c) for c in arr))
def arr_to_b2(arr):
return int(flatten_arr(arr), 2)
def mask(m, i, j):
if ((i*j)%3 + i*j) % 2 == 0:
return int(not(m[i][j]))
return m[i][j]
def main(m):
if len(m) != len(m[0]):
raise ValueError("QR code is not square")
if len(m) != 21:
raise NotImplementedError("This program can currently"
" parse only 21x21 QR codes.")
ECC_EXPLAIN = {0b00: 'H (30%)', 0b01: 'Q (25%)',
0b10: 'M (15%)', 0b11: 'L (7%)'}
ecc_arr = m[8][0:2]
ecc_lvl = arr_to_b2(ecc_arr)
print("Error correction: %s" % ECC_EXPLAIN.get(ecc_lvl, ecc_lvl))
# TODO: check if this is consistent with its horizontal equivalent
MASKS = {
0b000: lambda i, j: (i * j) % 2 + (i * j) % 3 == 0,
0b001: lambda i, j: (i / 2 + j / 3) % 2 == 0,
0b010: lambda i, j: ((i * j) % 3 + i + j) % 2 == 0,
0b011: lambda i, j: ((i * j) % 3 + i * j) % 2 == 0,
0b100: lambda i, j: i % 2 == 0,
0b101: lambda i, j: (i + j) % 2 == 0,
0b110: lambda i, j: (i + j) % 3 == 0,
0b111: lambda i, j: j % 3 == 0,
}
mask_arr = m[8][2:5]
mask_id = arr_to_b2(mask_arr)
print("Mask: %s" % flatten_arr(mask_arr))
masked = MASKS[mask_id]
mask = lambda m, i, j: int(not m[i][j]) if masked(i,j) else m[i][j]
ENCODING_EXPLAIN = {
0b0000: 'End of message',
0b0001: 'Numeric encoding (10 bits per 3 digits)',
0b0010: 'Alphanumeric encoding (11 bits per 2 characters)',
0b0011: 'Structured append (used to split a message across multiple QR symbols)',
0b0100: 'Byte encoding (8 bits per character)',
0b0101: 'FNC1 in first position (see Code 128 for more information)',
0b0111: 'Extended Channel Interpretation (select alternate character set or encoding)',
0b1000: 'Kanji encoding (13 bits per character)',
0b1001: 'FNC1 in second position',
}
encoding_arr = [mask(m, 20, 20), mask(m, 20, 19),
mask(m, 19, 20), mask(m, 19, 19)]
encoding_id = arr_to_b2(encoding_arr)
length_arr = [mask(m, 18, 20), mask(m, 18, 19),
mask(m, 17, 20), mask(m, 17, 19),
mask(m, 16, 20), mask(m, 16, 19),
mask(m, 15, 20), mask(m, 15, 19)]
length = arr_to_b2(length_arr)
print("Length: %s" % length)
print("Encoding: %s (%s)" % (ENCODING_EXPLAIN[encoding_id],
flatten_arr(encoding_arr)))
if 'ipython' in sys.argv:
import IPython
IPython.embed()
if __name__ == '__main__':
main(m)