Color quantization: Difference between revisions

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