Color quantization: Difference between revisions
Content added Content deleted
m (Updated D entry) |
(Updated D entry) |
||
Line 131: | Line 131: | ||
This code retains some of the style of the original OCaml code. |
This code retains some of the style of the original OCaml code. |
||
<lang d>import core.stdc.stdio, std.stdio, std.ascii, std.algorithm, |
<lang d>import core.stdc.stdio, std.stdio, std.ascii, std.algorithm, |
||
std.typecons, std.math, std.range, std.conv; |
std.typecons, std.math, std.range, std.conv, std.string; |
||
final class Image { |
final class Image { |
||
Line 144: | Line 144: | ||
void loadPPM6(in string fileName) { |
void loadPPM6(in string fileName) { |
||
⚫ | |||
static int read_num(FILE* f) nothrow { |
static int read_num(FILE* f) nothrow { |
||
int n; |
int n; |
||
Line 157: | Line 159: | ||
} |
} |
||
auto fin = fopen |
auto fin = fopen(fileName.toStringz(), "rb"); |
||
if (!fin) |
if (!fin) |
||
return; |
|||
if (fgetc(fin) != 'P' || |
if (fgetc(fin) != 'P' || |
||
fgetc(fin) != '6' || |
fgetc(fin) != '6' || |
||
!isWhite(fgetc(fin))) |
!isWhite(fgetc(fin))) |
||
return; |
|||
immutable int nc = read_num(fin); |
immutable int nc = read_num(fin); |
||
Line 170: | Line 172: | ||
immutable int maxval = read_num(fin); |
immutable int maxval = read_num(fin); |
||
if (nc <= 0 || nr <= 0 || maxval <= 0) |
if (nc <= 0 || nr <= 0 || maxval <= 0) |
||
return; |
|||
allocate(nr, nc); |
allocate(nr, nc); |
||
Line 176: | Line 178: | ||
if (count != 3 * nc * nr) |
if (count != 3 * nc * nr) |
||
throw new Exception("Wrong number of items read."); |
throw new Exception("Wrong number of items read."); |
||
bail: // scope(exit) is better |
|||
⚫ | |||
⚫ | |||
} |
} |
||
Line 189: | Line 187: | ||
"Not correct image."); |
"Not correct image."); |
||
} body { |
} body { |
||
auto fout = fopen |
auto fout = fopen(fileName.toStringz(), "wb"); |
||
if (fout == null) |
if (fout == null) |
||
throw new Exception("File can't be opened."); |
throw new Exception("File can't be opened."); |
||
Line 201: | Line 199: | ||
} |
} |
||
struct Col { float r, g, b; } |
|||
alias Tuple!(Col, float, Col, Col[]) Cluster; |
alias Tuple!(Col, float, Col, Col[]) Cluster; |
||
enum Axis { R, G, B } |
enum Axis { R, G, B } |
||
Line 209: | Line 207: | ||
int round(in float x) /*pure*/ nothrow { |
int round(in float x) /*pure*/ nothrow { |
||
return cast(int)floor(x + 0.5); // |
return cast(int)floor(x + 0.5); // Not pure. |
||
} |
} |
||
Ubyte3 roundUbyteRGB(in Col c) /*pure*/ nothrow { |
Ubyte3 roundUbyteRGB(in Col c) /*pure*/ nothrow { |
||
return tuple(cast(ubyte)round(c.r), |
return tuple(cast(ubyte)round(c.r), // Not pure. |
||
cast(ubyte)round(c.g), |
cast(ubyte)round(c.g), |
||
cast(ubyte)round(c.b)); |
cast(ubyte)round(c.b)); |
||
Line 223: | Line 221: | ||
} |
} |
||
immutable Col tot = reduce!addRGB(Col(0,0,0), pxList); |
immutable Col tot = reduce!addRGB(Col(0,0,0), pxList); |
||
immutable int n = walkLength( |
immutable int n = pxList.walkLength(); |
||
return Col(tot.r / n, tot.g / n, tot.b / n); |
return Col(tot.r / n, tot.g / n, tot.b / n); |
||
} |
} |
||
Tuple!(Col, Col) extrems(Col[] lst) pure nothrow { |
Tuple!(Col, Col) extrems(/*in*/ Col[] lst) pure nothrow { |
||
immutable minRGB = Col(float.infinity, |
immutable minRGB = Col(float.infinity, |
||
float.infinity, |
float.infinity, |
||
Line 244: | Line 242: | ||
} |
} |
||
Tuple!(float, Col) volumeAndDims(Col[] lst) pure nothrow { |
Tuple!(float, Col) volumeAndDims(/*in*/ Col[] lst) pure nothrow { |
||
immutable e = extrems(lst); |
immutable e = extrems(lst); |
||
immutable Col r = Col(e[1].r - e[0].r, |
immutable Col r = Col(e[1].r - e[0].r, |
||
Line 275: | Line 273: | ||
Tuple!(Cluster, Cluster) subdivide(in Col c, in float nVolProd, |
Tuple!(Cluster, Cluster) subdivide(in Col c, in float nVolProd, |
||
in Col vol, Col[] pixels) |
in Col vol, Col[] pixels) |
||
/*pure |
/*pure*/ nothrow { |
||
bool delegate(Col c) partFunc; |
bool delegate(Col c) pure nothrow partFunc; |
||
final switch (largestAxis(vol)) { |
final switch (largestAxis(vol)) { |
||
case Axis.R: partFunc = c1 => c1.r < c.r; break; |
case Axis.R: partFunc = c1 => c1.r < c.r; break; |
||
Line 282: | Line 280: | ||
case Axis.B: partFunc = c1 => c1.b < c.b; break; |
case Axis.B: partFunc = c1 => c1.b < c.b; break; |
||
} |
} |
||
Col[] px2 = partition!partFunc(pixels); |
Col[] px2 = partition!partFunc(pixels); // Not pure. |
||
Col[] px1 = pixels[0 .. $ - px2.length]; |
Col[] px1 = pixels[0 .. $ - px2.length]; |
||
return typeof(return)(makeCluster(px1), makeCluster(px2)); |
return typeof(return)(makeCluster(px1), makeCluster(px2)); |
||
} |
} |
||
Image colorQuantize(in Image img, in int n) /*pure |
Image colorQuantize(in Image img, in int n) /*pure*/ nothrow { |
||
immutable int width = img.w; |
immutable int width = img.w; |
||
immutable int height = img.h; |
immutable int height = img.h; |
||
auto cols = new Col[width * height]; |
auto cols = new Col[width * height]; |
||
foreach (i, ref c; cols) |
foreach (immutable i, ref c; cols) |
||
c = Col(img.pix[i * 3 + 0], |
c = Col(img.pix[i * 3 + 0], |
||
img.pix[i * 3 + 1], |
img.pix[i * 3 + 1], |
||
Line 320: | Line 318: | ||
uint[uint] pixMap; // faster than Ubyte3[Ubyte3] |
uint[uint] pixMap; // faster than Ubyte3[Ubyte3] |
||
ubyte[4] u4a, u4b; |
ubyte[4] u4a, u4b; |
||
foreach (cluster; clusters) { |
foreach (const cluster; clusters) { |
||
immutable ubyteMean = ubyte3ToUint(roundUbyteRGB(cluster[0])); |
immutable ubyteMean = ubyte3ToUint(roundUbyteRGB(cluster[0])); |
||
foreach (col; cluster[3]) |
foreach (immutable col; cluster[3]) |
||
pixMap[ubyte3ToUint(roundUbyteRGB(col))] = ubyteMean; |
pixMap[ubyte3ToUint(roundUbyteRGB(col))] = ubyteMean; |
||
} |
} |
||
Line 331: | Line 329: | ||
static Ubyte3 uintToUbyte3(in uint c) pure nothrow { |
static Ubyte3 uintToUbyte3(in uint c) pure nothrow { |
||
Ubyte3 r = void; |
Ubyte3 r = void; |
||
r[0] = |
r[0] = c & 0xFF; |
||
r[1] = (c >> 8) & |
r[1] = (c >> 8) & 0xFF; |
||
r[2] = (c >> 16) & |
r[2] = (c >> 16) & 0xFF; |
||
return r; |
return r; |
||
} |
} |
||
foreach (i; 0 .. height * width) { |
foreach (i; 0 .. height * width) { |
||
immutable |
immutable u3a = Ubyte3(img.pix[i * 3 + 0], |
||
img.pix[i * 3 + 1], |
|||
img.pix[i * 3 + 2]); |
|||
immutable Ubyte3 c = uintToUbyte3(pixMap[ubyte3ToUint(u3a)]); |
immutable Ubyte3 c = uintToUbyte3(pixMap[ubyte3ToUint(u3a)]); |
||
result.pix[i * 3 + 0] = c[0]; |
result.pix[i * 3 + 0] = c[0]; |
||
Line 353: | Line 351: | ||
string fileName; |
string fileName; |
||
int nCols; |
int nCols; |
||
switch (args.length) { |
|||
case 1: |
|||
fileName = "quantum_frog.ppm"; |
|||
nCols = 16; |
|||
break; |
|||
case 3: |
|||
fileName = args[1]; |
|||
} else { |
|||
nCols = to!int(args[2]); |
|||
⚫ | |||
break; |
|||
⚫ | |||
⚫ | |||
return; |
|||
} |
} |
||