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.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 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: | ||
} |
} |
||
RGB roundRGB(in Col c) /*pure*/ nothrow { |
|||
return |
return RGB(cast(ubyte)round(c.r), // Not pure. |
||
cast(ubyte)round(c.g), |
|||
cast(ubyte)round(c.b)); |
|||
} |
} |
||
Line 285: | Line 216: | ||
} |
} |
||
Image colorQuantize(in Image img, in int n) |
Image!RGB colorQuantize(in Image!RGB img, in int n) |
||
/*pure*/ nothrow { |
|||
immutable int width = img.w; |
|||
immutable int |
immutable int width = img.nx; |
||
⚫ | |||
auto cols = new Col[width * height]; |
auto cols = new Col[width * height]; |
||
foreach (immutable i, ref c; |
foreach (immutable i, ref c; img.image) |
||
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 |
static uint RGB2uint(in RGB c) pure nothrow { |
||
uint r; |
uint r; |
||
r |= c |
r |= c.r; |
||
r |= c |
r |= c.g << 8; |
||
r |= c |
r |= c.b << 16; |
||
return r; |
return r; |
||
} |
} |
||
uint[uint] pixMap; // faster than |
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 = |
immutable ubyteMean = RGB2uint(roundRGB(cluster[0])); |
||
foreach (immutable col; cluster[3]) |
foreach (immutable col; cluster[3]) |
||
pixMap[ |
pixMap[RGB2uint(roundRGB(col))] = ubyteMean; |
||
} |
} |
||
auto result = new Image; |
auto result = new Image!RGB; |
||
result.allocate(height, width); |
result.allocate(height, width); |
||
static |
static RGB uintToRGB(in uint c) pure nothrow { |
||
return RGB( c & 0xFF, |
|||
(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 = |
immutable u3a = RGB(img.image[i].r, |
||
img.image[i].g, |
|||
img.image[i].b); |
|||
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(" |
imq.savePPM6("quantum_frog_quantized.ppm"); |
||
}</lang> |
}</lang> |
||