Add some comments and docstrings

This commit is contained in:
kris 2021-01-25 23:16:46 +00:00
parent a7d734f2b0
commit 2bbd65a079
6 changed files with 38 additions and 7 deletions

View File

@ -1,3 +1,5 @@
"""Image converter to Apple II Double Hi-Res format."""
import argparse
import os.path
import time
@ -13,11 +15,8 @@ import screen as screen_py
# TODO:
# - support alternate palettes properly
# - compare to bmp2dhr and a2bestpix
# - support LR/DLR
# - support HGR
# - README
def main():
@ -62,6 +61,7 @@ def main():
screen = screen_py.DHGR140Screen(palette)
lookahead = 0
# Open and resize source image
image = image_py.open(args.input)
resized = np.array(
image_py.resize(image, screen.X_RES, screen.Y_RES)).astype(np.float32)
@ -70,10 +70,10 @@ def main():
dither = dither_pattern.PATTERNS[args.dither]()
start = time.time()
# start = time.time()
output_4bit, output_rgb = dither_pyx.dither_image(
screen, resized, dither, lookahead)
print(time.time() - start)
# print(time.time() - start)
if args.resolution == 140:
# Show un-fringed 140px output image
@ -92,9 +92,9 @@ def main():
if args.show_output:
out_image.show()
# Save Double hi-res image
outfile = os.path.join(os.path.splitext(args.output)[0] + "-preview.png")
out_image.save(outfile, "PNG")
with open(args.output, "wb") as f:
f.write(bytes(screen.main))
f.write(bytes(screen.aux))

View File

@ -1,3 +1,5 @@
"""Error diffusion dither patterns."""
import numpy as np

View File

@ -1,3 +1,5 @@
"""Image transformation functions."""
import numpy as np
from PIL import Image
@ -34,7 +36,6 @@ def resize(image: Image, x_res, y_res, srgb_output: bool = False) -> Image:
# Convert to linear RGB before rescaling so that colour interpolation is
# in linear space
linear = srgb_to_linear(np.asarray(image)).astype(np.uint8)
# linear = np.asarray(image).astype(np.uint8)
res = Image.fromarray(linear).resize((x_res, y_res), Image.LANCZOS)
if srgb_output:
return Image.fromarray(

View File

@ -1,3 +1,5 @@
"""RGB colour palettes to target for Apple II image conversions."""
import numpy as np
import image

View File

@ -1,3 +1,10 @@
"""Precompute CIE2000 perceptual colour distance matrices.
The matrix of delta-E values is computed for all pairs of 24-bit RGB values,
and 4-bit Apple II target palette. This is a 256MB file that is mmapped at
runtime for efficient access.
"""
import argparse
import image
import palette as palette_py

View File

@ -1,3 +1,5 @@
"""Representation of Apple II screen memory."""
import numpy as np
import palette as palette_py
@ -23,9 +25,15 @@ class Screen:
return 1024 * c + 128 * b + 40 * a
def _image_to_bitmap(self, image: np.ndarray) -> np.ndarray:
"""Converts 4-bit image to 2-bit image bitmap.
Each 4-bit colour value maps to a sliding window of 4 successive pixels,
via x%4.
"""
raise NotImplementedError
def pack(self, image: np.ndarray):
"""Packs an image into memory format (8k AUX + 8K MAIN)."""
bitmap = self._image_to_bitmap(image)
# The DHGR display encodes 7 pixels across interleaved 4-byte sequences
# of AUX and MAIN memory, as follows:
@ -53,6 +61,12 @@ class Screen:
return bitmap
def bitmap_to_image_rgb(self, bitmap: np.ndarray) -> np.ndarray:
"""Convert our 2-bit bitmap image into a RGB image.
Colour at every pixel is determined by the value of a 4-bit sliding
window indexed by x % 4, which gives the index into our 16-colour RGB
palette.
"""
image_rgb = np.empty((192, 560, 3), dtype=np.uint8)
for y in range(self.Y_RES):
pixel = [False, False, False, False]
@ -63,6 +77,7 @@ class Screen:
return image_rgb
def pixel_palette_options(self, last_pixel_4bit, x: int):
"""Returns available colours for given x pos and 4-bit colour of x-1"""
raise NotImplementedError
@ -85,6 +100,7 @@ class DHGR140Screen(Screen):
return bitmap
def pixel_palette_options(self, last_pixel_4bit, x: int):
# All 16 colour choices are available at every x position.
return (
np.array(list(self.palette.RGB.keys()), dtype=np.uint8),
np.array(list(self.palette.RGB.values()), dtype=np.uint8))
@ -107,6 +123,9 @@ class DHGR560Screen(Screen):
return bitmap
def pixel_palette_options(self, last_pixel_4bit, x: int):
# The two available colours for position x are given by the 4-bit
# value of position x-1, and the 4-bit value produced by toggling the
# value of the x % 4 bit (the current value of NTSC phase)
last_dots = self.palette.DOTS[last_pixel_4bit]
other_dots = list(last_dots)
other_dots[x % 4] = not other_dots[x % 4]