| 1 |
from hq import getZoom, scaleWeights |
| 2 |
from hq_gen import ( |
| 3 |
edges, expandQuadrant, genExpr2, genExpr3, genExpr4, simplifyWeights |
| 4 |
) |
| 5 |
|
| 6 |
from collections import defaultdict |
| 7 |
from itertools import count |
| 8 |
|
| 9 |
def normalizeWeights(pixelExpr): |
| 10 |
maxSum = max( |
| 11 |
sum(weights) |
| 12 |
for expr in pixelExpr |
| 13 |
for weights in expr |
| 14 |
) |
| 15 |
return [ |
| 16 |
[ scaleWeights(weights, maxSum) for weights in expr ] |
| 17 |
for expr in pixelExpr |
| 18 |
] |
| 19 |
|
| 20 |
def extractTopLeftWeights(weights): |
| 21 |
assert all(weights[n] == 0 for n in (2, 5, 6, 7, 8)), weights |
| 22 |
return weights[0 : 2] + weights[3 : 5] |
| 23 |
|
| 24 |
def extractTopLeftQuadrant(pixelExpr): |
| 25 |
zoom = getZoom(pixelExpr) |
| 26 |
quadrantWidth = (zoom + 1) // 2 |
| 27 |
quadrantMap = [ |
| 28 |
qy * zoom + qx |
| 29 |
for qy in range(quadrantWidth) |
| 30 |
for qx in range(quadrantWidth) |
| 31 |
] |
| 32 |
for expr in [ |
| 33 |
[ expr[subPixel] for subPixel in quadrantMap ] |
| 34 |
for expr in pixelExpr |
| 35 |
]: |
| 36 |
for weights in expr: |
| 37 |
for neighbour in (2, 5, 6, 7, 8): |
| 38 |
assert weights[neighbour] == 0, weights |
| 39 |
return [ |
| 40 |
[ extractTopLeftWeights(expr[subPixel]) for subPixel in quadrantMap ] |
| 41 |
for expr in pixelExpr |
| 42 |
] |
| 43 |
|
| 44 |
def convertExpr4to2(case, expr4): |
| 45 |
weights2 = [0] * 4 |
| 46 |
for weights4 in expr4: |
| 47 |
for neighbour, weight in enumerate(scaleWeights(weights4, 256)): |
| 48 |
weights2[neighbour] += weight |
| 49 |
weights2 = simplifyWeights(weights2) |
| 50 |
if ((case >> 4) & 15) in (2, 6, 8, 12): |
| 51 |
assert sorted(weights2) == [0, 2, 7, 23] |
| 52 |
weightMap = { 0: 0, 2: 1, 7: 1, 23: 2 } |
| 53 |
elif ((case >> 4) & 15) in (0, 1, 4, 5): |
| 54 |
assert sorted(weights2) == [0, 3, 3, 10] |
| 55 |
weightMap = { 0: 0, 3: 1, 10: 2 } |
| 56 |
else: |
| 57 |
weightMap = None |
| 58 |
if weightMap: |
| 59 |
weights2 = tuple(weightMap[weight] for weight in weights2) |
| 60 |
return [weights2] |
| 61 |
|
| 62 |
def convert4to2(topLeftQuadrant4): |
| 63 |
return [ |
| 64 |
convertExpr4to2(case, expr4) |
| 65 |
for case, expr4 in enumerate(topLeftQuadrant4) |
| 66 |
] |
| 67 |
|
| 68 |
def analyzeCaseFunction(caseToWeights): |
| 69 |
weightsToCase = defaultdict(set) |
| 70 |
for case, weights in enumerate(caseToWeights): |
| 71 |
weightsToCase[weights].add(case) |
| 72 |
for weights in sorted(weightsToCase.keys()): |
| 73 |
cases = weightsToCase[weights] |
| 74 |
partitions = { |
| 75 |
tuple((case >> edgeNum) & 1 for edgeNum in range(11, -1, -1)) |
| 76 |
for case in cases |
| 77 |
} |
| 78 |
|
| 79 |
for edgeNum in range(12): |
| 80 |
changed = True |
| 81 |
while changed: |
| 82 |
changed = False |
| 83 |
for part in list(partitions): |
| 84 |
if part not in partitions: |
| 85 |
continue |
| 86 |
if part[edgeNum] < 2: |
| 87 |
pre = part[ : edgeNum] |
| 88 |
post = part[edgeNum + 1 : ] |
| 89 |
dual = pre + (part[edgeNum] ^ 1,) + post |
| 90 |
if dual in partitions: |
| 91 |
partitions.remove(part) |
| 92 |
partitions.remove(dual) |
| 93 |
partitions.add(pre + (2,) + post) |
| 94 |
changed = True |
| 95 |
yield weights, [ |
| 96 |
''.join('01x'[bit] for bit in partition) |
| 97 |
for partition in sorted(partitions) |
| 98 |
] |
| 99 |
|
| 100 |
|
| 101 |
|
| 102 |
def findRelevantEdges(): |
| 103 |
for zoom, genExpr in zip(count(2), (genExpr2, genExpr3, genExpr4)): |
| 104 |
quadrant = genExpr() |
| 105 |
quadrantWidth = (zoom + 1) // 2 |
| 106 |
assert quadrantWidth ** 2 == len(quadrant[0]) |
| 107 |
subPixelOutput = [[] for _ in range(quadrantWidth * 10)] |
| 108 |
for subPixel in range(quadrantWidth ** 2): |
| 109 |
neighbourOutput = [[] for _ in range(8)] |
| 110 |
for neighbour in range(4): |
| 111 |
relevant = [ |
| 112 |
edgeNum |
| 113 |
for edgeNum in range(12) |
| 114 |
if any( |
| 115 |
quadrant[case][subPixel][neighbour] != |
| 116 |
quadrant[case ^ (1 << edgeNum)][subPixel][neighbour] |
| 117 |
for case in range(len(quadrant)) |
| 118 |
) |
| 119 |
] |
| 120 |
zero = len(relevant) == 0 and all( |
| 121 |
quadrant[case][subPixel][neighbour] == 0 |
| 122 |
for case in range(len(quadrant)) |
| 123 |
) |
| 124 |
center = ('.' if zero else str(neighbour)) |
| 125 |
for rowNum, row in enumerate(formatEdges(relevant)): |
| 126 |
if rowNum == 1: |
| 127 |
assert row[1] == 'o' |
| 128 |
row = row[0] + center + row[2] |
| 129 |
neighbourOutput[(neighbour // 2) * 4 + rowNum].append(row) |
| 130 |
neighbourOutput[(neighbour // 2) * 4 + 3].append(' ') |
| 131 |
for lineNum, line in enumerate(neighbourOutput): |
| 132 |
lineOutput = ' %s |' % ' '.join(line) |
| 133 |
subPixelOutput[ |
| 134 |
(subPixel // quadrantWidth) * 10 + lineNum + 1 |
| 135 |
].append(lineOutput) |
| 136 |
if lineNum == 7: |
| 137 |
subPixelOutput[(subPixel // quadrantWidth) * 10].append( |
| 138 |
lineOutput |
| 139 |
) |
| 140 |
subPixelOutput[(subPixel // quadrantWidth) * 10 + 9].append( |
| 141 |
'%ss%d' % ('-' * (len(lineOutput) - 2), subPixel) |
| 142 |
) |
| 143 |
print('Relevant edges for zoom %d:' % zoom) |
| 144 |
print() |
| 145 |
for line in subPixelOutput: |
| 146 |
print(' %s' % ''.join(line)) |
| 147 |
print() |
| 148 |
|
| 149 |
|
| 150 |
|
| 151 |
def formatEdges(edgeNums): |
| 152 |
cells = ['.' for _ in range(9)] |
| 153 |
cells[4] = 'o' |
| 154 |
def combine(index, ch): |
| 155 |
old = cells[index] |
| 156 |
if old == '.': |
| 157 |
cells[index] = ch |
| 158 |
elif (old == '/' and ch == '\\') or (old == '\\' and ch == '/'): |
| 159 |
cells[index] = 'X' |
| 160 |
else: |
| 161 |
assert False, (index, old, ch) |
| 162 |
for edgeNum in edgeNums: |
| 163 |
edge = edges[edgeNum] |
| 164 |
if 4 in edge: |
| 165 |
other = sum(edge) - 4 |
| 166 |
combine(other, '\\|/-'[other if other < 4 else 8 - other]) |
| 167 |
else: |
| 168 |
assert edge in ((1, 5), (5, 7), (3, 7), (1, 3)) |
| 169 |
x = 0 if 3 in edge else 2 |
| 170 |
y = 0 if 1 in edge else 2 |
| 171 |
combine(y * 3 + x, '/' if x == y else '\\') |
| 172 |
for y in range(3): |
| 173 |
yield ''.join(cells[y * 3 : (y + 1) * 3]) |
| 174 |
|
| 175 |
def formatWeights(weights): |
| 176 |
return ' '.join('%3d' % weight for weight in weights) |
| 177 |
|
| 178 |
def comparePixelExpr(pixelExpr1, pixelExpr2): |
| 179 |
zoom = getZoom(pixelExpr1) |
| 180 |
assert zoom == getZoom(pixelExpr2) |
| 181 |
mismatchCount = 0 |
| 182 |
for case, (expr1, expr2) in enumerate(zip(pixelExpr1, pixelExpr2)): |
| 183 |
if expr1 != expr2: |
| 184 |
binStr = bin(case)[2 : ].zfill(12) |
| 185 |
print('case: %d (%s)' % ( |
| 186 |
case, |
| 187 |
' '.join(binStr[i : i + 4] for i in range(0, 12, 4)) |
| 188 |
)) |
| 189 |
for sy in range(zoom): |
| 190 |
matrices = [] |
| 191 |
for sx in range(zoom): |
| 192 |
subPixel = sy * zoom + sx |
| 193 |
subExpr1 = expr1[subPixel] |
| 194 |
subExpr2 = expr2[subPixel] |
| 195 |
rows = [] |
| 196 |
for ny in range(3): |
| 197 |
rows.append('%s %s %s' % ( |
| 198 |
formatWeights(subExpr1[ny * 3 : (ny + 1) * 3]), |
| 199 |
'.' if subExpr1 == subExpr2 else '!', |
| 200 |
formatWeights(subExpr2[ny * 3 : (ny + 1) * 3]) |
| 201 |
)) |
| 202 |
matrices.append(rows) |
| 203 |
for ny in range(3): |
| 204 |
print(' %s' % ' '.join( |
| 205 |
matrices[sx][ny] |
| 206 |
for sx in range(zoom) |
| 207 |
)) |
| 208 |
print() |
| 209 |
mismatchCount += 1 |
| 210 |
print('Number of mismatches: %d' % mismatchCount) |
| 211 |
|
| 212 |
|
| 213 |
|
| 214 |
def checkConvert4to2(): |
| 215 |
topLeftQuadrant4 = genExpr4() |
| 216 |
topLeftQuadrant2 = convert4to2(topLeftQuadrant4) |
| 217 |
pixelExpr2 = expandQuadrant(topLeftQuadrant2, 2) |
| 218 |
|
| 219 |
comparePixelExpr(pixelExpr2, expandQuadrant(genExpr2(), 2)) |
| 220 |
|
| 221 |
|
| 222 |
|
| 223 |
if __name__ == '__main__': |
| 224 |
findRelevantEdges() |
| 225 |
checkConvert4to2() |