Conversion between RGB and HSV images

This commit is contained in:
wiredfool 2014-07-21 22:36:40 -07:00
parent 625ff24358
commit 0bb1cd398f
2 changed files with 303 additions and 0 deletions

177
Tests/test_format_hsv.py Normal file
View 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

View File

@ -35,6 +35,9 @@
#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 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 */
/* ---------------- */
@ -658,6 +781,7 @@ static struct {
{ "RGB", "RGBX", rgb2rgba },
{ "RGB", "CMYK", rgb2cmyk },
{ "RGB", "YCbCr", ImagingConvertRGB2YCbCr },
{ "RGB", "HSV", rgb2hsv },
{ "RGBA", "1", rgb2bit },
{ "RGBA", "L", rgb2l },
@ -687,6 +811,8 @@ static struct {
{ "YCbCr", "L", ycbcr2l },
{ "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
{ "HSV", "RGB", hsv2rgb },
{ "I", "I;16", I_I16L },
{ "I;16", "I", I16L_I },
{ "L", "I;16", L_I16L },