Commit Graph

100 Commits

Author SHA1 Message Date
kris 8694ab364e Perform conversions in linear RGB space 2021-11-16 12:38:53 +00:00
kris 7ad560247b Clean up 2021-11-16 12:24:43 +00:00
kris 10c829906b Checkpoint
- Repeatedly refit palettes since k-means is only a local
  optimization.  This can produce incremental improvements in image
  quality but may also overfit, especially on complex images.
- use pygame to render incremental images
- Fix off-by-one in palette striping
- When fitting palettes, first cluster a 16-colour palette for the
  entire image and use this to initialize the centroids for individual
  palettes.  This improves quality when fitting images with large
  blocks of colour, since they will otherwise be fit separately and
  may have slight differences.  With a global initializer these will
  tend to be the same.  This also improves performance.
2021-11-16 11:21:53 +00:00
kris b363d60754 Checkpoint
- switch to pyclustering for kmedians
- allow choosing the same palette as previous line, with a multiplicative penalty to distance in case it's much better
- iterate kmedians multiple times and choose the best, since it's only a local optimum
2021-11-15 09:19:44 +00:00
kris 643e50349e Optimize more 2021-11-13 17:29:13 +00:00
kris 0596aefe0b Use pyclustering for kmedians instead of hand-rolled
Optimize cython code
2021-11-13 17:18:34 +00:00
kris 5cab854269 Fit palettes from overlapping line ranges, and map line to palette
when dithering with two limitations:

- cannot choose the same palette as the previous line (this avoids banding)
- must be within +/- 1 of the "base" palette for the line number

This gives pretty good results!
2021-11-11 16:10:03 +00:00
kris ee2229d0ea * Modify Floyd-Steinberg dithering to diffuse less error in the y
direction.  Otherwise, errors can accumulate in an RGB channel if
  there are no palette colours with an extremal value, and then when
  we introduce a new palette the error all suddenly discharges in a
  spurious horizontal line.  This now gives quite good results!

* Switch to using L1-norm for k-means, per suggestion of Lucas
  Scharenbroich: "A k-medians effectively uses an L1 distance metric
  instead of L2 for k-means.  Using a squared distance metric causes
  the fit to "fall off" too quickly and allows too many of the k
  centroids to cluster around areas of high density, which results in
  many similar colors being selected.  A linear cost function forces
  the centroids to spread out since the error influence has a broader
  range."
2021-11-11 11:10:22 +00:00
kris 8c34d87216 WIP - interleave 3 successive palettes for each contiguous row range.
Avoids the banding but not clear if it's overall better

Also implement my own k-means clustering which is able to keep some
centroids fixed, e.g. to be able to retain some fixed palette entries
while swapping out others.  I was hoping this would improve colour
blending across neighbouring palettes but it's also not clear if it
does.
2021-11-10 18:30:39 +00:00
kris 322123522c Assign scan lines randomly to palettes and cluster independently. This doesn't give good results either, since
neighbouring lines end up getting similar but not identical colours, which still results in horizontal striping.
2021-11-10 00:34:17 +00:00
kris fb52815412 Experiment with striping 16 palettes contiguously across line ranges.
As expected it has clear banding.  A better approach (though still not optimal)
might be to assign lines to palettes randomly.
2021-11-09 22:42:27 +00:00
kris 80885aabf9 Working SHR version. Still just uses a single palette 2021-11-09 22:26:34 +00:00
kris 21058084e2 Tidy 2021-11-09 16:14:37 +00:00
kris 01b19a4a06 Use 4-bit RGB values instead of 8-bit 2021-11-09 15:35:44 +00:00
kris a92c9cd7b5 Work in CAM16-UCS colour space and cythonize 2021-11-09 15:13:07 +00:00
kris 173c283369 First implementation of using k-means clustering in RGB space to dither a 320x200 SHR image. 2021-11-09 11:23:25 +00:00
kris 34ae40ac2d Fix a bug with output for non-ntsc palette 2021-11-03 15:17:57 +00:00
kris bf76271d75 NTSC conversion should be using YIQ space instead of YUV, which seems
to explain several fudge factors I needed to include to match colours.
2021-11-02 23:28:58 +00:00
kris e84dfb59f9 Tidy a bit 2021-11-02 15:26:43 +00:00
kris b63fd81c07 Unify DHGRScreen implementations 2021-11-02 15:23:23 +00:00
kris 809b975e6e Return a bitmap directly from dither_image. This removes the need to
deal with n-bit encodings at all in DHGRScreen
2021-11-02 14:42:00 +00:00
kris cf69dc9cf2 Bounds check lookahead 2021-11-02 13:42:23 +00:00
kris 8cfee55b1d Get rid of support for 140px mode, it was only useful as a demo of why
other converters have the wrong basic approach.
2021-11-02 13:40:32 +00:00
kris feefdb5dc6 Use .npy format for RGB to CAM16UCS conversion matrix, and get rid of precomputed CIE2000 distances 2021-07-19 18:35:44 +01:00
kris e979df03bc Nope, don't need XYZ 2021-07-19 18:13:43 +01:00
kris 8b500b16cb Dither in XYZ representation but use CAM16UCS for colour differences.
This gives the best of both worlds: dithering in a linear space, with
good (and fast) perceptual error differences

