mirror of
https://github.com/KrisKennaway/ii-pix.git
synced 2024-06-03 05:29:32 +00:00
Working SHR version. Still just uses a single palette
This commit is contained in:
parent
21058084e2
commit
80885aabf9
32
convert.py
32
convert.py
|
@ -34,11 +34,9 @@ def cluster_palette(image: Image):
|
||||||
with colour.utilities.suppress_warnings(colour_usage_warnings=True):
|
with colour.utilities.suppress_warnings(colour_usage_warnings=True):
|
||||||
palette_rgb = colour.convert(palette_cam, "CAM16UCS", "RGB")
|
palette_rgb = colour.convert(palette_cam, "CAM16UCS", "RGB")
|
||||||
# SHR colour palette only uses 4-bit values
|
# SHR colour palette only uses 4-bit values
|
||||||
# TODO: do this more carefully
|
palette_rgb = np.round(palette_rgb * 15) / 15
|
||||||
palette_rgb = np.clip(np.round(palette_rgb * 16).astype(np.uint32) *
|
# palette_rgb = palette_rgb.astype(np.float32) / 255
|
||||||
16, 0, 255)
|
return palette_rgb.astype(np.float32)
|
||||||
palette_rgb = palette_rgb.astype(np.float32) / 255
|
|
||||||
return palette_rgb
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,13 +94,21 @@ def main():
|
||||||
image = image_py.open(args.input)
|
image = image_py.open(args.input)
|
||||||
if args.show_input:
|
if args.show_input:
|
||||||
image_py.resize(image, screen.X_RES, screen.Y_RES,
|
image_py.resize(image, screen.X_RES, screen.Y_RES,
|
||||||
srgb_output=True).show()
|
srgb_output=False).show()
|
||||||
rgb = np.array(
|
rgb = np.array(
|
||||||
image_py.resize(image, screen.X_RES, screen.Y_RES,
|
image_py.resize(image, screen.X_RES, screen.Y_RES,
|
||||||
gamma=args.gamma_correct)).astype(np.float32) / 255
|
gamma=args.gamma_correct, srgb_output=True)).astype(
|
||||||
|
np.float32) / 255
|
||||||
|
|
||||||
palette_rgb = cluster_palette(rgb)
|
palette_rgb = cluster_palette(rgb)
|
||||||
output_rgb = dither_pyx.dither_shr(rgb, palette_rgb, rgb_to_cam16)
|
# print(palette_rgb)
|
||||||
|
# screen.set_palette(0, (image_py.linear_to_srgb_array(palette_rgb) *
|
||||||
|
# 15).astype(np.uint8))
|
||||||
|
screen.set_palette(0, (np.round(palette_rgb * 15)).astype(np.uint8))
|
||||||
|
|
||||||
|
output_4bit = dither_pyx.dither_shr(rgb, palette_rgb, rgb_to_cam16)
|
||||||
|
screen.set_pixels(output_4bit)
|
||||||
|
output_rgb = (palette_rgb[output_4bit] * 255).astype(np.uint8)
|
||||||
output_srgb = image_py.linear_to_srgb(output_rgb).astype(np.uint8)
|
output_srgb = image_py.linear_to_srgb(output_rgb).astype(np.uint8)
|
||||||
|
|
||||||
# dither = dither_pattern.PATTERNS[args.dither]()
|
# dither = dither_pattern.PATTERNS[args.dither]()
|
||||||
|
@ -120,18 +126,20 @@ def main():
|
||||||
# output_screen.bitmap_to_image_rgb(bitmap)).astype(np.uint8)
|
# output_screen.bitmap_to_image_rgb(bitmap)).astype(np.uint8)
|
||||||
out_image = image_py.resize(
|
out_image = image_py.resize(
|
||||||
Image.fromarray(output_srgb), screen.X_RES, screen.Y_RES,
|
Image.fromarray(output_srgb), screen.X_RES, screen.Y_RES,
|
||||||
srgb_output=True)
|
srgb_output=False) # XXX true
|
||||||
|
|
||||||
if args.show_output:
|
if args.show_output:
|
||||||
out_image.show()
|
out_image.show()
|
||||||
|
|
||||||
# Save Double hi-res image
|
# Save Double hi-res image
|
||||||
# outfile = os.path.join(os.path.splitext(args.output)[0] + "-preview.png")
|
outfile = os.path.join(os.path.splitext(args.output)[0] + "-preview.png")
|
||||||
# out_image.save(outfile, "PNG")
|
out_image.save(outfile, "PNG")
|
||||||
# screen.pack(bitmap)
|
screen.pack()
|
||||||
# with open(args.output, "wb") as f:
|
# with open(args.output, "wb") as f:
|
||||||
# f.write(bytes(screen.aux))
|
# f.write(bytes(screen.aux))
|
||||||
# f.write(bytes(screen.main))
|
# f.write(bytes(screen.main))
|
||||||
|
with open(args.output, "wb") as f:
|
||||||
|
f.write(bytes(screen.memory))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
66
dither.pyx
66
dither.pyx
|
@ -329,11 +329,13 @@ import colour
|
||||||
@cython.boundscheck(False)
|
@cython.boundscheck(False)
|
||||||
@cython.wraparound(False)
|
@cython.wraparound(False)
|
||||||
def dither_shr(float[:, :, ::1] working_image, float[:, ::1] palette_rgb, float[:,::1] rgb_to_cam16ucs):
|
def dither_shr(float[:, :, ::1] working_image, float[:, ::1] palette_rgb, float[:,::1] rgb_to_cam16ucs):
|
||||||
cdef int y, x, idx
|
cdef int y, x, idx, best_colour_idx
|
||||||
cdef float best_distance, distance
|
cdef float best_distance, distance
|
||||||
cdef float[::1] best_colour_rgb, pixel_cam, colour_rgb, colour_cam
|
cdef float[::1] best_colour_rgb, pixel_cam, colour_rgb, colour_cam
|
||||||
cdef float[3] quant_error
|
cdef float[3] quant_error
|
||||||
|
|
||||||
|
cdef (unsigned char)[:, ::1] output_4bit = np.zeros((200, 320), dtype=np.uint8)
|
||||||
|
|
||||||
for y in range(200):
|
for y in range(200):
|
||||||
print(y)
|
print(y)
|
||||||
for x in range(320):
|
for x in range(320):
|
||||||
|
@ -341,12 +343,15 @@ def dither_shr(float[:, :, ::1] working_image, float[:, ::1] palette_rgb, float[
|
||||||
rgb_to_cam16ucs, working_image[y, x, 0], working_image[y, x, 1], working_image[y, x, 2])
|
rgb_to_cam16ucs, working_image[y, x, 0], working_image[y, x, 1], working_image[y, x, 2])
|
||||||
|
|
||||||
best_distance = 1e9
|
best_distance = 1e9
|
||||||
|
best_colour_idx = 0
|
||||||
for idx, colour_rgb in enumerate(palette_rgb):
|
for idx, colour_rgb in enumerate(palette_rgb):
|
||||||
colour_cam = convert_rgb_to_cam16ucs(rgb_to_cam16ucs, colour_rgb[0], colour_rgb[1], colour_rgb[2])
|
colour_cam = convert_rgb_to_cam16ucs(rgb_to_cam16ucs, colour_rgb[0], colour_rgb[1], colour_rgb[2])
|
||||||
distance = colour_distance_squared(pixel_cam, colour_cam)
|
distance = colour_distance_squared(pixel_cam, colour_cam)
|
||||||
if distance < best_distance:
|
if distance < best_distance:
|
||||||
best_distance = distance
|
best_distance = distance
|
||||||
best_colour_rgb = colour_rgb
|
best_colour_rgb = colour_rgb
|
||||||
|
best_colour_idx = idx
|
||||||
|
output_4bit[y, x] = best_colour_idx
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
quant_error[i] = working_image[y, x, i] - best_colour_rgb[i]
|
quant_error[i] = working_image[y, x, i] - best_colour_rgb[i]
|
||||||
|
@ -359,14 +364,63 @@ def dither_shr(float[:, :, ::1] working_image, float[:, ::1] palette_rgb, float[
|
||||||
working_image[y, x + 1, i] = clip(
|
working_image[y, x + 1, i] = clip(
|
||||||
working_image[y, x + 1, i] + quant_error[i] * (7 / 16), 0, 1)
|
working_image[y, x + 1, i] + quant_error[i] * (7 / 16), 0, 1)
|
||||||
if y < 199:
|
if y < 199:
|
||||||
|
if x > 0:
|
||||||
|
working_image[y + 1, x - 1, i] = clip(
|
||||||
|
working_image[y + 1, x - 1, i] + quant_error[i] * (3 / 16), 0,
|
||||||
|
1)
|
||||||
working_image[y + 1, x, i] = clip(
|
working_image[y + 1, x, i] = clip(
|
||||||
working_image[y + 1, x, i] + quant_error[i] * (5 / 16), 0, 1)
|
working_image[y + 1, x, i] + quant_error[i] * (5 / 16), 0, 1)
|
||||||
if x < 319:
|
if x < 319:
|
||||||
working_image[y + 1, x + 1, i] = clip(
|
working_image[y + 1, x + 1, i] = clip(
|
||||||
working_image[y + 1, x + 1, i] + quant_error[i] * (1 / 16),
|
working_image[y + 1, x + 1, i] + quant_error[i] * (1 / 16),
|
||||||
0, 1)
|
0, 1)
|
||||||
if x > 0:
|
|
||||||
working_image[y + 1, x - 1, i] = clip(
|
# # 0 0 X 7 5
|
||||||
working_image[y + 1, x - 1, i] + quant_error[i] * (3 / 16), 0,
|
# # 3 5 7 5 3
|
||||||
1)
|
# # 1 3 5 3 1
|
||||||
return np.array(working_image).astype(np.float32) * 255
|
# if x < 319:
|
||||||
|
# working_image[y, x + 1, i] = clip(
|
||||||
|
# working_image[y, x + 1, i] + quant_error[i] * (7 / 48), 0, 1)
|
||||||
|
# if x < 318:
|
||||||
|
# working_image[y, x + 2, i] = clip(
|
||||||
|
# working_image[y, x + 2, i] + quant_error[i] * (5 / 48), 0, 1)
|
||||||
|
# if y < 199:
|
||||||
|
# if x > 1:
|
||||||
|
# working_image[y + 1, x - 2, i] = clip(
|
||||||
|
# working_image[y + 1, x - 2, i] + quant_error[i] * (3 / 48), 0,
|
||||||
|
# 1)
|
||||||
|
# if x > 0:
|
||||||
|
# working_image[y + 1, x - 1, i] = clip(
|
||||||
|
# working_image[y + 1, x - 1, i] + quant_error[i] * (5 / 48), 0,
|
||||||
|
# 1)
|
||||||
|
# working_image[y + 1, x, i] = clip(
|
||||||
|
# working_image[y + 1, x, i] + quant_error[i] * (7 / 48), 0, 1)
|
||||||
|
# if x < 319:
|
||||||
|
# working_image[y + 1, x + 1, i] = clip(
|
||||||
|
# working_image[y + 1, x + 1, i] + quant_error[i] * (5 / 48),
|
||||||
|
# 0, 1)
|
||||||
|
# if x < 318:
|
||||||
|
# working_image[y + 1, x + 2, i] = clip(
|
||||||
|
# working_image[y + 1, x + 2, i] + quant_error[i] * (3 / 48),
|
||||||
|
# 0, 1)
|
||||||
|
# if y < 198:
|
||||||
|
# if x > 1:
|
||||||
|
# working_image[y + 2, x - 2, i] = clip(
|
||||||
|
# working_image[y + 2, x - 2, i] + quant_error[i] * (1 / 48), 0,
|
||||||
|
# 1)
|
||||||
|
# if x > 0:
|
||||||
|
# working_image[y + 2, x - 1, i] = clip(
|
||||||
|
# working_image[y + 2, x - 1, i] + quant_error[i] * (3 / 48), 0,
|
||||||
|
# 1)
|
||||||
|
# working_image[y + 2, x, i] = clip(
|
||||||
|
# working_image[y + 2, x, i] + quant_error[i] * (5 / 48), 0, 1)
|
||||||
|
# if x < 319:
|
||||||
|
# working_image[y + 2, x + 1, i] = clip(
|
||||||
|
# working_image[y + 2, x + 1, i] + quant_error[i] * (3 / 48),
|
||||||
|
# 0, 1)
|
||||||
|
# if x < 318:
|
||||||
|
# working_image[y + 2, x + 2, i] = clip(
|
||||||
|
# working_image[y + 2, x + 2, i] + quant_error[i] * (1 / 48),
|
||||||
|
# 0, 1)
|
||||||
|
|
||||||
|
return np.array(output_4bit, dtype=np.uint8)
|
56
screen.py
56
screen.py
|
@ -9,6 +9,62 @@ class SHR320Screen:
|
||||||
X_RES = 320
|
X_RES = 320
|
||||||
Y_RES = 200
|
Y_RES = 200
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.palettes = {k: np.zeros((16, 3), dtype=np.uint8) for k in
|
||||||
|
range(16)}
|
||||||
|
# Really 4-bit values, indexing into palette
|
||||||
|
self.pixels = np.array((self.Y_RES, self.X_RES), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Choice of palette per scan-line
|
||||||
|
self.line_palette = np.zeros(self.Y_RES, dtype=np.uint8)
|
||||||
|
|
||||||
|
self.memory = None
|
||||||
|
|
||||||
|
def set_palette(self, idx: int, palette: np.array):
|
||||||
|
if idx < 0 or idx > 15:
|
||||||
|
raise ValueError("Palette index %s must be in range 0 .. 15" % idx)
|
||||||
|
if palette.shape != (16, 3):
|
||||||
|
raise ValueError("Palette size %s != (16, 3)" % palette.shape)
|
||||||
|
# XXX check element range
|
||||||
|
if palette.dtype != np.uint8:
|
||||||
|
raise ValueError("Palette must be of type np.uint8")
|
||||||
|
print(palette)
|
||||||
|
self.palettes[idx] = np.array(palette)
|
||||||
|
|
||||||
|
def set_pixels(self, pixels):
|
||||||
|
self.pixels = np.array(pixels)
|
||||||
|
|
||||||
|
def pack(self):
|
||||||
|
dump = np.zeros(32768, dtype=np.uint8)
|
||||||
|
for y in range(self.Y_RES):
|
||||||
|
pixel_pair = 0
|
||||||
|
for x in range(self.X_RES):
|
||||||
|
if x % 2 == 0:
|
||||||
|
pixel_pair |= (self.pixels[y, x] << 4)
|
||||||
|
else:
|
||||||
|
pixel_pair |= self.pixels[y, x]
|
||||||
|
# print(pixel_pair)
|
||||||
|
dump[y * 160 + (x - 1) // 2] = pixel_pair
|
||||||
|
pixel_pair = 0
|
||||||
|
|
||||||
|
scan_control_offset = 320 * 200 // 2
|
||||||
|
for y in range(self.Y_RES):
|
||||||
|
dump[scan_control_offset + y] = self.line_palette[y]
|
||||||
|
|
||||||
|
palette_offset = scan_control_offset + 256
|
||||||
|
for palette_idx, palette in self.palettes.items():
|
||||||
|
for rgb_idx, rgb in enumerate(palette):
|
||||||
|
r, g, b = rgb
|
||||||
|
# print(r, g, b)
|
||||||
|
rgb_low = (g << 4) | b
|
||||||
|
rgb_hi = r
|
||||||
|
print(hex(rgb_hi), hex(rgb_low))
|
||||||
|
palette_idx_offset = palette_offset + (32 * palette_idx)
|
||||||
|
dump[palette_idx_offset + (2 * rgb_idx)] = rgb_low
|
||||||
|
dump[palette_idx_offset + (2 * rgb_idx + 1)] = rgb_hi
|
||||||
|
|
||||||
|
self.memory = dump
|
||||||
|
|
||||||
|
|
||||||
class DHGRScreen:
|
class DHGRScreen:
|
||||||
X_RES = 560
|
X_RES = 560
|
||||||
|
|
Loading…
Reference in New Issue
Block a user