You've already forked amazing-qr
mirror of
https://github.com/x-hw/amazing-qr.git
synced 2025-12-03 22:59:09 +02:00
215 lines
8.3 KiB
Python
215 lines
8.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Alignment Pattern Locations
|
|
alig_location = [
|
|
(6, 18), (6, 22), (6, 26), (6, 30), (6, 34), (6, 22, 38), (6, 24, 42), (6, 26, 46), (6, 28, 50), (6, 30, 54), (6, 32, 58), (6, 34, 62), (6, 26, 46, 66), (6, 26, 48, 70), (6, 26, 50, 74), (6, 30, 54, 78), (6, 30, 56, 82), (6, 30, 58, 86), (6, 34, 62, 90), (6, 28, 50, 72, 94), (6, 26, 50, 74, 98), (6, 30, 54, 78, 102), (6, 28, 54, 80, 106), (6, 32, 58, 84, 110), (6, 30, 58, 86, 114), (6, 34, 62, 90, 118), (6, 26, 50, 74, 98, 122), (6, 30, 54, 78, 102, 126), (6, 26, 52, 78, 104, 130), (6, 30, 56, 82, 108, 134), (6, 34, 60, 86, 112, 138), (6, 30, 58, 86, 114, 142), (6, 34, 62, 90, 118, 146), (6, 30, 54, 78, 102, 126, 150), (6, 24, 50, 76, 102, 128, 154), (6, 28, 54, 80, 106, 132, 158), (6, 32, 58, 84, 110, 136, 162), (6, 26, 54, 82, 110, 138, 166), (6, 30, 58, 86, 114, 142, 170)
|
|
]
|
|
|
|
# List of all Format Information Strings
|
|
# [
|
|
# level1[mask_pattern0, mask_pattern1, mask_...3,...],
|
|
# level2[...],
|
|
# level3[...],
|
|
# level4[...]
|
|
# ]
|
|
format_info_str = [
|
|
['111011111000100', '111001011110011', '111110110101010', '111100010011101', '110011000101111', '110001100011000', '110110001000001', '110100101110110'], ['101010000010010', '101000100100101', '101111001111100', '101101101001011', '100010111111001', '100000011001110', '100111110010111', '100101010100000'], ['011010101011111', '011000001101000', '011111100110001', '011101000000110', '010010010110100', '010000110000011', '010111011011010', '010101111101101'], ['001011010001001', '001001110111110', '001110011100111', '001100111010000', '000011101100010', '000001001010101', '000110100001100', '000100000111011']
|
|
]
|
|
|
|
# Version Information Strings
|
|
version_info_str = [
|
|
'000111110010010100', '001000010110111100', '001001101010011001', '001010010011010011', '001011101111110110', '001100011101100010', '001101100001000111', '001110011000001101', '001111100100101000', '010000101101111000', '010001010001011101', '010010101000010111', '010011010100110010', '010100100110100110', '010101011010000011', '010110100011001001', '010111011111101100', '011000111011000100', '011001000111100001', '011010111110101011', '011011000010001110', '011100110000011010', '011101001100111111', '011110110101110101', '011111001001010000', '100000100111010101', '100001011011110000', '100010100010111010', '100011011110011111', '100100101100001011', '100101010000101110', '100110101001100100', '100111010101000001', '101000110001101001'
|
|
]
|
|
|
|
lindex = {'L':0, 'M':1, 'Q':2, 'H':3}
|
|
|
|
def get_qrmatrix(ver, ecl, bits):
|
|
num = (ver - 1) * 4 + 21
|
|
qrmatrix = [([None] * num * num)[i:i+num] for i in range(num * num) if i % num == 0]
|
|
|
|
# Add the Finder Patterns & Add the Separators
|
|
add_finder_and_separator(qrmatrix)
|
|
|
|
# Add the Alignment Patterns
|
|
add_alignment(ver, qrmatrix)
|
|
|
|
# Add the Timing Patterns
|
|
add_timing(qrmatrix)
|
|
|
|
# Add the Dark Module and Reserved Areas
|
|
add_dark_and_reserving(ver, qrmatrix)
|
|
|
|
maskmatrix = [i[:] for i in qrmatrix]
|
|
|
|
# Place the Data Bits
|
|
place_bits(bits, qrmatrix)
|
|
|
|
# Data Masking
|
|
mask_num, qrmatrix = mask(maskmatrix, qrmatrix)
|
|
|
|
# Format Information
|
|
add_format_and_version_string(ver, ecl, mask_num, qrmatrix)
|
|
|
|
return qrmatrix
|
|
|
|
def add_finder_and_separator(m):
|
|
for i in range(8):
|
|
for j in range(8):
|
|
if i in (0, 6):
|
|
m[i][j] = m[-i-1][j] = m[i][-j-1] = 0 if j == 7 else 1
|
|
elif i in (1, 5):
|
|
m[i][j] = m[-i-1][j] = m[i][-j-1] = 1 if j in (0, 6) else 0
|
|
elif i == 7:
|
|
m[i][j] = m[-i-1][j] = m[i][-j-1] = 0
|
|
else:
|
|
m[i][j] = m[-i-1][j] = m[i][-j-1] = 0 if j in (1, 5, 7) else 1
|
|
|
|
def add_alignment(ver, m):
|
|
if ver > 1:
|
|
coordinates = alig_location[ver-2]
|
|
for i in coordinates:
|
|
for j in coordinates:
|
|
if m[i][j] is None:
|
|
add_an_alignment(i, j, m)
|
|
|
|
def add_an_alignment(row, column, m):
|
|
for i in range(row-2, row+3):
|
|
for j in range(column-2, column+3):
|
|
m[i][j] = 1 if i in (row-2, row+2) or j in (column-2, column+2) else 0
|
|
m[row][column] = 1
|
|
|
|
def add_timing(m):
|
|
for i in range(8, len(m)-8):
|
|
m[i][6] = m[6][i] = 1 if i % 2 ==0 else 0
|
|
|
|
def add_dark_and_reserving(ver, m):
|
|
for j in range(8):
|
|
m[8][j] = m[8][-j-1] = m[j][8] = m[-j-1][8] = 0
|
|
m[8][8] = 0
|
|
m[8][6] = m[6][8] = m[-8][8] = 1
|
|
|
|
if ver > 6:
|
|
for i in range(6):
|
|
for j in (-9, -10, -11):
|
|
m[i][j] = m[j][i] = 0
|
|
|
|
def place_bits(bits, m):
|
|
bit = (int(i) for i in bits)
|
|
|
|
up = True
|
|
for a in range(len(m)-1, 0, -2):
|
|
a = a-1 if a <= 6 else a
|
|
irange = range(len(m)-1, -1, -1) if up else range(len(m))
|
|
for i in irange:
|
|
for j in (a, a-1):
|
|
if m[i][j] is None:
|
|
m[i][j] = next(bit)
|
|
up = not up
|
|
|
|
def mask(mm, m):
|
|
mps = get_mask_patterns(mm)
|
|
scores = []
|
|
for mp in mps:
|
|
for i in range(len(mp)):
|
|
for j in range(len(mp)):
|
|
mp[i][j] = mp[i][j] ^ m[i][j]
|
|
scores.append(compute_score(mp))
|
|
best = scores.index(min(scores))
|
|
return best, mps[best]
|
|
|
|
def get_mask_patterns(mm):
|
|
def formula(i, row, column):
|
|
if i == 0:
|
|
return (row + column) % 2 == 0
|
|
elif i == 1:
|
|
return row % 2 == 0
|
|
elif i == 2:
|
|
return column % 3 == 0
|
|
elif i == 3:
|
|
return (row + column) % 3 == 0
|
|
elif i == 4:
|
|
return (row // 2 + column // 3) % 2 == 0
|
|
elif i == 5:
|
|
return ((row * column) % 2) + ((row * column) % 3) == 0
|
|
elif i == 6:
|
|
return (((row * column) % 2) + ((row * column) % 3)) % 2 == 0
|
|
elif i == 7:
|
|
return (((row + column) % 2) + ((row * column) % 3)) % 2 == 0
|
|
|
|
mm[-8][8] = None
|
|
for i in range(len(mm)):
|
|
for j in range(len(mm)):
|
|
mm[i][j] = 0 if mm[i][j] is not None else mm[i][j]
|
|
mps = []
|
|
for i in range(8):
|
|
mp = [ii[:] for ii in mm]
|
|
for row in range(len(mp)):
|
|
for column in range(len(mp)):
|
|
mp[row][column] = 1 if mp[row][column] is None and formula(i, row, column) else 0
|
|
mps.append(mp)
|
|
|
|
return mps
|
|
|
|
def compute_score(m):
|
|
def evaluation1(m):
|
|
def ev1(ma):
|
|
sc = 0
|
|
for mi in ma:
|
|
j = 0
|
|
while j < len(mi)-4:
|
|
n = 4
|
|
while mi[j:j+n+1] in [[1]*(n+1), [0]*(n+1)]:
|
|
n += 1
|
|
(sc, j) = (sc+n-2, j+n) if n > 4 else (sc, j+1)
|
|
return sc
|
|
return ev1(m) + ev1(list(map(list, zip(*m))))
|
|
|
|
def evaluation2(m):
|
|
sc = 0
|
|
for i in range(len(m)-1):
|
|
for j in range(len(m)-1):
|
|
sc += 3 if m[i][j] == m[i+1][j] == m[i][j+1] == m[i+1][j+1] else 0
|
|
return sc
|
|
|
|
def evaluation3(m):
|
|
def ev3(ma):
|
|
sc = 0
|
|
for mi in ma:
|
|
j = 0
|
|
while j < len(mi)-10:
|
|
if mi[j:j+11] == [1,0,1,1,1,0,1,0,0,0,0]:
|
|
sc += 40
|
|
j += 7
|
|
elif mi[j:j+11] == [0,0,0,0,1,0,1,1,1,0,1]:
|
|
sc += 40
|
|
j += 4
|
|
else:
|
|
j += 1
|
|
return sc
|
|
return ev3(m) + ev3(list(map(list, zip(*m))))
|
|
|
|
def evaluation4(m):
|
|
darknum = 0
|
|
for i in m:
|
|
darknum += sum(i)
|
|
percent = darknum / (len(m)**2) * 100
|
|
s = int((50 - percent) / 5) * 5
|
|
return 2*s if s >=0 else -2*s
|
|
|
|
score = evaluation1(m) + evaluation2(m)+ evaluation3(m) + evaluation4(m)
|
|
return score
|
|
|
|
def add_format_and_version_string(ver, ecl, mask_num, m):
|
|
fs = [int(i) for i in format_info_str[lindex[ecl]][mask_num]]
|
|
for j in range(6):
|
|
m[8][j] = m[-j-1][8] = fs[j]
|
|
m[8][-j-1] = m[j][8] = fs[-j-1]
|
|
m[8][7] = m[-7][8] = fs[6]
|
|
m[8][8] = m[8][-8] = fs[7]
|
|
m[7][8] = m[8][-7] = fs[8]
|
|
|
|
if ver > 6:
|
|
vs = (int(i) for i in version_info_str[ver-7])
|
|
for j in range(5, -1, -1):
|
|
for i in (-9, -10, -11):
|
|
m[i][j] = m[j][i] = next(vs) |