TBD: would linear RGB work as well as XYZ?
2021-07-19 17:54:46 +01:00
kris 70074a2942 Support arbitrary gamma correction of input image 2021-07-19 09:57:26 +01:00
kris 7a3adea025 Try cam16UCS instead 2021-07-15 14:25:32 +01:00
kris 4fcda908bd WIP - use colourspacious to perform image dithering in CAM02_UCS
colour space, which is supposed to be perceptually uniform.  i.e. we
can use Euclidean distance instead of CIEDE2000
2021-07-15 13:58:22 +01:00
kris 7a7923503f With recent optimizations converter became ~2.1x faster, so
--lookahead=8 is a reasonable new default.
2021-03-15 17:55:21 +00:00
kris 101d974d50 Use hyphens in option names so that --no-boolean-option is
consistently hyphenated.
2021-03-15 17:21:22 +00:00
kris f87ca90fd4 Add a --verbose option to output progress 2021-03-15 15:01:21 +00:00
kris 467a0cd196 Tidy up a bit to prepare for merge 2021-03-15 10:45:33 +00:00
kris ad9515dcf2 Implement NTSC emulation, using an 8 pixel window for chroma signal.
Use this to precompute a new ntsc palette with 256 entries (though
only 84 unique colours) that are available by appropriate pixel
sequences.  Unfortunately the precomputed distance matrix for this
palette is 4GB!

Optimize the precomputation to be less memory hungry, while also
making efficient use of the mmapped output file.

Add support for dithering images using this 8-bit palette depth,
i.e. to optimize for NTSC rendering.  This often gives better image
quality since more colours are available, especially when modulating
areas of similar colour.

Fix 140 pixel dithering and render the output including NTSC fringing
instead of the unrealistic 140px output that doesn't include it.

Add support for rendering output image using any target palette, which
is useful e.g. for comparing how an 8-pixel NTSC rendered image will
be displayed on an emulator using 4-pixel ntsc emulation (there is
usually some colour bias, because the 8 pixel chroma blending tends to
average away colours).

Switch the output binary format to write AUX memory first, which
matches the image format of other utilities.
2021-02-14 23:34:25 +00:00
kris 3ac5c284e9 Checkpoint NTSC emulation 2021-02-03 23:40:16 +00:00
kris a835baadf8 Fix --show_input 2021-01-26 00:16:51 +00:00
kris 2bbd65a079 Add some comments and docstrings 2021-01-25 23:16:46 +00:00
kris dd8cf07c49 Add support for multiple palettes 2021-01-25 22:28:00 +00:00
kris 84442f32a7 Clean up some more 2021-01-21 23:33:12 +00:00
kris a4ecf69610 Checkpoint WIP 2021-01-21 23:17:55 +00:00
kris 9f0cd870e7 In 140px resolution show the output image with and without NTSC fringing 2021-01-16 18:11:21 +00:00
kris f9f1398969 Show output as 560x384 regardless of input size 2021-01-16 17:57:21 +00:00
kris 18ce9d8f82 Expand help text 2021-01-15 23:03:31 +00:00
kris a061795022 Re-enable 140px mode 2021-01-15 22:58:01 +00:00
kris 8d671f08ac Remove bitmap support 2021-01-15 22:34:22 +00:00
kris d57d6f9038 Add flags to show input/output images 2021-01-15 22:34:03 +00:00
kris ce99d62304 Tidy 2021-01-15 22:28:44 +00:00
kris f132f0b84b Default to --lookahead=6 now that it's fast 2021-01-15 22:25:32 +00:00
kris c5b8feb17e Parametrize dither patterns as command-line argument 2021-01-15 22:25:06 +00:00
kris f5553e00b8 Rename 2021-01-15 22:20:28 +00:00