色彩还原是指某设备(手机、相机)对于色彩的还原能力,拍出来的图片越接近真实颜色,表示还原能力越好。
根据这个指标,我们可以对设备在色彩方面的能力得到一个评判标准;同时,我们也可以相机或者显示器上面进行色偏的检验和校准(目前就有相机是实用24色卡进行颜色校准的)。
下面先介绍下色彩还原的计算公式:
ΔC = ( (a -a*)2+ (b - b*)2 )1/2
ΔE = ( (L -L*)2+ (a -a*)2+ (b - b*)2 )1/2
Saturation = 100% ×((a 2 + b 2)1/2 ) / ((a2 + b2)1/2 )
其中L、a、b为经过拍照系统处理后的值,L*、a*、b为Color Checker的理想值;
L、a*、b*的标准为:
本文只计算接近肤色的8块:
[(37.986,13.555,14.059),(65.711,18.13,17.81),(62.661,36.067,57.096),(71.941,19.363,67.857),(96.539,-0.425,1.186),(81.257,-0.683,-0.335),(66.766,-0.734,-0.504),(50.867,-0.153,-0.25)]
具体步骤:
1.在标准环境下拍摄24色色卡;
2.把整张色卡裁剪成24个色块;
3.对每一张图片的相关色块进行计算,得到ΔC、ΔE、S的值。
代码1:把色卡裁剪成方块
import colorsys
from PIL import Image
import os
for n in range(1,25):
#创建新文件夹,一张原图创建一个文件夹
def mkdir(path):
folder=os.path.exists(path)
if not folder:
os.makedirs(path)
print("new folder")
else:
print("ok")
folders='cl/face/img'+str(n)
mkdir(folders)
path='color/face/'
files=os.listdir(path)
for n in range(24):
file=files[n]
im = Image.open(path+file)
# 图片的宽度和高度
img_size = im.size
print(file)
#图片需要分割成4行,6列
#则每张图片的宽x为:总宽度/6,高y为:总高度/4
xx = 6
yy = 4
x = img_size[0] // xx
y = img_size[1] // yy
#横向切割,先切第一行6块,再切第2行6块······
for j in range(yy):
for i in range(xx):
left = i*x
up = y*j
right = left + x
low = up + y
region = im.crop((left+100,up+120,right-150,low-100))
# print("坐标为:",(left,up,right,low))
# #保存切割好的图片,并且按j-i(第几行,第几列)的方式来命名
temp = str(j+1)+'-'+str(i+1)
region.save('cl/face/img'+str(n+1)+'/'+temp+".jpg")
cc='img'+str(n+1)+'/'+temp+".jpg"
print("文件名为:",cc)
运行之后,将得到24个小方块,对应的24色色卡的颜色
代码2:计算各个方块与理想值的ΔC、ΔE、S
由于图片不能直接转换成lab值,因此需要经过计算来转换
import colorsys
from PIL import Image
import numpy
import os
#此函数为设计公式,用于计算C、E、Sat的值
def result(L,a,b):
#把标准的Lab值单独提取出来
L1=Lab_value[file_num-1][0]
# L1="%.2LF"%L1
a1=Lab_value[file_num-1][1]
b1=Lab_value[file_num-1][2]
#按公式计算C、E、S的值,得出结果
x=numpy.square(a-a1) #(a -a*)2
y=numpy.square(b-b1) #(b - b*)2
z=numpy.square(L-L1) #(L -L*)2
c=numpy.sqrt(x+y) #ΔC =( (a -a*)2+ (b - b*)2 )1/2
print('C的值为:',"%.2f"%c)
e=numpy.sqrt(x+y+z) #ΔE = ( (L -L*)2+ (a -a*)2+ (b - b*)2 )1/2
print('E的值为:',"%.2f"%e)
#Saturation = 100% ×((a 2 + b 2)1/2 ) / ((a*2 + b*2)1/2 )
x1=numpy.sqrt(numpy.square(a)+numpy.square(b)) #(a 2 + b 2)1/2
x2=numpy.sqrt(numpy.square(a1)+numpy.square(b1))
S=(x1/x2)
# print('S的值为:',"%.4f"%S)
print('S的百分比为:',"%.2f%%"%(S*100)) #输出百分比
print("%.2f"%c,"%.2f"%e,"%.2f%%"%(S*100))
# print("第"+str(n)+"个文件夹计算结束")
print('---------------------------------')
line="%.2f"%c,"%.2f"%e,"%.2f%%"%(S*100)
# print(line)
f1.write(line[0]+' '+line[1]+' '+line[2]+'\n')
#此函数用于把RGB转换为Lab值
def rgb2lab(R,G,B):
Xn = 0.950456
Yn = 1.0
Zn = 1.088754
R=R/255.0
G=G/255.0
B=B/255.0
if (R > 0.04045):
R=pow((R + 0.055) / 1.055, 2.4)
else:
R=R / 12.92
if (G > 0.04045):
G=pow((G + 0.055) / 1.055, 2.4)
else:
G=G / 12.92
if (B > 0.04045):
B=pow((B + 0.055) / 1.055, 2.4)
else:
B=B / 12.92
X = 0.412453 * R + 0.357580 * G + 0.180423 * B
Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
Z = 0.019334 * R + 0.119193 * G + 0.950227 * B
x = X / Xn
y = Y / Yn
z = Z / Zn
if (y > 0.008856):
fY = pow(y, 1.0/3.0)
else:
fY = 7.787 * y + 16.0/116.0
if (x > 0.008856):
fX = pow(x, 1.0/3.0)
else:
fX = 7.787*x + 16.0/116.0
if (z > 0.008856):
fZ = pow(z, 1.0/3.0)
else:
fZ = 7.787*z + 16.0/116.0
L = 116.0 * fY - 16.0
a = (500.0*(fX - fY))
b = (200.0*(fY - fZ))
L=float("%.3f"%L)
a=float("%.3f"%a)
b=float("%.3f"%b)
# print("Lab的值为:",(L,a,b))
result(L,a,b)
def avg(list1):
avg = float(sum(list1))/len(list1)
return avg
#预先输入24张图片的L1,a1,b1的值,即标准的Lab值
Lab_value=[(37.986,13.555,14.059),(65.711,18.13,17.81),(62.661,36.067,57.096),(71.941,19.363,67.857),(96.539,-0.425,1.186),(81.257,-0.683,-0.335),(66.766,-0.734,-0.504),(50.867,-0.153,-0.25)]
f1=open('D:/work/1.工作任务/5.色彩还原--4环境+人脸肤色/face/data.txt','w+')
#用n循环第1-2个文件夹,取第yy行,xx列的图片做计算
path='D:/work/1.工作任务/5.色彩还原--4环境+人脸肤色/color/face/'
files=os.listdir(path)
n=0
for file in files:
n=n+1
im = Image.open(path+file)
#取图片的中心区域坐标,获取该区域的平均RGB
img_size = im.size
w = img_size[0] // 2-50
h = img_size[1] // 2-50
pix = im.load()
r=[]
g=[]
b=[]
for x in range(101):
RGB = pix[w+x, h+x]
r.append(RGB[0])
g.append(RGB[1])
b.append(RGB[2])
R=avg(r)
G=avg(g)
B=avg(b)
for i in range(8):
file_num = i + 1
print("第" + str(n) + "张,第" + str(file_num) + "次计算")
rgb2lab(R,G,B)
结果如下:
备注:
ΔC、ΔE的值,在正常范围内不标色,超出范围标“红色”
ΔC,ΔE表示色彩正确度误差,一般而言值越小表示越接近真实颜色SRGB,也表示摄像模组的颜色误差越小,颜色越好