Re-enable 140px mode

This commit is contained in:
kris 2021-01-15 22:58:01 +00:00
parent 8d671f08ac
commit a061795022
3 changed files with 55 additions and 21 deletions

View File

@ -13,7 +13,7 @@ import screen as screen_py
# TODO:
# - support 140px mode again
# - include fringing in 140px output
# - compare to bmp2dhr and a2bestpix
# - support LR/DLR
# - support HGR
@ -38,11 +38,20 @@ 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=int, choices=(560, 140), default=560,
help=("Double hi-res resolution to target. 140 treats pixels as "
"groups of 4 and ignores NTSC fringing.")
)
args = parser.parse_args()
palette = palette_py.Palette()
# screen = DHGR140Screen()
screen = screen_py.DHGR560Screen(palette)
if args.resolution == 560:
screen = screen_py.DHGR560Screen(palette)
lookahead = args.lookahead
else:
screen = screen_py.DHGR140Screen(palette)
lookahead = 0
image = image_py.open(screen.X_RES, screen.Y_RES, args.input)
if args.show_input:
@ -52,7 +61,7 @@ def main():
start = time.time()
output_4bit, output_rgb = dither_pyx.dither_image(
screen, image, dither, lookahead=args.lookahead)
screen, image, dither, lookahead)
print(time.time() - start)
screen.pack(output_4bit)

View File

@ -1,6 +1,7 @@
# cython: infer_types=True
cimport cython
import functools
import numpy as np
# from cython.parallel import prange
from cython.view cimport array as cvarray
@ -81,7 +82,7 @@ cdef int dither_bounds_yb(float [:, :, ::1] pattern, int y_origin, int y_res, in
@cython.boundscheck(False)
@cython.wraparound(False)
def dither_lookahead(
screen, float[:,:,::1] image_rgb, dither, int x, int y, char[:, ::1] options_4bit,
screen, float[:,:,::1] image_rgb, dither, int x, int y, unsigned char[:, ::1] options_4bit,
float[:, :, ::1] options_rgb, int lookahead):
cdef float[:, :, ::1] pattern = dither.PATTERN
cdef int x_res = screen.X_RES
@ -126,10 +127,11 @@ def dither_lookahead(
quant_error[k] = lah_image_rgb[j * lah_shape1 * lah_shape2 + i * lah_shape2 + k] - options_rgb[j, i, k]
apply_one_line(pattern, xl, xr, &lah_image_rgb[j * lah_shape1 * lah_shape2], lah_shape2, quant_error)
cdef unsigned char bit4
cdef int best
cdef int best_error = 2**31-1
cdef int total_error
cdef long flat, dist, bit4
cdef long flat, dist
cdef long r, g, b
cdef (unsigned char)[:, ::1] distances = screen.palette.distances
@ -153,8 +155,6 @@ def dither_lookahead(
free(lah_image_rgb)
return options_4bit[best, 0], options_rgb[best, 0, :]
import functools
@functools.lru_cache(None)
def lookahead_options(screen, lookahead, last_pixel_4bit, x):
@ -175,6 +175,27 @@ def lookahead_options(screen, lookahead, last_pixel_4bit, x):
return options_4bit, options_rgb
@cython.boundscheck(False)
@cython.wraparound(False)
def find_nearest_colour(screen, float[::1] pixel_rgb, unsigned char[::1] options_4bit, unsigned char[:, ::1] options_rgb):
cdef int best, dist
cdef unsigned char bit4
cdef int best_dist = 2**8
cdef long flat
cdef (unsigned char)[:, ::1] distances = screen.palette.distances
for i in range(options_4bit.shape[0]):
flat = (<long>pixel_rgb[0] << 16) + (<long>pixel_rgb[1] << 8) + <long>pixel_rgb[2]
bit4 = options_4bit[i]
dist = distances[flat, bit4]
if dist < best_dist:
best_dist = dist
best = i
return options_4bit[best], options_rgb[best, :]
@cython.boundscheck(False)
@cython.wraparound(False)
def dither_image(
@ -197,13 +218,17 @@ def dither_image(
output_pixel_4bit = 0
for x in range(xres):
input_pixel_rgb = image_rgb[y, x, :]
options_4bit, options_rgb = lookahead_options(
screen, lookahead, output_pixel_4bit, x % 4)
output_pixel_4bit, output_pixel_rgb = \
dither_lookahead(
screen, image_rgb, dither, x, y, options_4bit,
options_rgb, lookahead)
if lookahead:
palette_choices_4bit, palette_choices_rgb = lookahead_options(
screen, lookahead, output_pixel_4bit, x % 4)
output_pixel_4bit, output_pixel_rgb = \
dither_lookahead(
screen, image_rgb, dither, x, y, palette_choices_4bit,
palette_choices_rgb, lookahead)
else:
palette_choices_4bit, palette_choices_rgb = screen.pixel_palette_options(output_pixel_4bit, x)
output_pixel_4bit, output_pixel_rgb = \
find_nearest_colour(screen, input_pixel_rgb, palette_choices_4bit, palette_choices_rgb)
for i in range(3):
quant_error[i] = input_pixel_rgb[i] - output_pixel_rgb[i]
image_rgb[y, x, i] = output_pixel_rgb[i]

View File

@ -66,15 +66,16 @@ class DHGR140Screen(Screen):
(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_4bit[y, x].item()
pixel = image_4bit[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_4bit, x: int):
return np.array(list(self.palette.RGB.keys())), np.array(list(
self.palette.RGB.values()))
return (
np.array(list(self.palette.RGB.keys()), dtype=np.uint8),
np.array(list(self.palette.RGB.values()), dtype=np.uint8))
class DHGR560Screen(Screen):
@ -100,7 +101,6 @@ class DHGR560Screen(Screen):
other_dots = tuple(other_dots)
other_pixel_4bit = self.palette.DOTS_TO_4BIT[other_dots]
return (
np.array([last_pixel_4bit, other_pixel_4bit]),
np.array([last_pixel_4bit, other_pixel_4bit], dtype=np.uint8),
np.array([self.palette.RGB[last_pixel_4bit],
self.palette.RGB[other_pixel_4bit]]))
self.palette.RGB[other_pixel_4bit]], dtype=np.uint8))