mirror of
https://github.com/KrisKennaway/ii-pix.git
synced 2025-04-28 12:37:02 +00:00
Get rid of support for 140px mode, it was only useful as a demo of why
other converters have the wrong basic approach.
This commit is contained in:
parent
d442baf1f1
commit
8cfee55b1d
33
convert.py
33
convert.py
@ -40,16 +40,6 @@ def main():
|
||||
parser.add_argument(
|
||||
'--show-output', action=argparse.BooleanOptionalAction, default=True,
|
||||
help="Whether to show the output image after conversion.")
|
||||
parser.add_argument(
|
||||
'--resolution', type=str, choices=("140", "560"), default="560",
|
||||
help=("Effective double hi-res resolution to target. '140' treats "
|
||||
"pixels in groups of 4, with 16 colours that are chosen "
|
||||
"independently, and ignores NTSC fringing. This is mostly only "
|
||||
"useful for comparison to other 140px converters. '560' treats "
|
||||
"each pixel individually, with choice of 2 colours (depending on "
|
||||
"NTSC colour phase), and looking ahead over next --lookahead "
|
||||
"pixels to optimize the colour sequence (default: 560)")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--palette', type=str, choices=list(set(palette_py.PALETTES.keys())),
|
||||
default=palette_py.DEFAULT_PALETTE,
|
||||
@ -72,19 +62,12 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
palette = palette_py.PALETTES[args.palette]()
|
||||
if args.resolution == "140":
|
||||
if args.palette == "ntsc":
|
||||
raise argparse.ArgumentError(
|
||||
"--resolution=140 cannot be combined with --palette=ntsc")
|
||||
screen = screen_py.DHGR140Screen(palette)
|
||||
lookahead = 0
|
||||
if args.palette == "ntsc":
|
||||
# TODO: palette depth should be controlled by Palette not Screen
|
||||
screen = screen_py.DHGR560NTSCScreen(palette)
|
||||
else:
|
||||
if args.palette == "ntsc":
|
||||
# TODO: palette depth should be controlled by Palette not Screen
|
||||
screen = screen_py.DHGR560NTSCScreen(palette)
|
||||
else:
|
||||
screen = screen_py.DHGR560Screen(palette)
|
||||
lookahead = args.lookahead
|
||||
screen = screen_py.DHGR560Screen(palette)
|
||||
lookahead = args.lookahead
|
||||
|
||||
# Conversion matrix from RGB to CAM16UCS colour values. Indexed by
|
||||
# 24-bit RGB value
|
||||
@ -93,7 +76,7 @@ def main():
|
||||
# Open and resize source image
|
||||
image = image_py.open(args.input)
|
||||
if args.show_input:
|
||||
image_py.resize(image, screen.NATIVE_X_RES, screen.NATIVE_Y_RES * 2,
|
||||
image_py.resize(image, screen.X_RES, screen.Y_RES * 2,
|
||||
srgb_output=True).show()
|
||||
rgb = np.array(
|
||||
image_py.resize(image, screen.X_RES, screen.Y_RES,
|
||||
@ -114,8 +97,8 @@ def main():
|
||||
output_rgb = output_screen.bitmap_to_image_rgb(bitmap)
|
||||
out_image = Image.fromarray(image_py.linear_to_srgb(output_rgb).astype(
|
||||
np.uint8))
|
||||
out_image = image_py.resize(out_image, screen.NATIVE_X_RES,
|
||||
screen.NATIVE_Y_RES * 2, srgb_output=True)
|
||||
out_image = image_py.resize(out_image, screen.X_RES, screen.Y_RES * 2,
|
||||
srgb_output=True)
|
||||
|
||||
if args.show_output:
|
||||
out_image.show()
|
||||
|
103
dither.pyx
103
dither.pyx
@ -89,7 +89,7 @@ cdef inline unsigned char shift_pixel_window(
|
||||
cdef int dither_lookahead(Dither* dither, float[:, :, ::1] palette_cam16, float[:, :, ::1] palette_rgb,
|
||||
float[:, :, ::1] image_rgb, int x, int y, int lookahead, unsigned char last_pixels,
|
||||
int x_res, float[:,::1] rgb_to_cam16ucs, unsigned char palette_depth) nogil:
|
||||
cdef int i, j, k
|
||||
cdef int candidate_pixels, i, j
|
||||
cdef float[3] quant_error
|
||||
cdef int best
|
||||
cdef float best_error = 2**31-1
|
||||
@ -110,41 +110,42 @@ cdef int dither_lookahead(Dither* dither, float[:, :, ::1] palette_cam16, float[
|
||||
# given pixel (dependent on the state already chosen for pixels to the left), we need to look beyond local minima.
|
||||
# i.e. it might be better to make a sub-optimal choice for this pixel if it allows access to much better pixel
|
||||
# colours at later positions.
|
||||
for i in range(1 << lookahead):
|
||||
for candidate_pixels in range(1 << lookahead):
|
||||
# Working copy of input pixels
|
||||
for j in range(xxr - x):
|
||||
for k in range(3):
|
||||
lah_image_rgb[j * lah_shape2 + k] = image_rgb[y, x+j, k]
|
||||
for i in range(xxr - x):
|
||||
for j in range(3):
|
||||
lah_image_rgb[i * lah_shape2 + j] = image_rgb[y, x+i, j]
|
||||
|
||||
total_error = 0
|
||||
for j in range(xxr - x):
|
||||
xl = dither_bounds_xl(dither, j)
|
||||
xr = dither_bounds_xr(dither, xxr - x, j)
|
||||
phase = (x + j) % 4
|
||||
# Apply dithering to lookahead horizon or edge of screen
|
||||
for i in range(xxr - x):
|
||||
xl = dither_bounds_xl(dither, i)
|
||||
xr = dither_bounds_xr(dither, xxr - x, i)
|
||||
phase = (x + i) % 4
|
||||
|
||||
next_pixels = shift_pixel_window(
|
||||
last_pixels, next_pixels=i, shift_right_by=j+1, window_width=palette_depth)
|
||||
last_pixels, next_pixels=candidate_pixels, shift_right_by=i+1, window_width=palette_depth)
|
||||
|
||||
# We don't update the input at position x (since we've already chosen
|
||||
# fixed outputs), but we do propagate quantization errors to positions >x
|
||||
# so we can compensate for how good/bad these choices were. i.e. the
|
||||
# options_rgb choices are fixed, but we can still distribute quantization error
|
||||
# from having made these choices, in order to compute the total error.
|
||||
for k in range(3):
|
||||
quant_error[k] = lah_image_rgb[j * lah_shape2 + k] - palette_rgb[next_pixels, phase, k]
|
||||
apply_one_line(dither, xl, xr, j, lah_image_rgb, lah_shape2, quant_error)
|
||||
# We don't update the input at position x (since we've already chosen fixed outputs), but we do propagate
|
||||
# quantization errors to positions >x so we can compensate for how good/bad these choices were. i.e. the
|
||||
# next_pixels choices are fixed, but we can still distribute quantization error from having made these
|
||||
# choices, in order to compute the total error.
|
||||
for j in range(3):
|
||||
quant_error[j] = lah_image_rgb[i * lah_shape2 + j] - palette_rgb[next_pixels, phase, j]
|
||||
apply_one_line(dither, xl, xr, i, lah_image_rgb, lah_shape2, quant_error)
|
||||
|
||||
lah_cam16ucs = convert_rgb_to_cam16ucs(
|
||||
rgb_to_cam16ucs, lah_image_rgb[j*lah_shape2], lah_image_rgb[j*lah_shape2+1],
|
||||
lah_image_rgb[j*lah_shape2+2])
|
||||
rgb_to_cam16ucs, lah_image_rgb[i*lah_shape2], lah_image_rgb[i*lah_shape2+1],
|
||||
lah_image_rgb[i*lah_shape2+2])
|
||||
total_error += colour_distance_squared(lah_cam16ucs, palette_cam16[next_pixels, phase])
|
||||
|
||||
if total_error >= best_error:
|
||||
# No need to continue
|
||||
break
|
||||
|
||||
if total_error < best_error:
|
||||
best_error = total_error
|
||||
best = i
|
||||
best = candidate_pixels
|
||||
|
||||
free(lah_image_rgb)
|
||||
return best
|
||||
@ -215,36 +216,6 @@ cdef void apply(Dither* dither, int x_res, int y_res, int x, int y, float[:,:,::
|
||||
for k in range(3):
|
||||
image[i,j,k] = clip(image[i,j,k] + error_fraction * quant_error[k], 0, 1)
|
||||
|
||||
# Compute closest colour from array of candidate n-bit colour palette values.
|
||||
#
|
||||
# Args:
|
||||
# pixel_rgb: source RGB colour value to be matched
|
||||
# options_nbit: array of candidate n-bit colour palette values
|
||||
# distances: matrix of (24-bit RGB value, n-bit colour value) perceptual colour differences
|
||||
#
|
||||
# Returns:
|
||||
# index of options_nbit entry having lowest distance value
|
||||
#
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cdef unsigned char find_nearest_colour(float[::1] pixel_rgb, unsigned char[::1] options_nbit,
|
||||
unsigned char[:, ::1] distances):
|
||||
|
||||
cdef int best, dist
|
||||
cdef unsigned char bit4
|
||||
cdef int best_dist = 2**8
|
||||
cdef long flat
|
||||
|
||||
for i in range(options_nbit.shape[0]):
|
||||
flat = (<long>pixel_rgb[0] << 16) + (<long>pixel_rgb[1] << 8) + <long>pixel_rgb[2]
|
||||
bit4 = options_nbit[i]
|
||||
dist = distances[flat, bit4]
|
||||
if dist < best_dist:
|
||||
best_dist = dist
|
||||
best = i
|
||||
|
||||
return options_nbit[best]
|
||||
|
||||
|
||||
# Dither a source image
|
||||
#
|
||||
@ -259,7 +230,8 @@ cdef unsigned char find_nearest_colour(float[::1] pixel_rgb, unsigned char[::1]
|
||||
#
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
def dither_image(screen, float[:, :, ::1] image_rgb, dither, int lookahead, unsigned char verbose, float[:,::1] rgb_to_cam16ucs):
|
||||
def dither_image(
|
||||
screen, float[:, :, ::1] image_rgb, dither, int lookahead, unsigned char verbose, float[:,::1] rgb_to_cam16ucs):
|
||||
cdef int y, x, i, j, k
|
||||
# cdef float[3] input_pixel_rgb
|
||||
cdef float[3] quant_error
|
||||
@ -307,23 +279,16 @@ def dither_image(screen, float[:, :, ::1] image_rgb, dither, int lookahead, unsi
|
||||
print("%d/%d" % (y, yres))
|
||||
output_pixel_nbit = 0
|
||||
for x in range(xres):
|
||||
#for i in range(3):
|
||||
# input_pixel_rgb[i] = image_rgb[y,x,i]
|
||||
if lookahead:
|
||||
# Compute all possible 2**N choices of n-bit pixel colours for positions x .. x + lookahead
|
||||
# lookahead_palette_choices_nbit = lookahead_options(lookahead, output_pixel_nbit)
|
||||
# Apply error diffusion for each of these 2**N choices, and compute which produces the closest match
|
||||
# to the source image over the succeeding N pixels
|
||||
best_next_pixels = dither_lookahead(
|
||||
&cdither, palette_cam16, palette_rgb, image_rgb, x, y, lookahead, output_pixel_nbit, xres,
|
||||
rgb_to_cam16ucs, palette_depth)
|
||||
# Apply best choice for next 1 pixel
|
||||
output_pixel_nbit = shift_pixel_window(
|
||||
output_pixel_nbit, best_next_pixels, shift_right_by=1, window_width=palette_depth)
|
||||
#else:
|
||||
# # Choose the closest colour among the available n-bit palette options
|
||||
# palette_choices_nbit = screen.pixel_palette_options(output_pixel_nbit, x)
|
||||
# output_pixel_nbit = find_nearest_colour(input_pixel_rgb, palette_choices_nbit, distances)
|
||||
# Compute all possible 2**N choices of n-bit pixel colours for positions x .. x + lookahead
|
||||
# lookahead_palette_choices_nbit = lookahead_options(lookahead, output_pixel_nbit)
|
||||
# Apply error diffusion for each of these 2**N choices, and compute which produces the closest match
|
||||
# to the source image over the succeeding N pixels
|
||||
best_next_pixels = dither_lookahead(
|
||||
&cdither, palette_cam16, palette_rgb, image_rgb, x, y, lookahead, output_pixel_nbit, xres,
|
||||
rgb_to_cam16ucs, palette_depth)
|
||||
# Apply best choice for next 1 pixel
|
||||
output_pixel_nbit = shift_pixel_window(
|
||||
output_pixel_nbit, best_next_pixels, shift_right_by=1, window_width=palette_depth)
|
||||
|
||||
# Apply error diffusion from chosen output pixel value
|
||||
for i in range(3):
|
||||
|
206
screen.py
206
screen.py
@ -4,13 +4,9 @@ import numpy as np
|
||||
import palette as palette_py
|
||||
|
||||
|
||||
class Screen:
|
||||
X_RES = None
|
||||
Y_RES = None
|
||||
X_PIXEL_WIDTH = None
|
||||
|
||||
NATIVE_X_RES = 560
|
||||
NATIVE_Y_RES = 192
|
||||
class DHGRScreen:
|
||||
X_RES = 560
|
||||
Y_RES = 192
|
||||
|
||||
def __init__(self, palette: palette_py.Palette):
|
||||
self.main = np.zeros(8192, dtype=np.uint8)
|
||||
@ -42,9 +38,9 @@ class Screen:
|
||||
# PBBBAAAA PDDCCCCB PFEEEEDD PGGGGFFF
|
||||
# Aux N Main N Aux N+1 Main N+1 (N even)
|
||||
main_col = np.zeros(
|
||||
(self.Y_RES, self.X_RES * self.X_PIXEL_WIDTH // 14), dtype=np.uint8)
|
||||
(self.Y_RES, self.X_RES // 14), dtype=np.uint8)
|
||||
aux_col = np.zeros(
|
||||
(self.Y_RES, self.X_RES * self.X_PIXEL_WIDTH // 14), dtype=np.uint8)
|
||||
(self.Y_RES, self.X_RES // 14), dtype=np.uint8)
|
||||
for byte_offset in range(80):
|
||||
column = np.zeros(self.Y_RES, dtype=np.uint8)
|
||||
for bit in range(7):
|
||||
@ -69,19 +65,62 @@ class Screen:
|
||||
window indexed by x % 4, which gives the index into our 16-colour RGB
|
||||
palette.
|
||||
"""
|
||||
image_rgb = np.empty((self.NATIVE_Y_RES, self.NATIVE_X_RES, 3),
|
||||
image_rgb = np.empty((self.Y_RES, self.X_RES, 3),
|
||||
dtype=np.uint8)
|
||||
for y in range(self.Y_RES):
|
||||
pixel = [False, False, False, False]
|
||||
for x in range(self.NATIVE_X_RES):
|
||||
for x in range(self.X_RES):
|
||||
pixel[x % 4] = bitmap[y, x]
|
||||
dots = self.palette.DOTS_TO_INDEX[tuple(pixel)]
|
||||
image_rgb[y, x, :] = self.palette.RGB[dots]
|
||||
return image_rgb
|
||||
|
||||
def pixel_palette_options(self, last_pixel_nbit, x: int):
|
||||
"""Returns available colours for given x pos and n-bit colour of x-1"""
|
||||
raise NotImplementedError
|
||||
|
||||
class DHGR560Screen(DHGRScreen):
|
||||
"""DHGR screen including colour fringing and 4 pixel chroma bleed."""
|
||||
|
||||
def _image_to_bitmap(self, image_nbit: np.ndarray) -> np.ndarray:
|
||||
bitmap = np.zeros((self.Y_RES, self.X_RES), dtype=np.bool)
|
||||
for y in range(self.Y_RES):
|
||||
for x in range(self.X_RES):
|
||||
pixel = image_nbit[y, x]
|
||||
dots = self.palette.DOTS[pixel]
|
||||
phase = x % 4
|
||||
bitmap[y, x] = dots[phase]
|
||||
return bitmap
|
||||
|
||||
|
||||
# TODO: refactor to share implementation with DHGR560Screen
|
||||
class DHGR560NTSCScreen(DHGRScreen):
|
||||
"""DHGR screen including colour fringing and 8 pixel chroma bleed."""
|
||||
|
||||
# XXX image_nbit is MSB (x, ... x-7) LSB pixel values?
|
||||
|
||||
def _image_to_bitmap(self, image_nbit: np.ndarray) -> np.ndarray:
|
||||
bitmap = np.zeros((self.Y_RES, self.X_RES), dtype=np.bool)
|
||||
for y in range(self.Y_RES):
|
||||
for x in range(self.X_RES):
|
||||
pixel = image_nbit[y, x]
|
||||
bitmap[y, x] = pixel >> 7
|
||||
return bitmap
|
||||
|
||||
# TODO: unify with parent
|
||||
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 8-bit sliding
|
||||
window indexed by x % 4, which gives the index into our 256-colour RGB
|
||||
palette.
|
||||
"""
|
||||
image_rgb = np.empty((self.Y_RES, self.X_RES, 3),
|
||||
dtype=np.uint8)
|
||||
for y in range(self.Y_RES):
|
||||
pixel = [False, False, False, False, False, False, False, False]
|
||||
for x in range(self.X_RES):
|
||||
pixel = pixel[1:] + [bitmap[y, x]]
|
||||
dots = self.palette.DOTS_TO_INDEX[tuple(pixel)]
|
||||
image_rgb[y, x, :] = self.palette.RGB[dots, x % 4]
|
||||
return image_rgb
|
||||
|
||||
@staticmethod
|
||||
def _sin(pos, phase0=0):
|
||||
@ -99,145 +138,6 @@ class Screen:
|
||||
|
||||
return 1 if line[pos] else 0
|
||||
|
||||
|
||||
class DHGR140Screen(Screen):
|
||||
"""DHGR screen ignoring colour fringing, i.e. treating as 140x192x16."""
|
||||
|
||||
X_RES = 140
|
||||
Y_RES = 192
|
||||
X_PIXEL_WIDTH = 4
|
||||
|
||||
def _image_to_bitmap(self, image_nbit: np.ndarray) -> np.ndarray:
|
||||
bitmap = np.zeros(
|
||||
(self.Y_RES, self.X_RES * self.X_PIXEL_WIDTH), dtype=np.bool)
|
||||
for y in range(self.Y_RES):
|
||||
for x in range(self.X_RES):
|
||||
pixel = image_nbit[y, x]
|
||||
dots = self.palette.DOTS[pixel]
|
||||
bitmap[y, x * self.X_PIXEL_WIDTH:(
|
||||
(x + 1) * self.X_PIXEL_WIDTH)] = dots
|
||||
return bitmap
|
||||
|
||||
def pixel_palette_options(self, last_pixel_nbit, x: int):
|
||||
# All 16 colour choices are available at every x position.
|
||||
return np.array(list(self.palette.RGB.keys()), dtype=np.uint8)
|
||||
|
||||
|
||||
class DHGR560Screen(Screen):
|
||||
"""DHGR screen including colour fringing and 4 pixel chroma bleed."""
|
||||
X_RES = 560
|
||||
Y_RES = 192
|
||||
X_PIXEL_WIDTH = 1
|
||||
|
||||
def _image_to_bitmap(self, image_nbit: np.ndarray) -> np.ndarray:
|
||||
bitmap = np.zeros((self.Y_RES, self.X_RES), dtype=np.bool)
|
||||
for y in range(self.Y_RES):
|
||||
for x in range(self.X_RES):
|
||||
pixel = image_nbit[y, x]
|
||||
dots = self.palette.DOTS[pixel]
|
||||
phase = x % 4
|
||||
bitmap[y, x] = dots[phase]
|
||||
return bitmap
|
||||
|
||||
def pixel_palette_options(self, last_pixel_nbit, x: int):
|
||||
last_dots = self.palette.DOTS[last_pixel_nbit][1:] + [None]
|
||||
|
||||
# rearrange into palette order
|
||||
next_dots = [None] * 8
|
||||
for i in range(4):
|
||||
next_dots[(i - x) % 4] = last_dots[i]
|
||||
next_dots[(i - x) % 4 + 4] = last_dots[i + 4]
|
||||
|
||||
# XXX wrong
|
||||
|
||||
assert next_dots[(3 - x) % 4 + 4] is None
|
||||
# print(x, last_dots, next_dots)
|
||||
|
||||
next_dots[(3 - x) % 4 + 4] = False
|
||||
next_pixel_nbit_0 = self.palette.DOTS_TO_INDEX[next_dots]
|
||||
|
||||
next_dots[(3 - x) % 4 + 4] = True
|
||||
next_pixel_nbit_1 = self.palette.DOTS_TO_INDEX[next_dots]
|
||||
return np.array([next_pixel_nbit_0, next_pixel_nbit_1], dtype=np.uint8)
|
||||
|
||||
|
||||
# TODO: refactor to share implementation with DHGR560Screen
|
||||
class DHGR560NTSCScreen(Screen):
|
||||
"""DHGR screen including colour fringing and 8 pixel chroma bleed."""
|
||||
X_RES = 560
|
||||
Y_RES = 192
|
||||
X_PIXEL_WIDTH = 1
|
||||
|
||||
def _image_to_bitmap(self, image_nbit: np.ndarray) -> np.ndarray:
|
||||
bitmap = np.zeros((self.Y_RES, self.X_RES), dtype=np.bool)
|
||||
for y in range(self.Y_RES):
|
||||
for x in range(self.X_RES):
|
||||
pixel = image_nbit[y, x]
|
||||
#dots = self.palette.DOTS[pixel]
|
||||
#phase = x % 4
|
||||
bitmap[y, x] = pixel >> 7 # dots[4 + phase]
|
||||
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 8-bit sliding
|
||||
window indexed by x % 4, which gives the index into our 256-colour RGB
|
||||
palette.
|
||||
"""
|
||||
image_rgb = np.empty((self.NATIVE_Y_RES, self.NATIVE_X_RES, 3),
|
||||
dtype=np.uint8)
|
||||
for y in range(self.Y_RES):
|
||||
pixel = [False, False, False, False, False, False, False, False]
|
||||
for x in range(self.NATIVE_X_RES):
|
||||
# pixel[x % 4] = pixel[x % 4 + 4]
|
||||
# pixel[x % 4 + 4] = bitmap[y, x]
|
||||
pixel = pixel[1:] + [bitmap[y, x]]
|
||||
dots = self.palette.DOTS_TO_INDEX[tuple(pixel)]
|
||||
image_rgb[y, x, :] = self.palette.RGB[dots, x % 4]
|
||||
return image_rgb
|
||||
|
||||
def pixel_palette_options(self, last_pixel_nbit):
|
||||
# # The two available 8-bit pixel colour choices are given by:
|
||||
# # - Rotating the pixel value from the current x % 4 + 4 position to
|
||||
# # x % 4
|
||||
# # - choosing 0 and 1 for the new values of x % 4 + 4
|
||||
# next_dots0 = list(self.palette.DOTS[last_pixel_nbit])
|
||||
# next_dots1 = list(next_dots0)
|
||||
# next_dots0[x % 4] = next_dots0[x % 4 + 4]
|
||||
# next_dots0[x % 4 + 4] = False
|
||||
# next_dots1[x % 4] = next_dots1[x % 4 + 4]
|
||||
# next_dots1[x % 4 + 4] = True
|
||||
# pixel_nbit_0 = self.palette.DOTS_TO_INDEX[tuple(next_dots0)]
|
||||
# pixel_nbit_1 = self.palette.DOTS_TO_INDEX[tuple(next_dots1)]
|
||||
# return np.array([pixel_nbit_0, pixel_nbit_1], dtype=np.uint8)
|
||||
#next_dots = last_dots[1:] # list(self.palette.DOTS[
|
||||
# last_pixel_nbit][1:])
|
||||
#return np.array([
|
||||
# self.palette.DOTS_TO_INDEX[tuple(last_dots + [False])],
|
||||
# self.palette.DOTS_TO_INDEX[tuple(next_dots + [True])]],
|
||||
# dtype=np.uint8)
|
||||
|
||||
return np.array(last_pixel_nbit >> 1, (last_pixel_nbit >> 1) + 1,
|
||||
dtype=np.uint8)
|
||||
|
||||
# # rearrange into palette order
|
||||
# next_dots = [None] * 8
|
||||
# for i in range(4):
|
||||
# next_dots[i] = last_dots[(i - x) % 4]
|
||||
# next_dots[i + 4] = last_dots[(i - x) % 4 + 4]
|
||||
#
|
||||
# assert next_dots[(3 + x) % 4 + 4] is None
|
||||
# # print(x, last_dots, next_dots)
|
||||
#
|
||||
# next_dots[(3 + x) % 4 + 4] = False
|
||||
# next_pixel_nbit_0 = self.palette.DOTS_TO_INDEX[tuple(next_dots)]
|
||||
#
|
||||
# next_dots[(3 + x) % 4 + 4] = True
|
||||
# next_pixel_nbit_1 = self.palette.DOTS_TO_INDEX[tuple(next_dots)]
|
||||
# return np.array([next_pixel_nbit_0, next_pixel_nbit_1],
|
||||
# dtype=np.uint8)
|
||||
|
||||
def bitmap_to_ntsc(self, bitmap: np.ndarray) -> np.ndarray:
|
||||
y_width = 12
|
||||
u_width = 24
|
||||
|
Loading…
x
Reference in New Issue
Block a user