Color quantization: Difference between revisions

Content added Content deleted
(Updated D entry)
(Updated and shorter D entry)
Line 129: Line 129:
=={{header|D}}==
=={{header|D}}==
{{trans|OCaml}}
{{trans|OCaml}}
This code retains some of the style of the original OCaml code.
This code retains some of 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.ascii, std.algorithm,
<lang d>import core.stdc.stdio, std.stdio, std.ascii, std.algorithm, std.math,
std.typecons, std.math, std.range, std.conv, std.string;
std.typecons, std.range, std.conv, std.string, bitmap;

final class Image {
int w, h;
ubyte[] pix;

void allocate(in int nr, in int nc) pure nothrow {
w = nc;
h = nr;
this.pix.length = 3 * this.w * this.h;
}

void loadPPM6(in string fileName) {
scope(exit) if (fin) fclose(fin);

static int read_num(FILE* f) nothrow {
int n;
while (!fscanf(f, "%d ", &n)) {
if ((n = fgetc(f)) == '#') {
while ((n = fgetc(f)) != '\n')
if (n == EOF)
return 0;
} else
return 0;
}
return n;
}

auto fin = fopen(fileName.toStringz(), "rb");
if (!fin)
return;

if (fgetc(fin) != 'P' ||
fgetc(fin) != '6' ||
!isWhite(fgetc(fin)))
return;

immutable int nc = read_num(fin);
immutable int nr = read_num(fin);
immutable int maxval = read_num(fin);
if (nc <= 0 || nr <= 0 || maxval <= 0)
return;
allocate(nr, nc);

immutable count = fread(this.pix.ptr, 1, 3 * nc * nr, fin);
if (count != 3 * nc * nr)
throw new Exception("Wrong number of items read.");
}

void savePPM6(in string fileName) const
in {
assert(!fileName.empty);
assert(this.w > 0 && this.h > 0 &&
pix.length == (3 * this.w * this.h),
"Not correct image.");
} body {
auto fout = fopen(fileName.toStringz(), "wb");
if (fout == null)
throw new Exception("File can't be opened.");
fprintf(fout, "P6\n%d %d\n255\n", this.w, this.h);
immutable count = fwrite(this.pix.ptr, 1,
3 * this.w * this.h, fout);
if (count != 3 * this.w * this.h)
new Exception("Wrong number of items written.");
fclose(fout);
}
}


struct Col { float r, g, b; }
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 }

// ubyte[3] causes slow heap allocations
alias Tuple!(ubyte, ubyte, ubyte) Ubyte3;


int round(in float x) /*pure*/ nothrow {
int round(in float x) /*pure*/ nothrow {
Line 210: Line 141:
}
}


Ubyte3 roundUbyteRGB(in Col c) /*pure*/ nothrow {
RGB roundRGB(in Col c) /*pure*/ nothrow {
return tuple(cast(ubyte)round(c.r), // Not pure.
return RGB(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 285: Line 216:
}
}


Image colorQuantize(in Image img, in int n) /*pure*/ nothrow {
Image!RGB colorQuantize(in Image!RGB img, in int n)
/*pure*/ nothrow {
immutable int width = img.w;
immutable int height = img.h;
immutable int width = img.nx;
immutable int height = img.ny;


auto cols = new Col[width * height];
auto cols = new Col[width * height];
foreach (immutable i, ref c; cols)
foreach (immutable i, ref c; img.image)
c = Col(img.pix[i * 3 + 0],
cols[i] = Col(c.r, c.g, c.b);
img.pix[i * 3 + 1],
img.pix[i * 3 + 2]);
Cluster[] clusters = [makeCluster(cols)];
Cluster[] clusters = [makeCluster(cols)];


Line 308: Line 238:
}
}


static uint ubyte3ToUint(in Ubyte3 c) pure nothrow {
static uint RGB2uint(in RGB c) pure nothrow {
uint r;
uint r;
r |= c[0];
r |= c.r;
r |= c[1] << 8;
r |= c.g << 8;
r |= c[2] << 16;
r |= c.b << 16;
return r;
return r;
}
}


uint[uint] pixMap; // faster than Ubyte3[Ubyte3]
uint[uint] pixMap; // faster than RGB[RGB]
ubyte[4] u4a, u4b;
ubyte[4] u4a, u4b;
foreach (const cluster; clusters) {
foreach (const cluster; clusters) {
immutable ubyteMean = ubyte3ToUint(roundUbyteRGB(cluster[0]));
immutable ubyteMean = RGB2uint(roundRGB(cluster[0]));
foreach (immutable col; cluster[3])
foreach (immutable col; cluster[3])
pixMap[ubyte3ToUint(roundUbyteRGB(col))] = ubyteMean;
pixMap[RGB2uint(roundRGB(col))] = ubyteMean;
}
}


auto result = new Image;
auto result = new Image!RGB;
result.allocate(height, width);
result.allocate(height, width);


static Ubyte3 uintToUbyte3(in uint c) pure nothrow {
static RGB uintToRGB(in uint c) pure nothrow {
Ubyte3 r = void;
return RGB( c & 0xFF,
r[0] = c & 0xFF;
(c >> 8) & 0xFF,
r[1] = (c >> 8) & 0xFF;
(c >> 16) & 0xFF);
r[2] = (c >> 16) & 0xFF;
return r;
}
}


foreach (i; 0 .. height * width) {
foreach (immutable i; 0 .. height * width) {
immutable u3a = Ubyte3(img.pix[i * 3 + 0],
immutable u3a = RGB(img.image[i].r,
img.pix[i * 3 + 1],
img.image[i].g,
img.pix[i * 3 + 2]);
img.image[i].b);
immutable Ubyte3 c = uintToUbyte3(pixMap[ubyte3ToUint(u3a)]);
result.image[i] = uintToRGB(pixMap[RGB2uint(u3a)]);
result.pix[i * 3 + 0] = c[0];
result.pix[i * 3 + 1] = c[1];
result.pix[i * 3 + 2] = c[2];
}
}


Line 365: Line 290:
}
}


auto im = new Image;
auto im = new Image!RGB;
im.loadPPM6(fileName);
im.loadPPM6(fileName);
const imq = colorQuantize(im, nCols);
const imq = colorQuantize(im, nCols);
imq.savePPM6("out.ppm");
imq.savePPM6("quantum_frog_quantized.ppm");
}</lang>
}</lang>