/*=============================================================================
This file contains fuzzy color matching code.
It is all based on algorithms and methods by Kenan Kalajdzic
<kenan@unix.ba> in 2006.
=============================================================================*/
#include "netpbm/pm_c_util.h"
#include "netpbm/nstring.h"
#include "ppm.h"
typedef double fzLog;
/* fuzzy logic truth value */
/*----------------------------------------------------------------------------
Member functions
-----------------------------------------------------------------------------*/
static fzLog
memberS(fzLog const x1,
fzLog const x2,
fzLog const x) {
fzLog retval;
if (x <= x1)
retval = 0;
else if (x > x1 && x <= x2)
retval = (x - x1) / (x2 - x1);
else
retval = 1;
return retval;
}
static fzLog
memberZ(fzLog const x1,
fzLog const x2,
fzLog const x) {
fzLog retval;
if (x <= x1)
retval = 1;
else if (x > x1 && x <= x2)
retval = (x2 - x) / (x2 - x1);
else
retval = 0;
return retval;
}
static fzLog
memberTrapez(fzLog const x1,
fzLog const x2,
fzLog const x3,
fzLog const x4,
fzLog const x) {
fzLog retval;
if (x <= x1 || x > x4)
retval = 0;
else if (x > x1 && x <= x2)
retval = (x - x1) / (x2 - x1);
else if (x > x2 && x <= x3)
retval = 1;
else
retval = (x4 - x) / (x4 - x3);
return retval;
}
/*----------------------------------------------------------------------------
Hue membership functions
-----------------------------------------------------------------------------*/
static fzLog
hueIsAround000(double const hue) {
return memberZ(10, 20, hue);
}
static fzLog
hueIsAround015(double const hue) {
return memberZ(20, 40, hue);
}
static fzLog
hueIsAround030(double const hue) {
return memberTrapez(10, 20, 40, 60, hue);
}
static fzLog
hueIsAround060(double const hue) {
return memberTrapez(40, 50, 60, 70, hue);
}
static fzLog
hueIsAround120(double const hue) {
return memberTrapez(60, 70, 150, 180, hue);
}
static fzLog
hueIsAround180(double const hue) {
return memberTrapez(150, 180, 240, 260, hue);
}
static fzLog
hueIsAround270(double const hue) {
return memberTrapez(240, 260, 290, 310, hue);
}
static fzLog
hueIsAround320(double const hue) {
return memberTrapez(290, 310, 320, 350, hue);
}
static fzLog
hueIsAround360(double const hue) {
return memberS(320, 350, hue);
}
/*----------------------------------------------------------------------------
Saturation membership functions
-----------------------------------------------------------------------------*/
static fzLog
satIsVeryLow(double const sat) {
return memberZ(0.03, 0.08, sat);
}
static fzLog
satIsLow(double const sat) {
return memberTrapez(0.03, 0.08, 0.17, 0.2, sat);
}
static fzLog
satIsMedium(double const sat) {
return memberTrapez(0.17, 0.2, 0.6, 0.8, sat);
}
static fzLog
satIsHigh(double const sat) {
return memberS(0.6, 0.8, sat);
}
/*----------------------------------------------------------------------------
Value membership functions
-----------------------------------------------------------------------------*/
static fzLog
valIsVeryLow(double const val) {
return memberZ(0.05, 0.2, val);
}
static fzLog
valIsLow(double const val) {
return memberTrapez(0.05, 0.2, 0.25, 0.3, val);
}
static fzLog
valIsMedium(double const val) {
return memberTrapez(0.25, 0.3, 0.6, 0.7, val);
}
static fzLog
valIsHigh(double const val) {
return memberTrapez(0.6, 0.7, 0.95, 0.97, val);
}
static fzLog
valIsVeryHigh(double const val) {
return memberS(0.95, 0.97, val);
}
/*----------------------------------------------------------------------------
Fuzzy logic functions
-----------------------------------------------------------------------------*/
static fzLog
fzAnd(fzLog const opLeft,
fzLog const opRight) {
return MIN(opLeft, opRight);
}
static fzLog
fzOr(fzLog const opLeft,
fzLog const opRight) {
return MAX(opLeft, opRight);
}
static fzLog
fzNot(fzLog const op) {
return 1.0 - op;
}
/*----------------------------------------------------------------------------
Fuzzy color matching
-----------------------------------------------------------------------------*/
static void
matchBk(pixel const color,
pixval const maxval,
fzLog (* const bkMatchP)[BKCOLOR_COUNT]) {
struct hsv const hsv = ppm_hsv_from_color(color, maxval);
fzLog const satVeryLow = satIsVeryLow(hsv.s);
fzLog const satLow = satIsLow(hsv.s);
fzLog const satMedium = satIsMedium(hsv.s);
fzLog const satHigh = satIsHigh(hsv.s);
fzLog const valVeryLow = valIsVeryLow(hsv.v);
fzLog const valLow = valIsLow(hsv.v);
fzLog const valMedium = valIsMedium(hsv.v);
fzLog const valHigh = valIsHigh(hsv.v);
fzLog const valVeryHigh = valIsVeryHigh(hsv.v);
fzLog const hueAround000 = hueIsAround000(hsv.h);
fzLog const hueAround015 = hueIsAround015(hsv.h);
fzLog const hueAround030 = hueIsAround030(hsv.h);
fzLog const hueAround060 = hueIsAround060(hsv.h);
fzLog const hueAround120 = hueIsAround120(hsv.h);
fzLog const hueAround180 = hueIsAround180(hsv.h);
fzLog const hueAround270 = hueIsAround270(hsv.h);
fzLog const hueAround320 = hueIsAround320(hsv.h);
fzLog const hueAround360 = hueIsAround360(hsv.h);
(*bkMatchP)[BKCOLOR_BLACK] =
fzAnd(fzOr(satVeryLow, satLow), fzOr(valVeryLow, valLow));
(*bkMatchP)[BKCOLOR_GRAY] =
fzAnd(satVeryLow, fzAnd(fzNot(valVeryLow), fzNot(valVeryHigh)));
(*bkMatchP)[BKCOLOR_WHITE] =
fzAnd(satVeryLow, valVeryHigh);
(*bkMatchP)[BKCOLOR_RED] =
fzAnd(fzAnd(fzOr(hueAround000, hueAround360), fzNot(satVeryLow)),
fzOr(valMedium, valHigh)
);
(*bkMatchP)[BKCOLOR_ORANGE] =
fzAnd(fzAnd(hueAround030, fzOr(satMedium, satHigh)),
fzOr(fzOr(valMedium, valHigh), valVeryHigh)
);
(*bkMatchP)[BKCOLOR_YELLOW] =
fzAnd(fzAnd(hueAround060, fzOr(satMedium, satHigh)),
fzOr(valHigh, valVeryHigh)
);
(*bkMatchP)[BKCOLOR_GREEN] =
fzAnd(fzAnd(hueAround120, fzOr(satMedium, satHigh)),
fzAnd(fzNot(valVeryLow), fzNot(valLow))
);
(*bkMatchP)[BKCOLOR_BLUE] =
fzAnd(fzAnd(hueAround180, fzNot(satVeryLow)),
fzNot(valVeryLow)
);
(*bkMatchP)[BKCOLOR_VIOLET] =
fzAnd(fzAnd(hueAround270, fzOr(satMedium, satHigh)),
fzOr(valMedium, valHigh)
);
(*bkMatchP)[BKCOLOR_PURPLE] =
fzAnd(fzAnd(hueAround320, fzOr(satMedium, satHigh)),
fzOr(valMedium, valHigh)
);
(*bkMatchP)[BKCOLOR_BROWN] =
fzOr(
fzAnd(fzOr(hueAround015, hueAround360),
fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))),
fzAnd(hueAround015, satLow)
);
}
bk_color
ppm_bk_color_from_color(pixel const color,
pixval const maxval) {
fzLog bkmatch[BKCOLOR_COUNT];
bk_color i;
bk_color bestSoFar;
fzLog bestMatch;
matchBk(color, maxval, &bkmatch);
for (i = 0, bestSoFar = 0, bestMatch = 0.0; i < BKCOLOR_COUNT; ++i) {
if (bkmatch[i] > bestMatch) {
bestSoFar = i;
bestMatch = bkmatch[i];
}
}
return bestSoFar;
}
static pixel const bkColorMap[BKCOLOR_COUNT] = {
{174, 174, 174}, /* BKCOLOR_GRAY */
{128, 42, 42}, /* BKCOLOR_BROWN */
{255, 128, 0}, /* BKCOLOR_ORANGE */
{255, 0, 0}, /* BKCOLOR_RED */
{255, 255, 0}, /* BKCOLOR_YELLOW */
{ 0, 255, 0}, /* BKCOLOR_GREEN */
{ 0, 0, 255}, /* BKCOLOR_BLUE */
{143, 94, 153}, /* BKCOLOR_VIOLET */
{160, 32, 240}, /* BKCOLOR_PURPLE */
{255, 255, 255}, /* BKCOLOR_WHITE */
{ 0, 0, 0} /* BKCOLOR_BLACK */
};
pixel
ppm_color_from_bk_color(bk_color const bkColor,
pixval const maxval) {
pixel const color255 = bkColorMap[bkColor];
pixel retval;
if (maxval != 255) {
PPM_DEPTH(retval, color255, 255, maxval);
} else
retval = color255;
return retval;
}
static const char * const bkColorNameMap[BKCOLOR_COUNT] = {
"gray",
"brown",
"orange",
"red",
"yellow",
"green",
"blue",
"violet",
"purple",
"white",
"black"
};
bk_color
ppm_bk_color_from_name(const char * const name) {
bk_color i;
for (i = 0; i < BKCOLOR_COUNT; ++i) {
if (streq(name, bkColorNameMap[i]))
return i;
}
pm_error("Invalid Berlin-Kay color name: '%s'", name);
return 0; /* quiet compiler warning */
}
const char *
ppm_name_from_bk_color(bk_color const bkColor) {
if (bkColor >= BKCOLOR_COUNT)
pm_error("Invalid color passed to name_from_bk_color(): %u",
bkColor);
return bkColorNameMap[bkColor];
}