/* ichika4.c - Don Yang (uguu.org) 11/20/04 */ #include #include #define SHIFT_HUE 2.0f #define SHIFT_SAT 0.70f #define SHIFT_VAL 0.09f #define EYE_HUE_MIN 3.60f #define EYE_HUE_MAX 4.24f #define EYE_SAT1 0.65f #define EYE_VAL1 0.42f #define EYE_SAT2 0.36f #define EYE_VAL2 0.60f #define EYE_THRESHOLD 0.6f #define EYE_SCALE_VAL 1.29f #define FACE_HUE1_MIN 0.30f #define FACE_HUE1_MAX 0.45f #define FACE_HUE2_MIN 4.28f #define FACE_HUE2_MAX 6.00f #define HAIR_HUE_MIN 0.0f #define HAIR_HUE_MAX 0.1f typedef float flt; unsigned char *image, *mask, *m, *u, c; int width, height, size, align, headersize, i, j, k; flt r, g, b, h, s, v, r0, g0, b0, d1, d2, f, p, q, t; char header[64], *errstr[] = {"read error", "write error", "invalid image", "out of memory"}; FILE *file; void rgb2hsv() { p = q = r; if( p > g ) p = g; if( q < g ) q = g; if( p > b ) p = b; if( q < b ) q = b; h = s = v = 0.0f; if( p != q ) { p = q - p; h = (r == q) ? ((g - b) / p) : (g == q) ? ((b - r) / p + 2.0f) : ((r - g) / p + 4.0f); s = p / (v = q); } } int CheckNeighbors0(int offset) { if( offset < width || offset > size - width || !(offset % width) || !((offset + 1) % width) ) return 1; r = (flt)*(image + offset * 3) / 255.0f; g = (flt)*(image + offset * 3 + 1) / 255.0f; b = (flt)*(image + offset * 3 + 2) / 255.0f; rgb2hsv(); return ((FACE_HUE1_MIN < h && h < FACE_HUE1_MAX) || (FACE_HUE2_MIN < h && h < FACE_HUE2_MAX) || (HAIR_HUE_MIN < h && h < HAIR_HUE_MAX)) ? 0 : CheckNeighbors0(offset + k); } int ErrorMsg() { if( file ) fclose(file); return puts(errstr[k]); } void Erase(int offset) { if( mask[offset] - 255 ) { mask[offset] = 0xff; if( (offset % width) < width - 1 ) Erase(offset + 1); if( (offset / width) < height - 1 ) Erase(offset + width); if( offset % width ) Erase(offset - 1); if( offset / width ) Erase(offset - width); } } void ReorderBytes() { u = image - 1; for(i = 0; i < size; i++) { c = *++u; *u = *(u + 2); *(u += 2) = c; } } int main(int argc, char **argv) { if( argc < 3 ) return printf("%s \n", *argv); k = width = 0; if( !(file = fopen(*++argv, "rb")) ) return ErrorMsg(); image = NULL; if( fread(header, 64, 1, file) - 1 ) return ErrorMsg(); k += 2; if( *header == 'B' && header[1] == 'M' ) { if( *(int*)(header + 28) - 24 ) return ErrorMsg(); width = *(int*)(header + 18); height = *(int*)(header + 22); align = (4 - ((width * 3) & 3)) & 3; headersize = 54; } else if( *header == 'P' && header[1] == '6' ) { for(i = j = headersize = 0; i < 63; i++) if( header[i] == '\n' ) if( ++j == 3 ) { header[headersize = ++i] = '\0'; i = 64; } if( !headersize || (sscanf(header, "P6\n%d %d\n", &width, &height) - 2) ) return ErrorMsg(); align = 0; } if( width < 16 || height < 16 ) return ErrorMsg(); k++; if( !(image = (unsigned char*)malloc((size = width * height) * 3)) ) return ErrorMsg(); u = image; for(fseek(file, headersize, i = 0); i < height; i++) { fread(u, width * 3, 1, file); u += width * 3; for(j = 0; j++ < align; fgetc(file)); } fclose(file); file = NULL; if( *header == 'B' ) ReorderBytes(); if( !(m = mask = (unsigned char*)malloc(size)) ) return ErrorMsg(); u = image; for(j = 0; j < size; j++) { r = (flt)*(u++) / 255.0f; g = (flt)*(u++) / 255.0f; b = (flt)*(u++) / 255.0f; rgb2hsv(); *m = 0xff; if( EYE_HUE_MIN < h && h < EYE_HUE_MAX ) { d1 = (s-EYE_SAT1)*(s-EYE_SAT1) + (v-EYE_VAL1)*(v-EYE_VAL1) * EYE_SCALE_VAL; d2 = (s-EYE_SAT2)*(s-EYE_SAT2) + (v-EYE_VAL2)*(v-EYE_VAL2) * EYE_SCALE_VAL; if( d1 < EYE_THRESHOLD || d2 < EYE_THRESHOLD ) *m = (unsigned char)(255.0f * ((d1 < EYE_THRESHOLD) ? (d1 / EYE_THRESHOLD) : (d2 / EYE_THRESHOLD))); } m++; } for(i = j = k = 0; j < width;) { Erase(j); Erase(j + width); Erase(j + size - width * 2); Erase(j++ + size - width); } for(; k < height;) { Erase(k * width); Erase(k * width + 1); Erase((k + 1) * width - 2); Erase(++k * width - 1); } for(m = mask; i < size; i++) { if( *m - 255 ) if( (CheckNeighbors0(i + (k = 1)) ? 1 : CheckNeighbors0(i + (k = -1)) ? 1 : CheckNeighbors0(i + (k = width)) ? 1 : CheckNeighbors0(i + (k = -width)) ? 1 : 0) ) Erase(i); m++; } u = image; m = mask; for(i = 0; i < size; i++) { if( *m - 255 ) { d1 = (flt)*m / 255.0f; d2 = 1.0f - d1; r = r0 = (flt)*u / 255.0f; g = g0 = (flt)*(u+1) / 255.0f; b = b0 = (flt)*(u+2) / 255.0f; rgb2hsv(); if( (h += SHIFT_HUE) >= 6.0f ) h -= 6.0f; if( (s += SHIFT_SAT) > 1.0f ) s = 1.0f; if( (v += SHIFT_VAL) > 1.0f ) v = 1.0f; k = (int)h; f = h - (flt)k; p = v * (1.0f - s); q = v * (1.0f - s * f); t = v * (1.0f - s * (1.0f - f)); r = (k < 4) ? (k < 2) ? k ? q : v : p : (k > 4) ? v : t; g = (k < 3) ? k ? v : t : k > 3 ? p : q; b = (k < 3) ? (k > 1) ? t : p : (k > 4) ? q : v; r = r0 * d1 + r * d2; g = r0 * d1 + g * d2; b = r0 * d1 + b * d2; *u = (unsigned char)(r * 255.0f); u[1] = (unsigned char)(g * 255.0f); u[2] = (unsigned char)(b * 255.0f); } u += 3; m++; } free(mask); if( *header == 'B' ) ReorderBytes(); k = 1; if( !(file = fopen(*++argv, "wb+")) ) return ErrorMsg(); fwrite(header, headersize, 1, file); u = image; for(j = 0; j < height; j++) { fwrite(u, width * 3, 1, file); for(k = 0; k < align; k++) fputc(0, file); u += width * 3; } fclose(file); free(image); return 0; }