cythonize dither_image

This commit is contained in:
kris 2021-01-12 10:00:56 +00:00
parent 3e7cb37253
commit 32d54d5b78
2 changed files with 73 additions and 30 deletions

View File

@ -3,6 +3,7 @@ import bz2
import functools import functools
import os.path import os.path
import pickle import pickle
import time
from typing import Tuple from typing import Tuple
from PIL import Image from PIL import Image
@ -282,8 +283,8 @@ class Dither:
def apply(self, screen: Screen, image: np.ndarray, x: int, y: int, def apply(self, screen: Screen, image: np.ndarray, x: int, y: int,
quant_error: np.ndarray, one_line=False): quant_error: np.ndarray, one_line=False):
#el, er, xl, xr = self.x_dither_bounds(screen, x) # el, er, xl, xr = self.x_dither_bounds(screen, x)
#et, eb, yt, yb = self.y_dither_bounds(screen, y, one_line) # et, eb, yt, yb = self.y_dither_bounds(screen, y, one_line)
return dither_apply.apply(self, screen, x, y, image, quant_error) return dither_apply.apply(self, screen, x, y, image, quant_error)
# error = self.PATTERN * quant_error.reshape((1, 1, 3)) # error = self.PATTERN * quant_error.reshape((1, 1, 3))
# #
@ -361,26 +362,6 @@ def open_image(screen: Screen, filename: str) -> np.ndarray:
SRGBResize(im, (screen.X_RES, screen.Y_RES), Image.LANCZOS)) SRGBResize(im, (screen.X_RES, screen.Y_RES), Image.LANCZOS))
@functools.lru_cache(None)
def lookahead_options(screen, lookahead, last_pixel_4bit, x):
options_4bit = np.empty((2 ** lookahead, lookahead), dtype=np.uint8)
options_rgb = np.empty((2 ** lookahead, lookahead, 3), dtype=np.float32)
for i in range(2 ** lookahead):
output_pixel_4bit = last_pixel_4bit
for j in range(lookahead):
xx = x + j
palette_choices_4bit, palette_choices_rgb = \
screen.pixel_palette_options(output_pixel_4bit, xx)
output_pixel_4bit = palette_choices_4bit[(i & (1 << j)) >> j]
output_pixel_rgb = np.array(
palette_choices_rgb[(i & (1 << j)) >> j])
# XXX copy
options_4bit[i, j] = output_pixel_4bit
options_rgb[i, j, :] = np.copy(output_pixel_rgb)
return options_4bit, options_rgb
def dither_lookahead( def dither_lookahead(
screen: Screen, image_rgb: np.ndarray, dither: Dither, differ: screen: Screen, image_rgb: np.ndarray, dither: Dither, differ:
ColourDistance, x, y, last_pixel_4bit, lookahead ColourDistance, x, y, last_pixel_4bit, lookahead
@ -439,7 +420,7 @@ def dither_image(
print(y) print(y)
output_pixel_4bit = np.uint8(0) output_pixel_4bit = np.uint8(0)
for x in range(screen.X_RES): for x in range(screen.X_RES):
input_pixel_rgb = np.copy(image_rgb[y, x, :]) input_pixel_rgb = image_rgb[y, x, :]
options_4bit, options_rgb = lookahead_options( options_4bit, options_rgb = lookahead_options(
screen, lookahead, output_pixel_4bit, x % 4) screen, lookahead, output_pixel_4bit, x % 4)
@ -448,9 +429,9 @@ def dither_image(
screen, image_rgb, dither, differ, x, y, options_4bit, screen, image_rgb, dither, differ, x, y, options_4bit,
options_rgb, options_rgb,
lookahead) lookahead)
quant_error = input_pixel_rgb - output_pixel_rgb
image_4bit[y, x] = output_pixel_4bit image_4bit[y, x] = output_pixel_4bit
image_rgb[y, x, :] = output_pixel_rgb image_rgb[y, x, :] = output_pixel_rgb
quant_error = input_pixel_rgb - output_pixel_rgb
dither_apply.apply(dither, screen, x, y, image_rgb, quant_error) dither_apply.apply(dither, screen, x, y, image_rgb, quant_error)
return image_4bit, image_rgb return image_4bit, image_rgb
@ -478,8 +459,11 @@ def main():
differ = CIE2000Distance() differ = CIE2000Distance()
output_4bit, output_rgb = dither_image(screen, image, dither, differ, start = time.time()
lookahead=args.lookahead) output_4bit, output_rgb = dither_apply.dither_image(screen, image, dither,
differ,
lookahead=args.lookahead)
print(time.time() - start)
screen.pack(output_4bit) screen.pack(output_4bit)
out_image = Image.fromarray(linear_to_srgb(output_rgb).astype(np.uint8)) out_image = Image.fromarray(linear_to_srgb(output_rgb).astype(np.uint8))

View File

@ -3,7 +3,7 @@
cimport cython cimport cython
import numpy as np import numpy as np
# from cython.parallel import prange # from cython.parallel import prange
# from cython.view cimport array as cvarray from cython.view cimport array as cvarray
# from libc.stdlib cimport malloc, free # from libc.stdlib cimport malloc, free
@ -27,7 +27,7 @@ cdef void apply_one_line(float[:, :, ::1] pattern, int xl, int xr, float[:, ::1]
@cython.boundscheck(False) @cython.boundscheck(False)
@cython.wraparound(False) @cython.wraparound(False)
def apply(dither, screen, int x, int y, float [:, :, ::1]image, float[::1] quant_error): cdef apply(dither, screen, int x, int y, float [:, :, ::1]image, float[] quant_error):
cdef int i, j, k cdef int i, j, k
# XXX only need 2 dimensions now # XXX only need 2 dimensions now
@ -89,8 +89,8 @@ def dither_lookahead(
cdef int i, j, k, l cdef int i, j, k, l
cdef float[:, :, ::1] lah_image_rgb = np.empty( # XXX malloc
(2 ** lookahead, lookahead + xr - xl, 3), dtype=np.float32) cdef float[:, :, ::1] lah_image_rgb = cvarray((2 ** lookahead, lookahead + xr - xl, 3), itemsize=sizeof(float), format="f")
for i in range(2**lookahead): for i in range(2**lookahead):
# Copies of input pixels so we can dither in bulk # Copies of input pixels so we can dither in bulk
for j in range(xxr - x): for j in range(xxr - x):
@ -143,3 +143,62 @@ def dither_lookahead(
best = i best = i
return options_4bit[best, 0], options_rgb[best, 0, :] return options_4bit[best, 0], options_rgb[best, 0, :]
import functools
@functools.lru_cache(None)
def lookahead_options(screen, lookahead, last_pixel_4bit, x):
options_4bit = np.empty((2 ** lookahead, lookahead), dtype=np.uint8)
options_rgb = np.empty((2 ** lookahead, lookahead, 3), dtype=np.float32)
for i in range(2 ** lookahead):
output_pixel_4bit = last_pixel_4bit
for j in range(lookahead):
xx = x + j
palette_choices_4bit, palette_choices_rgb = \
screen.pixel_palette_options(output_pixel_4bit, xx)
output_pixel_4bit = palette_choices_4bit[(i & (1 << j)) >> j]
output_pixel_rgb = np.array(
palette_choices_rgb[(i & (1 << j)) >> j])
# XXX copy
options_4bit[i, j] = output_pixel_4bit
options_rgb[i, j, :] = np.copy(output_pixel_rgb)
return options_4bit, options_rgb
@cython.boundscheck(False)
@cython.wraparound(False)
def dither_image(
screen, float [:, :, ::1] image_rgb, dither, differ, int lookahead):
image_4bit = np.empty(
(image_rgb.shape[0], image_rgb.shape[1]), dtype=np.uint8)
cdef int yres = screen.Y_RES
cdef int xres = screen.X_RES
cdef int y, x, i
cdef float[3] quant_error
cdef (unsigned char)[:, ::1] options_4bit
cdef float[:, :, ::1] options_rgb
for y in range(yres):
# print(y)
output_pixel_4bit = np.uint8(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, differ, x, y, options_4bit,
options_rgb, lookahead)
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]
image_4bit[y, x] = output_pixel_4bit
apply(dither, screen, x, y, image_rgb, quant_error)
return image_4bit, np.array(image_rgb)