Convert images to Apple II Double Hi-Res format
Go to file
2023-02-25 23:56:52 +00:00
docs Move DHR examples to subdir in preparation for adding SHR examples 2022-07-18 23:11:23 +01:00
examples Add support for hi-res conversions (#11) 2023-02-03 00:40:32 +00:00
build-examples.sh Add a script to regenerate image conversions for example images, and add a .po disk image 2021-11-04 14:32:58 +00:00
common.pxd Optimize dither_dhr.dither_image performance by about 2x (#12) 2023-02-25 21:21:43 +00:00
common.pyx Optimize dither_dhr.dither_image performance by about 2x (#12) 2023-02-25 21:21:43 +00:00
convert_dhr.py Add support for hi-res conversions (#11) 2023-02-03 00:40:32 +00:00
convert_hgr.py Add support for hi-res conversions (#11) 2023-02-03 00:40:32 +00:00
convert_shr.py Add support for DHGR mono conversions 2023-01-21 17:30:27 +00:00
convert.py Add support for hi-res conversions (#11) 2023-02-03 00:40:32 +00:00
dither_dhr.pyx Optimize dither_dhr.dither_image performance by about 2x (#12) 2023-02-25 21:21:43 +00:00
dither_pattern.py Add support for hi-res conversions (#11) 2023-02-03 00:40:32 +00:00
dither_shr.pyx Optimize dither_dhr.dither_image performance by about 2x (#12) 2023-02-25 21:21:43 +00:00
image.py Add support for hi-res conversions (#11) 2023-02-03 00:40:32 +00:00
LICENSE Create LICENSE 2021-01-25 22:36:50 +00:00
ntsc_colours.py NTSC conversion should be using YIQ space instead of YUV, which seems 2021-11-02 23:28:58 +00:00
palette_ntsc.py Oops, no that was wrong. I forgot to cross-check against OpenEmulator <o> 2021-11-03 12:40:22 +00:00
palette.py Optimize palette initialization and NTSC image conversion 2023-02-25 23:56:52 +00:00
precompute_conversion.py Create data directory before writing to it 2023-01-31 21:12:42 +00:00
README.md Bump version in header 2023-02-03 00:42:11 +00:00
requirements.txt Add a requirements.txt to simplify installation 2022-07-18 22:11:32 +01:00
screen.py Optimize palette initialization and NTSC image conversion 2023-02-25 23:56:52 +00:00
setup.py Split out common utility functions into a shared module 2021-11-26 12:26:46 +00:00

][-pix 2.2

][-pix is an image conversion utility targeting Apple II graphics modes, currently Hi-Res (all models), Double Hi-Res (enhanced //e, //c, //gs) and Super Hi-Res (//gs).

Installation

Requires:

These dependencies can be installed using the following command:

# Install python dependencies
pip install -r requirements.txt

To build ][-pix, run the following commands:

# Compile cython code
python setup.py build_ext --inplace

# Precompute colour conversion matrices, used as part of image optimization
python precompute_conversion.py

Usage

To convert an image, the basic command is:

python convert.py <mode> [<flags>] <input> <output>

where

  • mode is one of the following:
    • hgr for Hi-Res Colour (560x192 but only half of the horizontal pixels may be independently controlled)
    • dhr for Double Hi-Res Colour (560x192)
    • dhr_mono for Double Hi-Res Mono (560x192)
    • shr for Super Hi-Res (320x200)
  • input is the source image file to convert (e.g. my-image.jpg)
  • output is the output filename to produce (e.g. my-image.dhr)

The following flags are supported in all modes:

  • --show-input Whether to show the input image before conversion. (default: False)
  • --show-output Whether to show the output image after conversion. (default: True)
  • --save-preview Whether to save a .PNG rendering of the output image (default: True)
  • --verbose Show progress during conversion (default: False)
  • --gamma-correct Gamma-correct image by this value (default: 2.4)

For other available options, use python convert.py <mode> --help

See below for mode-specific instructions.

Hi-Res

To convert an image to Hi-Res the simplest usage is:

python convert.py hgr <input> <output.hgr>

<output.hgr> contains the hires image data in a form suitable for transfer to an Apple II disk image.

TODO: document flags

TODO: add more details about HGR - resolution and colour model.

Double Hi-Res

To convert an image to Double Hi-Res (560x192, 16 colours but it's complicated), the simplest usage is:

python convert.py dhr --palette ntsc <input> <output.dhr>

<output.dhr> contains the double-hires image data in a form suitable for transfer to an Apple II disk image. The 16k output consists of 8k AUX data first, 8K MAIN data second (this matches the output format of other DHGR image converters). i.e. if loaded at 0x2000, the contents of 0x2000..0x3fff should be moved to 0x4000..0x5fff in AUX memory, and the image can be viewed on DHGR page 2.

By default, a preview image will be shown after conversion, and saved as <output>-preview.png

TODO: document flags

For more details about Double Hi-Res graphics and the conversion process, see here.

Super Hi-Res

To convert an image to Super Hi-Res (320x200, up to 256 colours), the simplest usage is:

python convert.py shr <input> <output.shr>

i.e. no additional options are required. In addition to the common flags described above, these additional flags are supported for shr conversions:

  • --save-intermediate Whether to save each intermediate iteration, or just the final image (default: False)
  • --fixed-colours How many colours to fix as identical across all 16 SHR palettes. (default: 0)
  • --show-final-score Whether to output the final image quality score (default: False)

TODO: link to KansasFest 2022 talk slides/video for more details

Examples

Hi-Res

This image was generated using

python convert.py hgr examples/hgr/mandarin-duck.jpg examples/hgr/mandarin-duck.bin

The image on the right is a screenshot taken from OpenEmulator.

Mandarin duck Mandarin duck

(Source: Adrian Pingstone, public domain, via Wikimedia Commons)

Portrait Portrait

(Source: Devanath, public domain)

TODO: add more hi-res images

Double Hi-Res

See here for more sample Double Hi-Res image conversions.

Original

Two colourful parrots sitting on a branch

(Source: Shreygadgil, CC BY-SA 4.0, via Wikimedia Commons)

][-pix preview image

This image was generated using

python convert.py dhr --lookahead 8 --palette openemulator examples/dhr/parrots-original.png examples/dhr/parrots-iipix-openemulator.dhr

The resulting ][-pix preview PNG image is shown here.

Two colourful parrots sitting on a branch

OpenEmulator screenshot

This is a screenshot taken from OpenEmulator when viewing the Double Hi-res image.

Two colourful parrots sitting on a branch

Some difference in colour tone is visible due to blending of colours across pixels (e.g. brown blending into grey, in the background). This is due to the fact that OpenEmulator simulates the reduced chroma bandwidth of the NTSC signal.

][-pix also allows modeling this NTSC signal behaviour, which effectively allows access to more than 16 DHGR colours, through carefully chosen sequences of pixels (see below for more details). The resulting images have much higher quality, but only when viewed on a suitable target (e.g. OpenEmulator, or real hardware). On other targets the colour balance tends to be skewed, though image detail is still good.

This is an OpenEmulator screenshot of the same image converted with --palette=ntsc instead of --palette=openemulator. Colour match to the original is substantially improved, and more colour detail is visible, e.g. in the shading of the background.

Two colourful parrots sitting on a branch

Super Hi-Res

See here for more sample Super Hi-Res image conversions.

Original

European rabbit kitten

(Source: Alexis LOURS, Licensed under Creative Commons Attribution 2.0 Generic, via Wikimedia Commons)

][-pix preview image

This image was generated using

python convert.py shr examples/shr/rabbit-kitten-original.png examples/shr/rabbit-kitten-original.shr

European rabbit kitten

Future work

  • Supporting lo-res and double lo-res graphics modes, and super hi-res 3200 modes would be straightforward.

  • Super hi-res 640 mode would also likely require some investigation, since it is a more highly constrained optimization problem than 320 mode.

  • I would like to be able to find an ordered dithering algorithm that works well for Apple II graphics. Ordered dithering specifically avoids diffusing errors arbitrarily across the image, which produces visual noise (and unnecessary deltas) when combined with animation. For example such a thing may work well with my II-Vision video streamer. However the properties of NTSC artifact colour seem to be in conflict with these requirements, i.e. pixel changes always propagate colour to some extent.

Version history

v2.2 (2023-02-03)

  • Added support for HGR colour conversions

v2.1 (2023-01-21)

  • Added support for DHGR mono conversions
  • Fixed compatibility with python 3.10

v2.0 (2022-07-16)

  • Added support for Super Hi-Res 320x200 image conversions

v1.1 (2021-11-05)

  • Significantly improved conversion performance
  • Switched from using CIE2000 delta-E perceptual distance metric to Euclidean distance in CAM16-UCS space. Image quality is improved, it requires much less precomputed memory (192MB cf 4GB for the 8-pixel colour mode!) and is much faster at runtime. Win-win-win!
  • Removed support for 140px conversions since these were only useful to show why this is not the right approach to DHGR
  • Add support for modifying gamma correction, which is sometimes useful for tweaking results with very bright or dark source images.
  • Switch default to --dither=floyd, which seems to produce the best results with --palette=ntsc
  • Various internal code simplifications and cleanups

v1.0 (2021-03-15)

Initial release

me