NTSC conversion should be using YIQ space instead of YUV, which seems
to explain several fudge factors I needed to include to match colours.
This commit is contained in:
parent
df0adec8aa
commit
bf76271d75
17
convert.py
17
convert.py
|
@ -84,14 +84,17 @@ def main():
|
||||||
screen, rgb, dither, args.lookahead, args.verbose, rgb_to_cam16)
|
screen, rgb, dither, args.lookahead, args.verbose, rgb_to_cam16)
|
||||||
|
|
||||||
# Show output image by rendering in target palette
|
# Show output image by rendering in target palette
|
||||||
output_palette = palette_py.PALETTES[args.show_palette or args.palette]()
|
output_palette_name = args.show_palette or args.palette
|
||||||
|
output_palette = palette_py.PALETTES[output_palette_name]()
|
||||||
output_screen = screen_py.DHGRScreen(output_palette)
|
output_screen = screen_py.DHGRScreen(output_palette)
|
||||||
# TODO: if output_palette_name == "ntsc" show bitmap_to_image_ntsc instead
|
if output_palette_name == "ntsc":
|
||||||
output_rgb = output_screen.bitmap_to_image_rgb(bitmap)
|
output_srgb = output_screen.bitmap_to_image_ntsc(bitmap)
|
||||||
out_image = Image.fromarray(image_py.linear_to_srgb(output_rgb).astype(
|
else:
|
||||||
np.uint8))
|
output_srgb = image_py.linear_to_srgb(
|
||||||
out_image = image_py.resize(out_image, screen.X_RES, screen.Y_RES * 2,
|
output_screen.bitmap_to_image_rgb(bitmap).astype(np.uint8))
|
||||||
srgb_output=True)
|
out_image = image_py.resize(
|
||||||
|
Image.fromarray(output_srgb), screen.X_RES, screen.Y_RES * 2,
|
||||||
|
srgb_output=True)
|
||||||
|
|
||||||
if args.show_output:
|
if args.show_output:
|
||||||
out_image.show()
|
out_image.show()
|
||||||
|
|
|
@ -19,8 +19,7 @@ def main():
|
||||||
# pixel, using NTSC emulation.
|
# pixel, using NTSC emulation.
|
||||||
# Double Hi-Res has a timing shift that rotates the displayed bits one
|
# Double Hi-Res has a timing shift that rotates the displayed bits one
|
||||||
# position with respect to NTSC phase.
|
# position with respect to NTSC phase.
|
||||||
# TODO: should be 3? Do I have a compensating off-by-one in bitmap_to_ntsc?
|
ntsc_shift = 1
|
||||||
ntsc_shift = 2
|
|
||||||
for j in range(ntsc_shift, ntsc_shift + 4):
|
for j in range(ntsc_shift, ntsc_shift + 4):
|
||||||
bitmap = np.zeros((1, 11 + ntsc_shift), dtype=bool)
|
bitmap = np.zeros((1, 11 + ntsc_shift), dtype=bool)
|
||||||
for bits in range(256):
|
for bits in range(256):
|
||||||
|
|
1784
palette_ntsc.py
1784
palette_ntsc.py
File diff suppressed because it is too large
Load Diff
51
screen.py
51
screen.py
|
@ -1,5 +1,6 @@
|
||||||
"""Representation of Apple II screen memory."""
|
"""Representation of Apple II screen memory."""
|
||||||
|
|
||||||
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import palette as palette_py
|
import palette as palette_py
|
||||||
|
|
||||||
|
@ -84,51 +85,49 @@ class DHGRScreen:
|
||||||
|
|
||||||
def bitmap_to_image_ntsc(self, bitmap: np.ndarray) -> np.ndarray:
|
def bitmap_to_image_ntsc(self, bitmap: np.ndarray) -> np.ndarray:
|
||||||
y_width = 12
|
y_width = 12
|
||||||
u_width = 24
|
i_width = 24
|
||||||
v_width = 24
|
q_width = 24
|
||||||
|
|
||||||
contrast = 1
|
contrast = 1
|
||||||
# TODO: where does this come from? OpenEmulator looks like it should
|
saturation = 1
|
||||||
# use a value of 1.0 by default.
|
# DHGR has a timing shift of 1/4 phase, i.e x=0 is actually 1/4 phase.
|
||||||
saturation = 2
|
# XXX should use (x + 1) % 4 ?
|
||||||
# Fudge factor to make colours line up with OpenEmulator
|
hue = math.pi / 2
|
||||||
# TODO: where does this come from - is it due to the band-pass
|
|
||||||
# filtering they do?
|
|
||||||
hue = -0.3
|
|
||||||
|
|
||||||
# Apply effect of saturation
|
# Apply effect of saturation
|
||||||
yuv_to_rgb = np.array(
|
yiq_to_rgb = np.array(
|
||||||
((1, 0, 0), (0, saturation, 0), (0, 0, saturation)), dtype=np.float)
|
((1, 0, 0), (0, saturation, 0), (0, 0, saturation)), dtype=np.float)
|
||||||
# Apply hue phase rotation
|
# Apply hue phase rotation
|
||||||
yuv_to_rgb = np.matmul(np.array(
|
yiq_to_rgb = np.matmul(np.array(
|
||||||
((1, 0, 0), (0, np.cos(hue), np.sin(hue)), (0, -np.sin(hue),
|
((1, 0, 0), (0, np.cos(hue), np.sin(hue)), (0, -np.sin(hue),
|
||||||
np.cos(hue)))),
|
np.cos(hue)))),
|
||||||
yuv_to_rgb)
|
yiq_to_rgb)
|
||||||
# Y'UV to R'G'B' conversion
|
# Y'IQ to R'G'B' conversion
|
||||||
yuv_to_rgb = np.matmul(np.array(
|
yiq_to_rgb = np.matmul(np.array(
|
||||||
((1, 0, 1.13983), (1, -0.39465, -.58060), (1, 2.03211, 0))),
|
((1, 0.956, 0.621), (1, -0.272, -.647), (1, -1.107, 1.704))),
|
||||||
yuv_to_rgb)
|
yiq_to_rgb)
|
||||||
|
|
||||||
# Apply effect of contrast
|
# Apply effect of contrast
|
||||||
yuv_to_rgb *= contrast
|
yiq_to_rgb *= contrast
|
||||||
|
|
||||||
out_rgb = np.empty((bitmap.shape[0], bitmap.shape[1] * 3, 3),
|
out_rgb = np.empty((bitmap.shape[0], bitmap.shape[1] * 3, 3),
|
||||||
dtype=np.uint8)
|
dtype=np.uint8)
|
||||||
for y in range(bitmap.shape[0]):
|
for y in range(bitmap.shape[0]):
|
||||||
ysum = 0
|
ysum = 0
|
||||||
usum = 0
|
isum = 0
|
||||||
vsum = 0
|
qsum = 0
|
||||||
line = np.repeat(bitmap[y], 3)
|
line = np.repeat(bitmap[y], 3)
|
||||||
|
|
||||||
for x in range(bitmap.shape[1] * 3):
|
for x in range(bitmap.shape[1] * 3):
|
||||||
ysum += self._read(line, x) - self._read(line, x - y_width)
|
ysum += self._read(line, x) - self._read(line, x - y_width)
|
||||||
usum += self._read(line, x) * self._sin(x) - self._read(
|
isum += self._read(line, x) * self._sin(x) - self._read(
|
||||||
line, x - u_width) * self._sin((x - u_width))
|
line, x - i_width) * self._sin((x - i_width))
|
||||||
vsum += self._read(line, x) * self._cos(x) - self._read(
|
qsum += self._read(line, x) * self._cos(x) - self._read(
|
||||||
line, x - v_width) * self._cos((x - v_width))
|
line, x - q_width) * self._cos((x - q_width))
|
||||||
rgb = np.matmul(
|
rgb = np.matmul(
|
||||||
yuv_to_rgb, np.array(
|
yiq_to_rgb, np.array(
|
||||||
(ysum / y_width, usum / u_width,
|
(ysum / y_width, isum / i_width,
|
||||||
vsum / v_width)).reshape((3, 1))).reshape(3)
|
qsum / q_width)).reshape((3, 1))).reshape(3)
|
||||||
r = min(255, max(0, rgb[0] * 255))
|
r = min(255, max(0, rgb[0] * 255))
|
||||||
g = min(255, max(0, rgb[1] * 255))
|
g = min(255, max(0, rgb[1] * 255))
|
||||||
b = min(255, max(0, rgb[2] * 255))
|
b = min(255, max(0, rgb[2] * 255))
|
||||||
|
|
Loading…
Reference in New Issue