void filterExpand(Img *dst, Img *src, int l, int r, int t, int b) {
if (!src->data) return;
if (l < 0) l = 0;
if (r < 0) r = 0;
if (t < 0) t = 0;
if (b < 0) b = 0;
int w = src->w + l + r;
int h = src->h + t + b;
if (dst->w != w || dst->h != h) imgInit(dst, w, h);
double *ps = src->data, *pd = dst->data + w*t*4;
for(int y = 0; y < src->h; ++y) {
memcpy(pd, ps, 4*sizeof(double)*src->w);
for(double *p = ps, *e = p + 4*l; p < e; p += 4)
memcpy(p, pd, 4*sizeof(double));
ps += (src->w-1)*4;
pd += w*4;
for(double *e = pd, *p = e - r*4; p < e; p += 4)
memcpy(p, ps, 4*sizeof(double));
ps += 4;
}
for(int y = 0; y < t; ++y)
memcpy(imgPixel(dst, 0, y), imgPixel(dst, 0, t), 4*sizeof(double)*w);
for(int y = h-b; y < h; ++y)
memcpy(imgPixel(dst, 0, y), imgPixel(dst, 0, h-b-1), 4*sizeof(double)*w);
}
void filterArea(Img *dst, Img *src) {
int w = dst->w > src->w ? dst->w : src->w;
int h = dst->h > src->h ? dst->h : src->h;
if (dst->w < w || dst->h < h)
imgInit(dst, w, h);
int gap = (w - src->w)*4;
double *pd = dst->data, *ps = src->data;
memcpy(pd, ps, 4*sizeof(*ps)); ps += 4; pd += 4;
for(double *e = ps + 4*(src->w - 1); ps < e; ++ps, ++pd)
*pd = *ps + *(pd - 4);
pd += gap;
for(int y = 1; y < src->h; ++y) {
for(int i = 0; i < 4; ++i, ++ps, ++pd)
*pd = *ps + *(pd - w*4);
for(double *e = ps + 4*(src->w - 1); ps < e; ++ps, ++pd)
*pd = *ps + *(pd - 4) + *(pd - w*4) - *(pd - (w+1)*4);
pd += gap;
}
}
void pixelAreaAvg(double *pixel, Img *areaImg, int x0, int y0, int x1, int y1) {
if (x0 < 0) x0 = 0;
if (y0 < 0) y0 = 0;
if (x1 >= areaImg->w) x1 = areaImg->w - 1;
if (y1 >= areaImg->h) y1 = areaImg->h - 1;
if (x1 < x0 || y1 < y0) { memset(pixel, 0, sizeof(*pixel)*4); return; }
double k = 1.0/((x1 - x0 + 1)*(y1 - y0 + 1));
if (x0) {
if (y0) {
--x0; --y0;
double *p00 = imgPixel(areaImg, x0, y0);
double *p01 = imgPixel(areaImg, x0, y1);
double *p10 = imgPixel(areaImg, x1, y0);
double *p11 = imgPixel(areaImg, x1, y1);
for(int i = 0; i < 4; ++i)
pixel[i] = (p00[i] + p11[i] - p01[i] - p10[i])*k;
return;
}
double *p0 = imgPixel(areaImg, x0-1, y1);
double *p1 = imgPixel(areaImg, x1, y1);
for(int i = 0; i < 4; ++i)
pixel[i] = (p1[i] - p0[i])*k;
} else
if (y0) {
double *p0 = imgPixel(areaImg, x1, y0-1);
double *p1 = imgPixel(areaImg, x1, y1);
for(int i = 0; i < 4; ++i)
pixel[i] = (p1[i] - p0[i])*k;
} else {
double *p = imgPixel(areaImg, x1, y1);
for(int i = 0; i < 4; ++i)
pixel[i] = p[i]*k;
}
}
void filterBW(Img *img, int size, double k) {
if (!img->data) return;
Img area = {};
filterArea(&area, img);
char *res = malloc(img->w * img->h);
double sum[2][4] = {};
int count[2] = {};
k *= 3;
double avg[4] = {};
char *r = res;
for(int y = 0; y < img->h; ++y) {
for(int x = 0; x < img->w; ++x) {
pixelAreaAvg(avg, &area, x - size, y - size, x + size, y + size);
double a = avg[0] + avg[1] + avg[2];
double *c = imgPixel(img, x, y);
double cb = c[0] + c[1] + c[2];
int value = (cb > a + k);
for(int i = 0; i < 4; ++i)
sum[value][i] += c[i];
++count[value];
*r++ = value;
}
}
imgDestroy(&area);
for(int j = 0; j < 2; ++j)
if (count[j])
for(int i = 0; i < 4; ++i)
sum[j][i] /= count[j];
double *p = img->data;
for(char *r = res, *e = r + img->w*img->h; r < e; ++r, p += 4)
memcpy(p, sum[(int)*r], sizeof(*sum));
free(res);
}
int bwGenerate(int argc, char **argv) {
char *inFile = "", *outFile = "";
double sub = 1.0/32;
double k = -0.1;
ArgDesc descs[] = {
{ 'i', &inFile, &pargString, "path to input image" },
{ 'o', &outFile, &pargString, "path to output image" },
{ 's', &sub, &pargDouble, "subrect size (relative to image size, default: 0.03)" },
{ 'k', &k, &pargDouble, "threshold shift (default: -0.1)" },
{}
};
if (!parseArgs(descs, argc, argv) || !inFile[0] || !outFile[0]) {
printUsage(descs);
return FALSE;
}
Img img = {};
if (!imgLoad(&img, inFile))
return FALSE;
filterBW(&img, (img.w + img.h)*sub/4, k);
if (!imgSave(&img, outFile)) {
imgDestroy(&img);
return FALSE;
}
imgDestroy(&img);
return TRUE;
}