mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-06 14:40:51 +03:00
Conversion between RGB and HSV images
This commit is contained in:
parent
625ff24358
commit
0bb1cd398f
177
Tests/test_format_hsv.py
Normal file
177
Tests/test_format_hsv.py
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
from helper import unittest, PillowTestCase, lena
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
import colorsys, itertools
|
||||||
|
|
||||||
|
class TestFormatHSV(PillowTestCase):
|
||||||
|
|
||||||
|
def int_to_float(self, i):
|
||||||
|
return float(i)/255.0
|
||||||
|
def str_to_float(self, i):
|
||||||
|
return float(ord(i))/255.0
|
||||||
|
def to_int(self, f):
|
||||||
|
return int(f*255.0)
|
||||||
|
|
||||||
|
def test_sanity(self):
|
||||||
|
im = Image.new('HSV', (100,100))
|
||||||
|
|
||||||
|
def wedge(self):
|
||||||
|
w =Image._wedge()
|
||||||
|
w90 = w.rotate(90)
|
||||||
|
|
||||||
|
(px, h) = w.size
|
||||||
|
|
||||||
|
r = Image.new('L', (px*3,h))
|
||||||
|
g = r.copy()
|
||||||
|
b = r.copy()
|
||||||
|
|
||||||
|
r.paste(w, (0,0))
|
||||||
|
r.paste(w90, (px,0))
|
||||||
|
|
||||||
|
g.paste(w90, (0,0))
|
||||||
|
g.paste(w, (2*px,0))
|
||||||
|
|
||||||
|
b.paste(w, (px,0))
|
||||||
|
b.paste(w90, (2*px,0))
|
||||||
|
|
||||||
|
img = Image.merge('RGB',(r,g,b))
|
||||||
|
|
||||||
|
#print (("%d, %d -> "% (int(1.75*px),int(.25*px))) + \
|
||||||
|
# "(%s, %s, %s)"%img.getpixel((1.75*px, .25*px)))
|
||||||
|
#print (("%d, %d -> "% (int(.75*px),int(.25*px))) + \
|
||||||
|
# "(%s, %s, %s)"%img.getpixel((.75*px, .25*px)))
|
||||||
|
return img
|
||||||
|
|
||||||
|
def to_xxx_colorsys(self, im, func, mode):
|
||||||
|
# convert the hard way using the library colorsys routines.
|
||||||
|
|
||||||
|
(r,g,b) = im.split()
|
||||||
|
|
||||||
|
if bytes is str:
|
||||||
|
f_r = map(self.str_to_float,r.tobytes())
|
||||||
|
f_g = map(self.str_to_float,g.tobytes())
|
||||||
|
f_b = map(self.str_to_float,b.tobytes())
|
||||||
|
else:
|
||||||
|
f_r = map(self.int_to_float,r.tobytes())
|
||||||
|
f_g = map(self.int_to_float,g.tobytes())
|
||||||
|
f_b = map(self.int_to_float,b.tobytes())
|
||||||
|
|
||||||
|
f_h = [];
|
||||||
|
f_s = [];
|
||||||
|
f_v = [];
|
||||||
|
|
||||||
|
if hasattr(itertools, 'izip'):
|
||||||
|
iter_helper = itertools.izip
|
||||||
|
else:
|
||||||
|
iter_helper = itertools.zip_longest
|
||||||
|
|
||||||
|
for (_r, _g, _b) in iter_helper(f_r, f_g, f_b):
|
||||||
|
_h, _s, _v = func(_r, _g, _b)
|
||||||
|
f_h.append(_h)
|
||||||
|
f_s.append(_s)
|
||||||
|
f_v.append(_v)
|
||||||
|
|
||||||
|
h = Image.new('L', r.size)
|
||||||
|
h.putdata(list(map(self.to_int, f_h)))
|
||||||
|
s = Image.new('L', r.size)
|
||||||
|
s.putdata(list(map(self.to_int, f_s)))
|
||||||
|
v = Image.new('L', r.size)
|
||||||
|
v.putdata(list(map(self.to_int, f_v)))
|
||||||
|
|
||||||
|
hsv = Image.merge(mode, (h, s, v))
|
||||||
|
|
||||||
|
return hsv
|
||||||
|
|
||||||
|
def to_hsv_colorsys(self, im):
|
||||||
|
return self.to_xxx_colorsys(im, colorsys.rgb_to_hsv, 'HSV')
|
||||||
|
|
||||||
|
def to_rgb_colorsys(self, im):
|
||||||
|
return self.to_xxx_colorsys(im, colorsys.hsv_to_rgb, 'RGB')
|
||||||
|
|
||||||
|
def test_wedge(self):
|
||||||
|
im = self.wedge().convert('HSV')
|
||||||
|
comparable = self.to_hsv_colorsys(self.wedge())
|
||||||
|
|
||||||
|
#print (im.getpixel((448, 64)))
|
||||||
|
#print (comparable.getpixel((448, 64)))
|
||||||
|
|
||||||
|
#print(im.split()[0].histogram())
|
||||||
|
#print(comparable.split()[0].histogram())
|
||||||
|
|
||||||
|
#im.split()[0].show()
|
||||||
|
#comparable.split()[0].show()
|
||||||
|
|
||||||
|
self.assert_image_similar(im.split()[0], comparable.split()[0],
|
||||||
|
1, "Hue conversion is wrong")
|
||||||
|
self.assert_image_similar(im.split()[1], comparable.split()[1],
|
||||||
|
1, "Saturation conversion is wrong")
|
||||||
|
self.assert_image_similar(im.split()[2], comparable.split()[2],
|
||||||
|
1, "Value conversion is wrong")
|
||||||
|
|
||||||
|
#print (im.getpixel((192, 64)))
|
||||||
|
|
||||||
|
comparable = self.wedge()
|
||||||
|
im = im.convert('RGB')
|
||||||
|
|
||||||
|
#im.split()[0].show()
|
||||||
|
#comparable.split()[0].show()
|
||||||
|
#print (im.getpixel((192, 64)))
|
||||||
|
#print (comparable.getpixel((192, 64)))
|
||||||
|
|
||||||
|
self.assert_image_similar(im.split()[0], comparable.split()[0],
|
||||||
|
3, "R conversion is wrong")
|
||||||
|
self.assert_image_similar(im.split()[1], comparable.split()[1],
|
||||||
|
3, "G conversion is wrong")
|
||||||
|
self.assert_image_similar(im.split()[2], comparable.split()[2],
|
||||||
|
3, "B conversion is wrong")
|
||||||
|
|
||||||
|
|
||||||
|
def test_convert(self):
|
||||||
|
im = lena('RGB').convert('HSV')
|
||||||
|
comparable = self.to_hsv_colorsys(lena('RGB'))
|
||||||
|
|
||||||
|
# print ([ord(x) for x in im.split()[0].tobytes()[:80]])
|
||||||
|
# print ([ord(x) for x in comparable.split()[0].tobytes()[:80]])
|
||||||
|
|
||||||
|
# print(im.split()[0].histogram())
|
||||||
|
# print(comparable.split()[0].histogram())
|
||||||
|
|
||||||
|
self.assert_image_similar(im.split()[0], comparable.split()[0],
|
||||||
|
1, "Hue conversion is wrong")
|
||||||
|
self.assert_image_similar(im.split()[1], comparable.split()[1],
|
||||||
|
1, "Saturation conversion is wrong")
|
||||||
|
self.assert_image_similar(im.split()[2], comparable.split()[2],
|
||||||
|
1, "Value conversion is wrong")
|
||||||
|
|
||||||
|
|
||||||
|
def test_hsv_to_rgb(self):
|
||||||
|
comparable = self.to_hsv_colorsys(lena('RGB'))
|
||||||
|
converted = comparable.convert('RGB')
|
||||||
|
comparable = self.to_rgb_colorsys(comparable)
|
||||||
|
|
||||||
|
# print(converted.split()[1].histogram())
|
||||||
|
# print(target.split()[1].histogram())
|
||||||
|
|
||||||
|
# print ([ord(x) for x in target.split()[1].tobytes()[:80]])
|
||||||
|
# print ([ord(x) for x in converted.split()[1].tobytes()[:80]])
|
||||||
|
|
||||||
|
|
||||||
|
self.assert_image_similar(converted.split()[0], comparable.split()[0],
|
||||||
|
3, "R conversion is wrong")
|
||||||
|
self.assert_image_similar(converted.split()[1], comparable.split()[1],
|
||||||
|
3, "G conversion is wrong")
|
||||||
|
self.assert_image_similar(converted.split()[2], comparable.split()[2],
|
||||||
|
3, "B conversion is wrong")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
|
||||||
|
# End of file
|
|
@ -35,6 +35,9 @@
|
||||||
|
|
||||||
#include "Imaging.h"
|
#include "Imaging.h"
|
||||||
|
|
||||||
|
#define MAX(a, b) (a)>(b) ? (a) : (b)
|
||||||
|
#define MIN(a, b) (a)<(b) ? (a) : (b)
|
||||||
|
|
||||||
#define CLIP(v) ((v) <= 0 ? 0 : (v) >= 255 ? 255 : (v))
|
#define CLIP(v) ((v) <= 0 ? 0 : (v) >= 255 ? 255 : (v))
|
||||||
#define CLIP16(v) ((v) <= -32768 ? -32768 : (v) >= 32767 ? 32767 : (v))
|
#define CLIP16(v) ((v) <= -32768 ? -32768 : (v) >= 32767 ? 32767 : (v))
|
||||||
|
|
||||||
|
@ -236,6 +239,126 @@ rgb2bgr24(UINT8* out, const UINT8* in, int xsize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rgb2hsv(UINT8* out, const UINT8* in, int xsize)
|
||||||
|
{ // following colorsys.py
|
||||||
|
float h,s,rc,gc,bc,cr;
|
||||||
|
UINT8 maxc,minc;
|
||||||
|
UINT8 r, g, b;
|
||||||
|
UINT8 uh,us,uv;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
for (x = 0; x < xsize; x++, in += 4) {
|
||||||
|
r = in[0];
|
||||||
|
g = in[1];
|
||||||
|
b = in[2];
|
||||||
|
|
||||||
|
maxc = MAX(r,MAX(g,b));
|
||||||
|
minc = MIN(r,MIN(g,b));
|
||||||
|
uv = maxc;
|
||||||
|
if (minc == maxc){
|
||||||
|
*out++ = 0;
|
||||||
|
*out++ = 0;
|
||||||
|
*out++ = uv;
|
||||||
|
} else {
|
||||||
|
cr = (float)(maxc-minc);
|
||||||
|
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)CLIP((int)(h*255.0));
|
||||||
|
us = (UINT8)CLIP((int)(s*255.0));
|
||||||
|
|
||||||
|
*out++ = uh;
|
||||||
|
*out++ = us;
|
||||||
|
*out++ = uv;
|
||||||
|
|
||||||
|
}
|
||||||
|
*out++ = in[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hsv2rgb(UINT8* out, const UINT8* in, int xsize)
|
||||||
|
{ // following colorsys.py
|
||||||
|
|
||||||
|
int p,q,t;
|
||||||
|
uint up,uq,ut;
|
||||||
|
int i, x;
|
||||||
|
float f, fs;
|
||||||
|
uint h,s,v;
|
||||||
|
|
||||||
|
for (x = 0; x < xsize; x++, in += 4) {
|
||||||
|
h = in[0];
|
||||||
|
s = in[1];
|
||||||
|
v = in[2];
|
||||||
|
|
||||||
|
if (s==0){
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = v;
|
||||||
|
} else {
|
||||||
|
i = floor((float)h * 6.0 / 255.0); // 0 - 6
|
||||||
|
f = (float)h * 6.0 / 255.0 - (float)i; // 0-1 : remainder.
|
||||||
|
fs = ((float)s)/255.0;
|
||||||
|
|
||||||
|
p = round((float)v * (1.0-fs));
|
||||||
|
q = round((float)v * (1.0-fs*f));
|
||||||
|
t = round((float)v * (1.0-fs*(1.0-f)));
|
||||||
|
up = (UINT8)CLIP(p);
|
||||||
|
uq = (UINT8)CLIP(q);
|
||||||
|
ut = (UINT8)CLIP(t);
|
||||||
|
|
||||||
|
switch (i%6) {
|
||||||
|
case 0:
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = ut;
|
||||||
|
*out++ = up;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
*out++ = uq;
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = up;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*out++ = up;
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = ut;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*out++ = up;
|
||||||
|
*out++ = uq;
|
||||||
|
*out++ = v;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*out++ = ut;
|
||||||
|
*out++ = up;
|
||||||
|
*out++ = v;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = up;
|
||||||
|
*out++ = uq;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out++ = in[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ---------------- */
|
/* ---------------- */
|
||||||
/* RGBA conversions */
|
/* RGBA conversions */
|
||||||
/* ---------------- */
|
/* ---------------- */
|
||||||
|
@ -658,6 +781,7 @@ static struct {
|
||||||
{ "RGB", "RGBX", rgb2rgba },
|
{ "RGB", "RGBX", rgb2rgba },
|
||||||
{ "RGB", "CMYK", rgb2cmyk },
|
{ "RGB", "CMYK", rgb2cmyk },
|
||||||
{ "RGB", "YCbCr", ImagingConvertRGB2YCbCr },
|
{ "RGB", "YCbCr", ImagingConvertRGB2YCbCr },
|
||||||
|
{ "RGB", "HSV", rgb2hsv },
|
||||||
|
|
||||||
{ "RGBA", "1", rgb2bit },
|
{ "RGBA", "1", rgb2bit },
|
||||||
{ "RGBA", "L", rgb2l },
|
{ "RGBA", "L", rgb2l },
|
||||||
|
@ -687,6 +811,8 @@ static struct {
|
||||||
{ "YCbCr", "L", ycbcr2l },
|
{ "YCbCr", "L", ycbcr2l },
|
||||||
{ "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
|
{ "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
|
||||||
|
|
||||||
|
{ "HSV", "RGB", hsv2rgb },
|
||||||
|
|
||||||
{ "I", "I;16", I_I16L },
|
{ "I", "I;16", I_I16L },
|
||||||
{ "I;16", "I", I16L_I },
|
{ "I;16", "I", I16L_I },
|
||||||
{ "L", "I;16", L_I16L },
|
{ "L", "I;16", L_I16L },
|
||||||
|
|
Loading…
Reference in New Issue
Block a user