mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 17:24:31 +03:00
Merge branch 'master' of https://github.com/python-pillow/Pillow
This commit is contained in:
commit
8101c2a313
|
@ -4,6 +4,9 @@ Changelog (Pillow)
|
|||
2.6.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- HSV Support #816
|
||||
[wiredfool]
|
||||
|
||||
- Removed unusable ImagePalette.new()
|
||||
[hugovk]
|
||||
|
||||
|
|
|
@ -220,6 +220,7 @@ _MODEINFO = {
|
|||
"CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
|
||||
"YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
|
||||
"LAB": ("RGB", "L", ("L", "A", "B")),
|
||||
"HSV": ("RGB", "L", ("H", "S", "V")),
|
||||
|
||||
# Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and
|
||||
# BGR;24. Use these modes only if you know exactly what you're
|
||||
|
|
|
@ -261,6 +261,7 @@ mode_map = {'1': _PyAccess8,
|
|||
'PA': _PyAccess32_2,
|
||||
'RGB': _PyAccess32_3,
|
||||
'LAB': _PyAccess32_3,
|
||||
'HSV': _PyAccess32_3,
|
||||
'YCbCr': _PyAccess32_3,
|
||||
'RGBA': _PyAccess32_4,
|
||||
'RGBa': _PyAccess32_4,
|
||||
|
|
|
@ -100,7 +100,7 @@ class PillowTestCase(unittest.TestCase):
|
|||
ave_diff = float(diff)/(a.size[0]*a.size[1])
|
||||
self.assertGreaterEqual(
|
||||
epsilon, ave_diff,
|
||||
msg or "average pixel value difference %.4f > epsilon %.4f" % (
|
||||
(msg or '') + " average pixel value difference %.4f > epsilon %.4f" % (
|
||||
ave_diff, epsilon))
|
||||
|
||||
def assert_warning(self, warn_class, func):
|
||||
|
|
|
@ -9,6 +9,7 @@ modes = [
|
|||
"RGB", "RGBA", "RGBa", "RGBX",
|
||||
"CMYK",
|
||||
"YCbCr",
|
||||
"LAB", "HSV",
|
||||
]
|
||||
|
||||
|
||||
|
|
169
Tests/test_format_hsv.py
Normal file
169
Tests/test_format_hsv.py
Normal file
|
@ -0,0 +1,169 @@
|
|||
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 tuple_to_ints(self, tp):
|
||||
x,y,z = tp
|
||||
return (int(x*255.0), int(y*255.0), int(z*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:
|
||||
conv_func = self.str_to_float
|
||||
else:
|
||||
conv_func = self.int_to_float
|
||||
|
||||
if hasattr(itertools, 'izip'):
|
||||
iter_helper = itertools.izip
|
||||
else:
|
||||
iter_helper = itertools.zip_longest
|
||||
|
||||
|
||||
converted = [self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b)))
|
||||
for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(), b.tobytes())]
|
||||
|
||||
if str is bytes:
|
||||
new_bytes = b''.join(chr(h)+chr(s)+chr(v) for (h,s,v) in converted)
|
||||
else:
|
||||
new_bytes = b''.join(bytes(chr(h)+chr(s)+chr(v), 'latin-1') for (h,s,v) in converted)
|
||||
|
||||
hsv = Image.frombytes(mode,r.size, new_bytes)
|
||||
|
||||
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):
|
||||
src = self.wedge().resize((3*32,32),Image.BILINEAR)
|
||||
im = src.convert('HSV')
|
||||
comparable = self.to_hsv_colorsys(src)
|
||||
|
||||
#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 = src
|
||||
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
|
|
@ -13,8 +13,8 @@
|
|||
#include "Imaging.h"
|
||||
|
||||
/* use Tests/make_hash.py to calculate these values */
|
||||
#define ACCESS_TABLE_SIZE 21
|
||||
#define ACCESS_TABLE_HASH 30197
|
||||
#define ACCESS_TABLE_SIZE 27
|
||||
#define ACCESS_TABLE_HASH 3078
|
||||
|
||||
static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE];
|
||||
|
||||
|
@ -238,6 +238,7 @@ ImagingAccessInit()
|
|||
ADD("CMYK", line_32, get_pixel_32, put_pixel_32);
|
||||
ADD("YCbCr", line_32, get_pixel_32, put_pixel_32);
|
||||
ADD("LAB", line_32, get_pixel_32, put_pixel_32);
|
||||
ADD("HSV", line_32, get_pixel_32, put_pixel_32);
|
||||
}
|
||||
|
||||
ImagingAccess
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -566,6 +566,12 @@ static struct {
|
|||
{"LAB", "A", 8, band1},
|
||||
{"LAB", "B", 8, band2},
|
||||
|
||||
/* HSV */
|
||||
{"HSV", "HSV", 24, ImagingPackRGB},
|
||||
{"HSV", "H", 8, band0},
|
||||
{"HSV", "S", 8, band1},
|
||||
{"HSV", "V", 8, band2},
|
||||
|
||||
/* integer */
|
||||
{"I", "I", 32, copy4},
|
||||
{"I", "I;16B", 16, packI16B},
|
||||
|
|
|
@ -186,6 +186,13 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize,
|
|||
im->pixelsize = 4;
|
||||
im->linesize = xsize * 4;
|
||||
|
||||
} else if (strcmp(mode, "HSV") == 0) {
|
||||
/* 24-bit color, luminance, + 2 color channels */
|
||||
/* L is uint8, a,b are int8 */
|
||||
im->bands = 3;
|
||||
im->pixelsize = 4;
|
||||
im->linesize = xsize * 4;
|
||||
|
||||
} else {
|
||||
free(im);
|
||||
return (Imaging) ImagingError_ValueError("unrecognized mode");
|
||||
|
|
|
@ -1159,6 +1159,12 @@ static struct {
|
|||
{"LAB", "A", 8, band1},
|
||||
{"LAB", "B", 8, band2},
|
||||
|
||||
/* HSV Color */
|
||||
{"HSV", "HSV", 24, ImagingUnpackRGB},
|
||||
{"HSV", "H", 8, band0},
|
||||
{"HSV", "S", 8, band1},
|
||||
{"HSV", "V", 8, band2},
|
||||
|
||||
/* integer variations */
|
||||
{"I", "I", 32, copy4},
|
||||
{"I", "I;8", 8, unpackI8},
|
||||
|
|
34
profile-installed.py
Executable file
34
profile-installed.py
Executable file
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python
|
||||
import nose
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
|
||||
import profile
|
||||
|
||||
# monkey with the path, removing the local directory but adding the Tests/
|
||||
# directory for helper.py and the other local imports there.
|
||||
|
||||
del(sys.path[0])
|
||||
sys.path.insert(0, os.path.abspath('./Tests'))
|
||||
|
||||
# if there's no test selected (mostly) choose a working default.
|
||||
# Something is required, because if we import the tests from the local
|
||||
# directory, once again, we've got the non-installed PIL in the way
|
||||
if len(sys.argv) == 1:
|
||||
sys.argv.extend(glob.glob('Tests/test*.py'))
|
||||
|
||||
# Make sure that nose doesn't muck with our paths.
|
||||
if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv):
|
||||
sys.argv.insert(1, '--no-path-adjustment')
|
||||
|
||||
if 'NOSE_PROCESSES' not in os.environ:
|
||||
for arg in sys.argv:
|
||||
if '--processes' in arg:
|
||||
break
|
||||
else: # for
|
||||
sys.argv.insert(1, '--processes=-1') # -1 == number of cores
|
||||
sys.argv.insert(1, '--process-timeout=30')
|
||||
|
||||
if __name__ == '__main__':
|
||||
profile.run("nose.main()", sort=2)
|
Loading…
Reference in New Issue
Block a user