Skip to content

Instantly share code, notes, and snippets.

@jbedo
Created March 20, 2011 09:35
Show Gist options
  • Save jbedo/878239 to your computer and use it in GitHub Desktop.
Save jbedo/878239 to your computer and use it in GitHub Desktop.
Adjusts image contrast
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
static double Ptop = 0.995;
static double Pbot = 0.01;
typedef struct range range;
struct range{
uchar min, max;
double scale;
};
void
usage(void)
{
fprint(2, "usage: %s [-h high] [-l low]\n", argv0);
exits("usage");
}
void *
emalloc(ulong sz)
{
void *ret;
if((ret = malloc(sz)) == nil)
sysfatal("emalloc: %r");
return ret;
}
void
swap(uchar *a, uchar *b)
{
uchar tmp;
tmp = *b;
*b = *a;
*a = tmp;
}
/* Radix sort */
void
radix(uchar *buf, int len, int bit)
{
int u, v;
if(bit < 0 || len <= 0)
return;
u = 0;
v = len - 1;
for(;;){
for(;u < len && !((buf[u] >> bit) & 0x01); u++);
for(;v > u && ((buf[v] >> bit) & 0x01); v--);
if(u >= v)
break;
swap(&buf[u], &buf[v]);
}
radix(buf, u, bit - 1);
radix(&buf[u], len - u, bit - 1);
}
void
endpoints(uchar **sl, uint ysz, uint xsz, range *ends)
{
uchar *buf;
int i, len;
buf = emalloc((len = ysz * xsz) * sizeof(*buf));
for(i = 0; i < ysz; i++)
memcpy(&buf[i * xsz], &sl[i], sizeof(*buf) * xsz);
radix(buf, len, 8 * sizeof(*buf) - 1);
ends->min = buf[(uint)(Pbot * (double)len)];
ends->max = buf[(uint)(Ptop * (double)len)];
ends->scale = 256/((double)ends->max - (double)ends->min);
free(buf);
}
void
relevelc(uchar *in, uint sz, range *ends, uchar *out)
{
int i;
double op;
for(i = 0; i < sz; i++){
out[i] = (in[i] < ends->min) ? 0 : (in[i] - ends->min);
op = (double)in[i] * ends->scale;
out[i] = (op > 255) ? 255 : ((uchar)op);
}
}
Memimage*
relevel(Memimage *m)
{
int i, j, bpl;
Memimage *new;
uchar **oscan, **nscan;
range ends;
new = allocmemimage(Rect(0, 0, Dx(m->r), Dy(m->r)), m->chan);
if(new == nil)
sysfatal("can't allocate new image: %r");
oscan = emalloc(Dy(m->r) * sizeof(uchar *));
nscan = emalloc(Dy(m->r) * sizeof(uchar *));
/* unload original image into scan lines */
bpl = bytesperline(m->r, m->depth);
for(i = 0; i < Dy(m->r); i++){
oscan[i] = emalloc(bpl);
j = unloadmemimage(m, Rect(m->r.min.x, m->r.min.y+i, m->r.max.x, m->r.min.y + i + 1), oscan[i], bpl);
if(j != bpl)
sysfatal("unloadmemimage");
}
/* allocate scan lines for destination */
for(i = 0; i< Dy(m->r); i++)
nscan[i] = emalloc(bpl);
/* relevel */
endpoints(oscan, Dy(m->r), bpl, &ends);
for(i = 0; i < Dy(m->r); i++)
relevelc(oscan[i], bpl, &ends, nscan[i]);
/* pack data into destination */
bpl = bytesperline(new->r, m->depth);
for(i = 0; i < Dy(m->r); i++){
j = loadmemimage(new, Rect(0, i, Dx(m->r), i + 1), nscan[i], bpl);
if(j != bpl)
sysfatal("loadmemimage: %r");
}
return new;
}
void
main(int argc, char *argv[])
{
Rectangle rparam;
Memimage *m, *new;
char tmp[100];
memimageinit();
memset(&rparam, 0, sizeof rparam);
ARGBEGIN{
case 'h': /* high threshold */
Ptop = 1 - atof(EARGF(usage()));
break;
case 'l': /* low threshold */
Pbot = atof(EARGF(usage()));
break;
default:
usage();
break;
}ARGEND
if(Ptop <= 0.5 || Ptop > 1)
sysfatal("High threshold must be in the range [0, 0.5]");
if(Pbot < 0 || Pbot > 0.5)
sysfatal("Low threshold must b in the range [0, 0.5)");
if(Ptop < Pbot)
sysfatal("Low threshold must be less than high threshold");
m = readmemimage(0);
if(m == nil)
sysfatal("can't load image: %r");
new = nil;
switch(m->chan){
case GREY8:
case RGB24:
new = relevel(m);
break;
default:
sysfatal("can't handle channel type %s", chantostr(tmp, m->chan));
}
assert(new);
if(writememimage(1, new) < 0)
sysfatal("write error on output: %r");
exits(nil);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment