OXYGENHUB
◆ main openmsx / src / video / scalers / hq_analyze.py
6.3 KiB · Python · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 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
		# Repeatedly merge partitions until we have a minimal set.
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
# Various analysis:
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
# Visualization:
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
# Sanity checks:
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
# Main:
222
223
if __name__ == '__main__':
224
	findRelevantEdges()
225
	checkConvert4to2()