Merge pull request #4004 from radarhere/hsv

Improved HSV conversion
This commit is contained in:
Andrew Murray 2019-09-07 00:50:34 +10:00 committed by GitHub
commit 14859ce506
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 147 additions and 37 deletions

View File

@ -23,6 +23,7 @@ class TestImageConvert(PillowTestCase):
"RGBX", "RGBX",
"CMYK", "CMYK",
"YCbCr", "YCbCr",
"HSV",
) )
for mode in modes: for mode in modes:

View File

@ -101,6 +101,19 @@ bit2ycbcr(UINT8* out, const UINT8* in, int xsize)
} }
} }
static void
bit2hsv(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, out += 4) {
UINT8 v = (*in++ != 0) ? 255 : 0;
out[0] = 0;
out[1] = 0;
out[2] = v;
out[3] = 255;
}
}
/* ----------------- */ /* ----------------- */
/* RGB/L conversions */ /* RGB/L conversions */
/* ----------------- */ /* ----------------- */
@ -175,6 +188,19 @@ l2rgb(UINT8* out, const UINT8* in, int xsize)
} }
} }
static void
l2hsv(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, out += 4) {
UINT8 v = *in++;
out[0] = 0;
out[1] = 0;
out[2] = v;
out[3] = 255;
}
}
static void static void
la2l(UINT8* out, const UINT8* in, int xsize) la2l(UINT8* out, const UINT8* in, int xsize)
{ {
@ -196,6 +222,19 @@ la2rgb(UINT8* out, const UINT8* in, int xsize)
} }
} }
static void
la2hsv(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
UINT8 v = in[0];
out[0] = 0;
out[1] = 0;
out[2] = v;
out[3] = in[3];
}
}
static void static void
rgb2bit(UINT8* out, const UINT8* in, int xsize) rgb2bit(UINT8* out, const UINT8* in, int xsize)
{ {
@ -283,54 +322,58 @@ rgb2bgr24(UINT8* out, const UINT8* in, int xsize)
} }
static void static void
rgb2hsv(UINT8* out, const UINT8* in, int xsize) rgb2hsv_row(UINT8* out, const UINT8* in)
{ // following colorsys.py { // following colorsys.py
float h,s,rc,gc,bc,cr; float h,s,rc,gc,bc,cr;
UINT8 maxc,minc; UINT8 maxc,minc;
UINT8 r, g, b; UINT8 r, g, b;
UINT8 uh,us,uv; UINT8 uh,us,uv;
int x;
for (x = 0; x < xsize; x++, in += 4) { r = in[0];
r = in[0]; g = in[1];
g = in[1]; b = in[2];
b = in[2]; maxc = MAX(r,MAX(g,b));
minc = MIN(r,MIN(g,b));
maxc = MAX(r,MAX(g,b)); uv = maxc;
minc = MIN(r,MIN(g,b)); if (minc == maxc){
uv = maxc; uh = 0;
if (minc == maxc){ us = 0;
*out++ = 0; } else {
*out++ = 0; cr = (float)(maxc-minc);
*out++ = uv; s = cr/(float)maxc;
rc = ((float)(maxc-r))/cr;
gc = ((float)(maxc-g))/cr;
bc = ((float)(maxc-b))/cr;
if (r == maxc) {
h = bc-gc;
} else if (g == maxc) {
h = 2.0 + rc-bc;
} else { } else {
cr = (float)(maxc-minc); h = 4.0 + gc-rc;
s = cr/(float)maxc;
rc = ((float)(maxc-r))/cr;
gc = ((float)(maxc-g))/cr;
bc = ((float)(maxc-b))/cr;
if (r == maxc) {
h = bc-gc;
} else if (g == maxc) {
h = 2.0 + rc-bc;
} else {
h = 4.0 + gc-rc;
}
// incorrect hue happens if h/6 is negative.
h = fmod((h/6.0 + 1.0), 1.0);
uh = (UINT8)CLIP8((int)(h*255.0));
us = (UINT8)CLIP8((int)(s*255.0));
*out++ = uh;
*out++ = us;
*out++ = uv;
} }
*out++ = in[3]; // incorrect hue happens if h/6 is negative.
h = fmod((h/6.0 + 1.0), 1.0);
uh = (UINT8)CLIP8((int)(h*255.0));
us = (UINT8)CLIP8((int)(s*255.0));
}
out[0] = uh;
out[1] = us;
out[2] = uv;
}
static void
rgb2hsv(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
rgb2hsv_row(out, in);
out[3] = in[3];
} }
} }
static void static void
hsv2rgb(UINT8* out, const UINT8* in, int xsize) hsv2rgb(UINT8* out, const UINT8* in, int xsize)
{ // following colorsys.py { // following colorsys.py
@ -562,6 +605,22 @@ cmyk2rgb(UINT8* out, const UINT8* in, int xsize)
} }
} }
static void
cmyk2hsv(UINT8* out, const UINT8* in, int xsize)
{
int x, nk, tmp;
for (x = 0; x < xsize; x++) {
nk = 255 - in[3];
out[0] = CLIP8(nk - MULDIV255(in[0], nk, tmp));
out[1] = CLIP8(nk - MULDIV255(in[1], nk, tmp));
out[2] = CLIP8(nk - MULDIV255(in[2], nk, tmp));
rgb2hsv_row(out, out);
out[3] = 255;
out += 4;
in += 4;
}
}
/* ------------- */ /* ------------- */
/* I conversions */ /* I conversions */
/* ------------- */ /* ------------- */
@ -631,6 +690,25 @@ i2rgb(UINT8* out, const UINT8* in_, int xsize)
} }
} }
static void
i2hsv(UINT8* out, const UINT8* in_, int xsize)
{
int x;
INT32* in = (INT32*) in_;
for (x = 0; x < xsize; x++, in++, out+=4) {
out[0] = 0;
out[1] = 0;
if (*in <= 0) {
out[2] = 0;
} else if (*in >= 255) {
out[2] = 255;
} else {
out[2] = (UINT8) *in;
}
out[3] = 255;
}
}
/* ------------- */ /* ------------- */
/* F conversions */ /* F conversions */
/* ------------- */ /* ------------- */
@ -861,6 +939,7 @@ static struct {
{ "1", "RGBX", bit2rgb }, { "1", "RGBX", bit2rgb },
{ "1", "CMYK", bit2cmyk }, { "1", "CMYK", bit2cmyk },
{ "1", "YCbCr", bit2ycbcr }, { "1", "YCbCr", bit2ycbcr },
{ "1", "HSV", bit2hsv },
{ "L", "1", l2bit }, { "L", "1", l2bit },
{ "L", "LA", l2la }, { "L", "LA", l2la },
@ -871,6 +950,7 @@ static struct {
{ "L", "RGBX", l2rgb }, { "L", "RGBX", l2rgb },
{ "L", "CMYK", l2cmyk }, { "L", "CMYK", l2cmyk },
{ "L", "YCbCr", l2ycbcr }, { "L", "YCbCr", l2ycbcr },
{ "L", "HSV", l2hsv },
{ "LA", "L", la2l }, { "LA", "L", la2l },
{ "LA", "La", lA2la }, { "LA", "La", lA2la },
@ -879,6 +959,7 @@ static struct {
{ "LA", "RGBX", la2rgb }, { "LA", "RGBX", la2rgb },
{ "LA", "CMYK", la2cmyk }, { "LA", "CMYK", la2cmyk },
{ "LA", "YCbCr", la2ycbcr }, { "LA", "YCbCr", la2ycbcr },
{ "LA", "HSV", la2hsv },
{ "La", "LA", la2lA }, { "La", "LA", la2lA },
@ -887,6 +968,7 @@ static struct {
{ "I", "RGB", i2rgb }, { "I", "RGB", i2rgb },
{ "I", "RGBA", i2rgb }, { "I", "RGBA", i2rgb },
{ "I", "RGBX", i2rgb }, { "I", "RGBX", i2rgb },
{ "I", "HSV", i2hsv },
{ "F", "L", f2l }, { "F", "L", f2l },
{ "F", "I", f2i }, { "F", "I", f2i },
@ -915,6 +997,7 @@ static struct {
{ "RGBA", "RGBX", rgb2rgba }, { "RGBA", "RGBX", rgb2rgba },
{ "RGBA", "CMYK", rgb2cmyk }, { "RGBA", "CMYK", rgb2cmyk },
{ "RGBA", "YCbCr", ImagingConvertRGB2YCbCr }, { "RGBA", "YCbCr", ImagingConvertRGB2YCbCr },
{ "RGBA", "HSV", rgb2hsv },
{ "RGBa", "RGBA", rgba2rgbA }, { "RGBa", "RGBA", rgba2rgbA },
@ -926,10 +1009,12 @@ static struct {
{ "RGBX", "RGB", rgba2rgb }, { "RGBX", "RGB", rgba2rgb },
{ "RGBX", "CMYK", rgb2cmyk }, { "RGBX", "CMYK", rgb2cmyk },
{ "RGBX", "YCbCr", ImagingConvertRGB2YCbCr }, { "RGBX", "YCbCr", ImagingConvertRGB2YCbCr },
{ "RGBX", "HSV", rgb2hsv },
{ "CMYK", "RGB", cmyk2rgb }, { "CMYK", "RGB", cmyk2rgb },
{ "CMYK", "RGBA", cmyk2rgb }, { "CMYK", "RGBA", cmyk2rgb },
{ "CMYK", "RGBX", cmyk2rgb }, { "CMYK", "RGBX", cmyk2rgb },
{ "CMYK", "HSV", cmyk2hsv },
{ "YCbCr", "L", ycbcr2l }, { "YCbCr", "L", ycbcr2l },
{ "YCbCr", "LA", ycbcr2la }, { "YCbCr", "LA", ycbcr2la },
@ -1101,6 +1186,28 @@ pa2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
} }
} }
static void
p2hsv(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
for (x = 0; x < xsize; x++, out += 4) {
const UINT8* rgb = &palette[*in++ * 4];
rgb2hsv_row(out, rgb);
out[3] = 255;
}
}
static void
pa2hsv(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
const UINT8* rgb = &palette[in[0] * 4];
rgb2hsv_row(out, rgb);
out[3] = 255;
}
}
static void static void
p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{ {
@ -1192,6 +1299,8 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
convert = alpha ? pa2cmyk : p2cmyk; convert = alpha ? pa2cmyk : p2cmyk;
else if (strcmp(mode, "YCbCr") == 0) else if (strcmp(mode, "YCbCr") == 0)
convert = alpha ? pa2ycbcr : p2ycbcr; convert = alpha ? pa2ycbcr : p2ycbcr;
else if (strcmp(mode, "HSV") == 0)
convert = alpha ? pa2hsv : p2hsv;
else else
return (Imaging) ImagingError_ValueError("conversion not supported"); return (Imaging) ImagingError_ValueError("conversion not supported");