Color quantization: Difference between revisions
Content added Content deleted
(Added Wren) |
Thundergnat (talk | contribs) m (syntax highlighting fixup automation) |
||
Line 16: | Line 16: | ||
# Node folding priorities are tracked by a binary heap instead of typical linked list. |
# Node folding priorities are tracked by a binary heap instead of typical linked list. |
||
The output image is better at preserving textures of the original than Gimp, though it obviously depends on the input image. This particular frog image has the color bar added at the top specifically to throw off some early truncation algorithms, which Gimp is suseptible to. |
The output image is better at preserving textures of the original than Gimp, though it obviously depends on the input image. This particular frog image has the color bar added at the top specifically to throw off some early truncation algorithms, which Gimp is suseptible to. |
||
< |
<syntaxhighlight lang="c">typedef struct oct_node_t oct_node_t, *oct_node; |
||
struct oct_node_t{ |
struct oct_node_t{ |
||
/* sum of all colors represented by this node. 64 bit in case of HUGE image */ |
/* sum of all colors represented by this node. 64 bit in case of HUGE image */ |
||
Line 126: | Line 126: | ||
node_free(); |
node_free(); |
||
free(heap.buf); |
free(heap.buf); |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
{{libheader|opticl}} |
{{libheader|opticl}} |
||
Use median cut. |
Use median cut. |
||
< |
<syntaxhighlight lang="lisp">(defpackage #:quantize |
||
(:use #:cl |
(:use #:cl |
||
#:opticl)) |
#:opticl)) |
||
Line 196: | Line 196: | ||
(quantized (gethash original color-map))) |
(quantized (gethash original color-map))) |
||
(values-list quantized))) |
(values-list quantized))) |
||
(write-png-file output-file result-image)))</ |
(write-png-file output-file result-image)))</syntaxhighlight> |
||
=={{header|D}}== |
=={{header|D}}== |
||
Line 202: | Line 202: | ||
{{trans|OCaml}} |
{{trans|OCaml}} |
||
This code retains the style of the original OCaML code, and uses the bitmap module from the Bitmap Task. |
This code retains the style of the original OCaML code, and uses the bitmap module from the Bitmap Task. |
||
< |
<syntaxhighlight lang="d">import core.stdc.stdio, std.stdio, std.algorithm, std.typecons, |
||
std.math, std.range, std.conv, std.string, bitmap; |
std.math, std.range, std.conv, std.string, bitmap; |
||
Line 346: | Line 346: | ||
const imq = colorQuantize(im, nCols); |
const imq = colorQuantize(im, nCols); |
||
imq.savePPM6("quantum_frog_quantized.ppm"); |
imq.savePPM6("quantum_frog_quantized.ppm"); |
||
}</ |
}</syntaxhighlight> |
||
===Imperative Version=== |
===Imperative Version=== |
||
{{trans|C}} |
{{trans|C}} |
||
This code retains part of the style of the original C code. |
This code retains part of the style of the original C code. |
||
< |
<syntaxhighlight lang="d">import core.stdc.stdlib: malloc, calloc, realloc, free, abort; |
||
import std.stdio: stderr, File; |
import std.stdio: stderr, File; |
||
import std.ascii: isWhite; |
import std.ascii: isWhite; |
||
Line 797: | Line 797: | ||
im.free; |
im.free; |
||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
Compiled with ldc2, it runs on the quantum_frog image in about 0.20 seconds with dithering and about 0.10 seconds without dithering. |
Compiled with ldc2, it runs on the quantum_frog image in about 0.20 seconds with dithering and about 0.10 seconds without dithering. |
||
=={{header|Go}}== |
=={{header|Go}}== |
||
A very basic median cut algorithm, no dithering. |
A very basic median cut algorithm, no dithering. |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 1,104: | Line 1,104: | ||
*pq = q[:n] |
*pq = q[:n] |
||
return c |
return c |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
{{libheader|JuicyPixels}} |
{{libheader|JuicyPixels}} |
||
A variation of the median cut algorithm by splitting color space on the nearest to the mean instead. It provides lower error than the Gimp output sample. |
A variation of the median cut algorithm by splitting color space on the nearest to the mean instead. It provides lower error than the Gimp output sample. |
||
< |
<syntaxhighlight lang="haskell">import qualified Data.ByteString.Lazy as BS |
||
import qualified Data.Foldable as Fold |
import qualified Data.Foldable as Fold |
||
import qualified Data.List as List |
import qualified Data.List as List |
||
Line 1,236: | Line 1,236: | ||
case args of |
case args of |
||
[path, outpath] -> quantizeIO path outpath 16 |
[path, outpath] -> quantizeIO path outpath 16 |
||
_ -> putStrLn $ "Usage: " ++ prog ++ " <image-file> <out-file.png>"</ |
_ -> putStrLn $ "Usage: " ++ prog ++ " <image-file> <out-file.png>"</syntaxhighlight> |
||
=={{header|J}}== |
=={{header|J}}== |
||
Line 1,242: | Line 1,242: | ||
Here, we use a simplistic averaging technique to build an initial set of colors and then use k-means clustering to refine them. |
Here, we use a simplistic averaging technique to build an initial set of colors and then use k-means clustering to refine them. |
||
< |
<syntaxhighlight lang="j">kmcL=:4 :0 |
||
C=. /:~ 256 #.inv ,y NB. colors |
C=. /:~ 256 #.inv ,y NB. colors |
||
G=. x (i.@] <.@* %) #C NB. groups (initial) |
G=. x (i.@] <.@* %) #C NB. groups (initial) |
||
Line 1,251: | Line 1,251: | ||
G=. (i. <./)"1 C +/&.:*: .- |:Q |
G=. (i. <./)"1 C +/&.:*: .- |:Q |
||
end.Q |
end.Q |
||
)</ |
)</syntaxhighlight> |
||
The left argument is the number of colors desired. |
The left argument is the number of colors desired. |
||
Line 1,259: | Line 1,259: | ||
The result is the colors represented as pixel triples (blue, green, red). They are shown here as fractional numbers, but they should be either rounded to the nearest integer in the range 0..255 (and possibly converted back to bmp integer form) or scaled so they are floating point triples in the range 0..1. |
The result is the colors represented as pixel triples (blue, green, red). They are shown here as fractional numbers, but they should be either rounded to the nearest integer in the range 0..255 (and possibly converted back to bmp integer form) or scaled so they are floating point triples in the range 0..1. |
||
< |
<syntaxhighlight lang="j"> 16 kmcL img |
||
7.52532 22.3347 0.650468 |
7.52532 22.3347 0.650468 |
||
8.20129 54.4678 0.0326828 |
8.20129 54.4678 0.0326828 |
||
Line 1,275: | Line 1,275: | ||
164.969 199.742 67.0467 |
164.969 199.742 67.0467 |
||
179.849 207.594 109.973 |
179.849 207.594 109.973 |
||
209.229 221.18 204.513</ |
209.229 221.18 204.513</syntaxhighlight> |
||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
The Images package for Julia uses the ImageMagick libraries by default, but this Julia module does not currently implement ImageMagick's support for color quantization. However, once ImageMagick is installed for the Images Julia module, a direct call to ImageMagick's convert command is possible. |
The Images package for Julia uses the ImageMagick libraries by default, but this Julia module does not currently implement ImageMagick's support for color quantization. However, once ImageMagick is installed for the Images Julia module, a direct call to ImageMagick's convert command is possible. |
||
< |
<syntaxhighlight lang="julia"> |
||
const execstring =`convert Quantum_frog.png -dither None -colors 16 Quantum_frog_new.png` |
const execstring =`convert Quantum_frog.png -dither None -colors 16 Quantum_frog_new.png` |
||
run(execstring) |
run(execstring) |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
{{works with|Ubuntu 16.04}} |
{{works with|Ubuntu 16.04}} |
||
Rather than coding this from scratch, we invoke programatically ImageMagick's 'convert' tool which has all this stuff built in. |
Rather than coding this from scratch, we invoke programatically ImageMagick's 'convert' tool which has all this stuff built in. |
||
< |
<syntaxhighlight lang="scala">// Version 1.2.41 |
||
import java.io.BufferedReader |
import java.io.BufferedReader |
||
Line 1,327: | Line 1,327: | ||
} |
} |
||
br.close() |
br.close() |
||
}</ |
}</syntaxhighlight> |
||
{{output}} |
{{output}} |
||
Line 1,351: | Line 1,351: | ||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
||
< |
<syntaxhighlight lang="mathematica">ColorQuantize[Import["http://rosettacode.org/mw/images/3/3f/Quantum_frog.png"],16,Dithering->False]</syntaxhighlight> |
||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
Line 1,357: | Line 1,357: | ||
We use a simple version of the median cut algorithm, with no special optimizations. |
We use a simple version of the median cut algorithm, with no special optimizations. |
||
< |
<syntaxhighlight lang="nim">import algorithm |
||
import nimPNG |
import nimPNG |
||
Line 1,457: | Line 1,457: | ||
echo "File ", Input, " processed. Result is available in file ", Output |
echo "File ", Input, " processed. Result is available in file ", Output |
||
else: |
else: |
||
echo "Error: ", status.error</ |
echo "Error: ", status.error</syntaxhighlight> |
||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
||
Line 1,463: | Line 1,463: | ||
Here we use a simplified method inspired from this paper: [http://www.leptonica.com/papers/mediancut.pdf www.leptonica.com/papers/mediancut.pdf] |
Here we use a simplified method inspired from this paper: [http://www.leptonica.com/papers/mediancut.pdf www.leptonica.com/papers/mediancut.pdf] |
||
< |
<syntaxhighlight lang="ocaml">let rem_from rem from = |
||
List.filter ((<>) rem) from |
List.filter ((<>) rem) from |
||
Line 1,563: | Line 1,563: | ||
done; |
done; |
||
done; |
done; |
||
(res)</ |
(res)</syntaxhighlight> |
||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
< |
<syntaxhighlight lang="perl">use strict; |
||
use warnings; |
use warnings; |
||
Line 1,574: | Line 1,574: | ||
$img->read(file => 'frog.png'); |
$img->read(file => 'frog.png'); |
||
my $img16 = $img->to_paletted({ max_colors => 16}); |
my $img16 = $img->to_paletted({ max_colors => 16}); |
||
$img16->write(file => "frog-16.png")</ |
$img16->write(file => "frog-16.png")</syntaxhighlight> |
||
Compare offsite images: [https://github.com/SqrtNegInf/Rosettacode-Perl5-Smoke/blob/master/ref/frog.png frog.png] vs. |
Compare offsite images: [https://github.com/SqrtNegInf/Rosettacode-Perl5-Smoke/blob/master/ref/frog.png frog.png] vs. |
||
[https://github.com/SqrtNegInf/Rosettacode-Perl5-Smoke/blob/master/ref/frog-16.png frog-16.png] |
[https://github.com/SqrtNegInf/Rosettacode-Perl5-Smoke/blob/master/ref/frog-16.png frog-16.png] |
||
Line 1,582: | Line 1,582: | ||
{{trans|Tcl}} |
{{trans|Tcl}} |
||
Gui app, shows original and modified side-by-side. |
Gui app, shows original and modified side-by-side. |
||
< |
<syntaxhighlight lang="phix">-- demo\rosetta\Color_quantization.exw |
||
include pGUI.e |
include pGUI.e |
||
Line 1,677: | Line 1,677: | ||
IupMainLoop() |
IupMainLoop() |
||
IupClose()</ |
IupClose()</syntaxhighlight> |
||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
[[file:Compare_16_Quantum_frog_PureBasic.png|comparison|thumb|200px]] |
[[file:Compare_16_Quantum_frog_PureBasic.png|comparison|thumb|200px]] |
||
[[file:Compare_16_Quantum_frog_histograms_PureBasic.png|histogram (external application)|thumb|200px]] |
[[file:Compare_16_Quantum_frog_histograms_PureBasic.png|histogram (external application)|thumb|200px]] |
||
<syntaxhighlight lang="purebasic"> |
|||
<lang PureBasic> |
|||
; ColorQuantization.pb |
; ColorQuantization.pb |
||
Line 1,905: | Line 1,905: | ||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
< |
<syntaxhighlight lang="python">from PIL import Image |
||
if __name__=="__main__": |
if __name__=="__main__": |
||
im = Image.open("frog.png") |
im = Image.open("frog.png") |
||
im2 = im.quantize(16) |
im2 = im.quantize(16) |
||
im2.show()</ |
im2.show()</syntaxhighlight> |
||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
<syntaxhighlight lang="racket"> |
|||
<lang Racket> |
|||
#lang racket/base |
#lang racket/base |
||
(require racket/class |
(require racket/class |
||
Line 2,243: | Line 2,243: | ||
r g b)) |
r g b)) |
||
(loop child (add1 level)))))) |
(loop child (add1 level)))))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
Line 2,249: | Line 2,249: | ||
{{works with|Rakudo|2018.10}} |
{{works with|Rakudo|2018.10}} |
||
<lang |
<syntaxhighlight lang="raku" line>use MagickWand; |
||
use MagickWand::Enums; |
use MagickWand::Enums; |
||
Line 2,255: | Line 2,255: | ||
$frog.read("./Quantum_frog.png"); |
$frog.read("./Quantum_frog.png"); |
||
$frog.quantize(16, RGBColorspace, 0, True, False); |
$frog.quantize(16, RGBColorspace, 0, True, False); |
||
$frog.write('./Quantum-frog-16-perl6.png');</ |
$frog.write('./Quantum-frog-16-perl6.png');</syntaxhighlight> |
||
See: [https://github.com/thundergnat/rc/blob/master/img/Quantum-frog-16-perl6.png Quantum-frog-16-perl6.png] (offsite .png image) |
See: [https://github.com/thundergnat/rc/blob/master/img/Quantum-frog-16-perl6.png Quantum-frog-16-perl6.png] (offsite .png image) |
||
=={{header|Sidef}}== |
=={{header|Sidef}}== |
||
< |
<syntaxhighlight lang="ruby">require('Image::Magick') |
||
func quantize_image(n = 16, input, output='output.png') { |
func quantize_image(n = 16, input, output='output.png') { |
||
Line 2,268: | Line 2,268: | ||
} |
} |
||
quantize_image(input: 'Quantum_frog.png')</ |
quantize_image(input: 'Quantum_frog.png')</syntaxhighlight> |
||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
{{trans|OCaml}} |
{{trans|OCaml}} |
||
{{libheader|Tk}} |
{{libheader|Tk}} |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.6 |
||
package require Tk |
package require Tk |
||
Line 2,350: | Line 2,350: | ||
} |
} |
||
return $newimg |
return $newimg |
||
}</ |
}</syntaxhighlight> |
||
Demonstration code: |
Demonstration code: |
||
< |
<syntaxhighlight lang="tcl">set src [image create photo -file quantum_frog.png] |
||
set dst [colorQuant $src 16] |
set dst [colorQuant $src 16] |
||
# Save as GIF now that quantization is done, then exit explicitly (no GUI desired) |
# Save as GIF now that quantization is done, then exit explicitly (no GUI desired) |
||
$dst write quantum_frog_compressed.gif |
$dst write quantum_frog_compressed.gif |
||
exit</ |
exit</syntaxhighlight> |
||
=={{header|Wren}}== |
=={{header|Wren}}== |
||
Line 2,363: | Line 2,363: | ||
{{libheader|Wren-dynamic}} |
{{libheader|Wren-dynamic}} |
||
{{libheader|Wren-sort}} |
{{libheader|Wren-sort}} |
||
< |
<syntaxhighlight lang="ecmascript">import "dome" for Window |
||
import "graphics" for Canvas, Color, ImageData |
import "graphics" for Canvas, Color, ImageData |
||
import "./dynamic" for Struct |
import "./dynamic" for Struct |
||
Line 2,502: | Line 2,502: | ||
} |
} |
||
var Game = ColorQuantization.new("Quantum_frog.png", "Quantum_frog_16.png")</ |
var Game = ColorQuantization.new("Quantum_frog.png", "Quantum_frog_16.png")</syntaxhighlight> |
||
{{out}} |
{{out}} |