img2ansi

package module
v0.0.0-...-259f9a5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2024 License: BSD-3-Clause Imports: 19 Imported by: 2

README

img2ansi

Block-Based ANSI Art Dithering Algorithm (Brown Dithering Algorithm)

This project implements a unique dithering algorithm specifically designed for converting images into ANSI art. Unlike traditional dithering methods, my approach uses a block-based processing technique optimized for terminal and text-based display.

Key Features

  1. Block-Based Processing: Operates on 2x2 pixel blocks instead of individual pixels, allowing for more complex patterns within a single character cell.

  2. ANSI Color Quantization: Utilizes a specialized color quantization scheme tailored for the ANSI color palette, ensuring optimal color representation in terminal environments.

  3. Unicode Block Character Selection: Chooses the best Unicode block character to represent each 2x2 pixel block, maximizing the detail in the final ANSI art.

  4. Dual-Color Representation: Each block is represented by both a foreground and background color, enabling more nuanced color transitions and detail.

  5. Edge Detection Integration: Incorporates edge detection to adjust error distribution, preserving important image details.

  6. Optimized for Text Output: Designed to produce ANSI escape code sequences, making it ideal for terminal-based image display.

  7. Optimized KD Tree Search: Optimized for ANSI art generation by precomputing quantized color distances.

How It Works

The algorithm processes the input image in 2x2 blocks, determining the best Unicode character and color combination to represent each block. It then uses a modified error diffusion technique inspired by Floyd-Steinberg dithering to distribute quantization errors to neighboring blocks.

This approach results in high-quality ANSI art that preserves the detail and color of the original image while optimizing for the constraints of text-based display.

Requirements

Requires OpenCV 4 to be installed.

Example Output

The below examples are 80 column wide images, with a scale factor of 2. The first example uses the default 16-color ANSI palette, while the second example uses the 256 color scheme.

Baboon ANSI Art - 16 colors Baboon ANSI Art - JetBrains Baboon ANSI Art - 256 colors

Installation

To build the program, run the following commands:

go build github.com/wbrown/ansi2img/cmd/ansify

Usage

./img2ansi -input <input> [-output <output>] [-width <width>] [-scale <scale>] [-quantization <quantization>] [-maxchars <maxchars>] [-color_method <color_method>] [-palette <palette>] [-kdsearch <kdsearch>] [-cache_threshold <cache_threshold>]

Performance

The following performance options are available. There are tradeoffs between speed and quality. The defaults are chosen to be a good balance between the two. But if you want the absolute best quality, set the -kdsearch option to 0 and the -cache_threshold option to 0. This may cause the program to take multiple minutes to run.

  • -kdsearch <int>: Number of nearest neighbors to search in KD-tree, 0 to disable (default 50)

The KD search option is the number of nearest neighbors to search in the KD-tree for the block cache. A value of 0 will disable the KD-tree search and the cache.

  • -cache_threshold <float>: Threshold for block cache (default 40)

The block cache is a cache of the block characters that are used to render the image. The cache is used to speed up the program by not having to recompute the blocks for each 2x2 pixel block in the image. It is a fuzzy cache, so it is thresholded on error distance from the target block.

There are built in embedded palettes that have precomputed tables for the colors. These are ansi16, ansi256, and jetbrains32. Each precomputed palette also has three color spaces that are precomputed: RGB, Lab, and Redmean. The default is Redmean.

Colors

By default the program uses the 16-color ANSI palette, split into 8 foreground colors and 8 background colors. There are three palettes built in, selectable by using the -palette option:

  • ansi16: The default 16-color ANSI palette
  • ansi256: The 256-color ANSI palette
  • jetbrains32: The JetBrains color scheme that uses 32 colors by having separate palettes for foreground and background colors. The program performs well without quantization, but if you want to reduce the number of colors in the output, you can use the -quantization option. The default is 256 colors. This isn't the output colors, but the number of colors used in the quantization step.

There are three color space options available: RGB, Lab, and Redmean. The most perceptually accurate is Lab, but it is also the slowest. The default is Redmean.

Image Size

The -width option can be used to set the target width of the output image, this is the primary factor in determining the output ANSI dimensions. The default -scale is 2, which approximately halves the height of the output, to compensate for the fact that characters are taller than they are wide.

  -cache_threshold float
    	Threshold for block cache (default 40)
  -colormethod string
    	Color distance method: RGB, LAB, or Redmean (default "RGB")
  -input string
    	Path to the input image file (required)
  -kdsearch int
    	Number of nearest neighbors to search in KD-tree, 0 to disable (default 50)
  -maxchars int
    	Maximum number of characters in the output (default 1048576)
  -output string
    	Path to save the output (if not specified, prints to stdout)
  -palette string
    	Path to the palette file (Embedded: ansi16, ansi256, jetbrains32) (default "ansi16")
  -quantization int
    	Quantization factor (default 256)
  -scale float
    	Scale factor for the output image (default 2)
  -width int
    	Target width of the output image (default 80)

