Color quantization: Difference between revisions

Content added Content deleted
(Added Wren)
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.
<lang c>typedef struct oct_node_t oct_node_t, *oct_node;
<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);
}</lang>
}</syntaxhighlight>


=={{header|Common Lisp}}==
=={{header|Common Lisp}}==
{{libheader|opticl}}
{{libheader|opticl}}
Use median cut.
Use median cut.
<lang lisp>(defpackage #:quantize
<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)))</lang>
(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.
<lang d>import core.stdc.stdio, std.stdio, std.algorithm, std.typecons,
<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");
}</lang>
}</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.
<lang d>import core.stdc.stdlib: malloc, calloc, realloc, free, abort;
<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;
}</lang>
}</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.
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 1,104: Line 1,104:
*pq = q[:n]
*pq = q[:n]
return c
return c
}</lang>
}</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.
<lang Haskell>import qualified Data.ByteString.Lazy as BS
<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>"</lang>
_ -> 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.


<lang j>kmcL=:4 :0
<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
)</lang>
)</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.


<lang j> 16 kmcL img
<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</lang>
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.
<lang julia>
<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.
<lang scala>// Version 1.2.41
<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()
}</lang>
}</syntaxhighlight>


{{output}}
{{output}}
Line 1,351: Line 1,351:


=={{header|Mathematica}} / {{header|Wolfram Language}}==
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<lang Mathematica>ColorQuantize[Import["http://rosettacode.org/mw/images/3/3f/Quantum_frog.png"],16,Dithering->False]</lang>
<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.


<lang Nim>import algorithm
<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</lang>
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]


<lang ocaml>let rem_from rem from =
<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)</lang>
(res)</syntaxhighlight>


=={{header|Perl}}==
=={{header|Perl}}==
<lang perl>use strict;
<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")</lang>
$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.
<lang Phix>-- demo\rosetta\Color_quantization.exw
<syntaxhighlight lang="phix">-- demo\rosetta\Color_quantization.exw
include pGUI.e
include pGUI.e


Line 1,677: Line 1,677:


IupMainLoop()
IupMainLoop()
IupClose()</lang>
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}}==
<lang python>from PIL import Image
<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()</lang>
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 perl6>use MagickWand;
<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');</lang>
$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}}==
<lang ruby>require('Image::Magick')
<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')</lang>
quantize_image(input: 'Quantum_frog.png')</syntaxhighlight>


=={{header|Tcl}}==
=={{header|Tcl}}==
{{trans|OCaml}}
{{trans|OCaml}}
{{libheader|Tk}}
{{libheader|Tk}}
<lang tcl>package require Tcl 8.6
<syntaxhighlight lang="tcl">package require Tcl 8.6
package require Tk
package require Tk


Line 2,350: Line 2,350:
}
}
return $newimg
return $newimg
}</lang>
}</syntaxhighlight>
Demonstration code:
Demonstration code:
<lang tcl>set src [image create photo -file quantum_frog.png]
<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</lang>
exit</syntaxhighlight>


=={{header|Wren}}==
=={{header|Wren}}==
Line 2,363: Line 2,363:
{{libheader|Wren-dynamic}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-sort}}
{{libheader|Wren-sort}}
<lang ecmascript>import "dome" for Window
<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")</lang>
var Game = ColorQuantization.new("Quantum_frog.png", "Quantum_frog_16.png")</syntaxhighlight>


{{out}}
{{out}}