Documentation

Index

Constants

View Source
const (
	ESC = "\u001b"
)

Variables

View Source
var (
	TargetWidth    = 100
	ScaleFactor    = 3.0
	MaxChars       = 1048576
	Quantization   = 1
	KdSearch       = 0
	CacheThreshold = 50.0

	LookupHits    int
	LookupMisses  int
	BeginInitTime time.Time
	BestBlockTime time.Duration

	DistinctColors int
)
View Source
var ColorDistanceMethods = []string{
	"RGB",
	"LAB",
	"Redmean",
}
View Source
var CurrentColorDistanceMethod = MethodRGB

Global variable to set the color distance method

Functions

func BrownDitherForBlocks

func BrownDitherForBlocks(
	img gocv.Mat,
	edges gocv.Mat,
) [][]BlockRune

BrownDitherForBlocks applies a modified Floyd-Steinberg dithering algorithm to an image operating on 2x2 blocks rather than pixels. The function takes an input image and a binary image with edges detected. It returns a BlockRune representation with the dithering algorithm applied, with colors quantized to the nearest ANSI color.

func CompressANSI

func CompressANSI(ansiImage string) string

CompressANSI compresses an ANSI image by combining adjacent blocks with the same foreground and background colors. The function takes an ANSI image as a string and returns the more efficient ANSI image as a string.

func ComputeDistinctColors

func ComputeDistinctColors()

func ImageToANSI

func ImageToANSI(imagePath string) string

ImageToANSI converts an image to ANSI art. The function takes the path to an image file as a string and returns the image as an ANSI string.

func LoadPalette

func LoadPalette(path string) (*ComputedTables, *ComputedTables, error)

func LoadPaletteAsCompactTables

func LoadPaletteAsCompactTables(path string) (CompactComputedTables,
	CompactComputedTables, error)

func LoadPaletteJSON

func LoadPaletteJSON(path string) (*ComputedTables, *ComputedTables, error)

func PaletteSame

func PaletteSame(fgData AnsiData, bgData AnsiData) bool

func ReadAnsiDataFromJSON

func ReadAnsiDataFromJSON(filename string) (AnsiData, AnsiData, error)

ReadAnsiDataFromJSON reads ANSI color data from a JSON file and returns the foreground and background ANSI color data as AnsiData slices. The function takes a filename as a string and returns the foreground and background AnsiData slices, or an error if the file cannot be read or the data cannot be unmarshalled.

Types

type AnsiData

type AnsiData []AnsiEntry

func (AnsiData) ToOrderedMap

func (ansiData AnsiData) ToOrderedMap() *OrderedMap

ToOrderedMap converts an AnsiData slice to an OrderedMap with the values as keys and the keys as values.

type AnsiEntry

type AnsiEntry struct {
	Key   uint32
	Value string
}

type ApproximateCache

type ApproximateCache map[Uint256]lookupEntry

ApproximateCache is a map of Uint256 to lookupEntry that is used to store approximate matches for a given block of 4 RGB values. Approximate matches are performed by comparing the error of a given match to a threshold Value.

The Key of the map is a Uint256, which is a 256-bit unsigned integer that is used to represent the foreground and background colors of a block of 4 RGB values.

There may be multiple matches for a given Key, so the Value of the map is a lookupEntry, which is a struct that contains a slice of Match structs.

type BlockRune

type BlockRune struct {
	Rune rune
	FG   RGB
	BG   RGB
}

BlockRune represents a 2x2 block of runes with foreground and background colors mapped in the ANSI color space. The struct contains a rune representing the block character, and two RGB colors representing the foreground and background colors of the block.

type ByAnsiCode

type ByAnsiCode AnsiData

ByAnsiCode implements sort.Interface for AnsiData based on the numeric value of the ANSI code

func (ByAnsiCode) Len

func (a ByAnsiCode) Len() int

func (ByAnsiCode) Less

func (a ByAnsiCode) Less(i, j int) bool

func (ByAnsiCode) Swap

func (a ByAnsiCode) Swap(i, j int)

type ColorDistance

type ColorDistance struct {
	// contains filtered or unexported fields
}

ColorDistance is a helper struct to keep track of colors and their distances

type ColorDistanceMethod

type ColorDistanceMethod int

ColorDistanceMethod is an enum type for different color distance calculation methods

const (
	MethodRGB ColorDistanceMethod = iota
	MethodLAB
	MethodRedmean
)

type ColorMethodCompactTables

type ColorMethodCompactTables map[ColorDistanceMethod]CompactTablePair

type ColorNode

type ColorNode struct {
	Color       RGB
	Left, Right *ColorNode
	SplitAxis   int
}

ColorNode represents a node in a KD-tree that stores RGB colors. Each node contains a color, a left child, a right child, and the axis along which the colors are split.

func DeserializeKDTree

func DeserializeKDTree(data []byte) (*ColorNode, []byte)

func (*ColorNode) Serialize

func (node *ColorNode) Serialize() []byte

type ColorTableEntry

type ColorTableEntry struct {
	Color RGB
	Index uint32
}

type CompactComputedTables

type CompactComputedTables struct {
	ColorArr        []RGB
	AnsiData        AnsiData
	ClosestColorIdx []byte // Use 1 byte per color instead of full RGB
	ColorTable      []ColorTableEntry
	KDTreeData      []byte // Serialized KD-tree data
}

func CompactComputeTables

func CompactComputeTables(colorData AnsiData) CompactComputedTables

func (CompactComputedTables) Restore

func (cct CompactComputedTables) Restore() ComputedTables

type CompactTablePair

type CompactTablePair struct {
	Fg CompactComputedTables
	Bg CompactComputedTables
}

type ComputedTables

type ComputedTables struct {
	AnsiData        AnsiData
	ColorArr        *[]RGB
	ClosestColorArr *[]RGB
	ColorTable      *map[RGB]uint32
	KdTree          *ColorNode
}

func ComputeTables

func ComputeTables(colorData AnsiData) ComputedTables

ComputeTables computes the color tables and KD-tree for a given color map. The function takes an OrderedMap of color codes and RGB colors as input, and returns a ComputedTables struct containing the color array, closest color array, color table, and KD-tree.

func LoadPaletteBinary

func LoadPaletteBinary(path string) (fg, bg *ComputedTables, err error)

type LAB

type LAB struct {
	L float64
	A float64
	B float64
}

type Match

type Match struct {
	Rune  rune
	FG    RGB
	BG    RGB
	Error float64
}

Match is a struct that contains the rune, foreground color, background color, and error of a match. The error is a float64 Value that represents the difference between the actual block of 4 RGB values and the pair of foreground and background colors encoded in the Key as an Uint256.

type OrderedMap

type OrderedMap struct {
	// contains filtered or unexported fields
}

OrderedMap represents an ordered map data structure

func NewOrderedMap

func NewOrderedMap() *OrderedMap

New creates a new OrderedMap

func (*OrderedMap) Delete

func (om *OrderedMap) Delete(key interface{})

Delete removes a Key-Value pair from the map

func (*OrderedMap) Get

func (om *OrderedMap) Get(key interface{}) (interface{}, bool)

Get retrieves a Value from the map by Key

func (*OrderedMap) Iterate

func (om *OrderedMap) Iterate(f func(key, value interface{}))

Iterate calls the provided function for each Key-Value pair in order

func (*OrderedMap) Keys

func (om *OrderedMap) Keys() []interface{}

Keys returns a slice of keys in the order they were inserted

func (*OrderedMap) Len

func (om *OrderedMap) Len() int

Len returns the number of elements in the map

func (*OrderedMap) Set

func (om *OrderedMap) Set(key, value interface{})

Set adds a Key-Value pair to the map

type PriorityQueue

type PriorityQueue []ColorDistance

PriorityQueue implements heap.Interface and holds ColorDistance items

func (PriorityQueue) Len

func (pq PriorityQueue) Len() int

func (PriorityQueue) Less

func (pq PriorityQueue) Less(i, j int) bool

func (*PriorityQueue) Pop

func (pq *PriorityQueue) Pop() interface{}

func (*PriorityQueue) Push

func (pq *PriorityQueue) Push(x interface{})

func (PriorityQueue) Swap

func (pq PriorityQueue) Swap(i, j int)

type Quadrants

type Quadrants struct {
	TopLeft     bool
	TopRight    bool
	BottomLeft  bool
	BottomRight bool
}

Quadrants represents the four quadrants of a 2x2 block of a rune that can be colored independently. Each quadrant is represented by a boolean Value, where true indicates that the quadrant should be colored with the foreground color, and false indicates that the quadrant should be colored with the background color.

type RGB

type RGB struct {
	R, G, B uint8
}

RGB represents a color in the RGB color space with 8-bit channels, where each channel ranges from 0 to 255. The RGB color space is additive, meaning that colors are created by adding together the red, green, and blue channels.

type Uint128

type Uint128 struct {
	High uint64
	Low  uint64
}

type Uint256

type Uint256 struct {
	Highest uint64
	High    uint64
	Low     uint64
	Lowest  uint64
}

Directories

Path Synopsis
cmd
ansify Module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL