所以我正在设计一些使用python
在PIL
编辑照片的程序,其中一个是将图像转换为灰度(我避免使用PIL
中的任何函数)。
我使用的算法很简单:对于每个像素(颜色深度为24),我计算了R
,G
和B
值的平均值,并将RGB值设置为此平均值。
我的程序生成的灰度图像似乎准确,但我想知道我是否使用了正确的算法,我遇到this answer一个问题,似乎'正确'的算法是计算0.299 R + 0.587 G + 0.114 B
。
我决定将我的程序与这个算法进行比较。我使用我的程序生成了一个灰度图像,另一个(使用相同的输入)来自a website online('image to grayscale'
的顶级Google结果)。
在我的肉眼看来,它们似乎完全相同,如果有任何变化,我看不到它。但是,我决定使用this website('compare two images online'
的谷歌搜索结果)来比较我的灰度图像。事实证明,在像素的深处,它们有轻微的变化,但没有一个人乍一看是可感知的(差异可以被发现,但通常只有当图像相互叠加或在几毫秒之间切换时) 。
我的问题(第一个是主要问题):
我的关键代码(如果需要):
def greyScale(pixelTuple):
return tuple([round(sum(pixelTuple) / 3)] * 3)
'正确'的算法(似乎重量很重):
def greyScale(pixelTuple):
return tuple([round(0.299 * pixelTuple[0] + 0.587 * pixelTuple[1] + 0.114 * pixelTuple[2])] * 3)
当在线比较灰度图像时(突出显示红色是差异,使用10%的模糊):qazxsw poi
尽管上面突出显示了像素的变化,但上面的灰度图像看起来几乎完全相同(至少对我而言)。
另外,关于我的第一个问题,如果有人感兴趣,已经对转换为灰度的不同算法做了一些分析,并且还有一些自定义算法。
编辑:
为了回应@ Szulat的回答,我的算法实际上生成了这个图像(忽略了糟糕的裁剪,原始图像有三个圆圈,但我只需要第一个):
如果人们想知道转换为灰度的原因是什么(因为看起来算法取决于目的),我只是在制作一些简单的照片编辑工具,这样我就可以使用迷你Photoshop并且不要需要依靠互联网来应用过滤器和效果。
赏金的原因:这里的不同答案涵盖了不同的事物,这些都是相关的和有用的。这使得选择接受哪个答案变得非常困难。我已经开始获得赏金,因为我喜欢这里列出的一些答案,但也因为有一个答案涵盖了我对这个问题所需要的一切,这很好。
最明显的例子:
所以,不要平均RGB。平均RGB是完全错误的!
(好吧,你是对的,平均可能在一些不起眼的应用程序中有效,即使当RGB值被视为颜色时它没有物理或生理意义。顺便说一句,加权平均的“常规”方式也是错误的因为伽玛,一个更微妙的方式。应首先将sRGB线性化,然后将最终结果转换回sRGB(这相当于检索Lab颜色空间中的L分量))
您可以使用任何转换方程,比例,线性。你找到的那个:
基于平均人眼“平均”原色(R,G,B)感知灵敏度(至少对于它创建的时间段和人口/ HW;记住这些标准是在LED,TFT等之前创建的。屏幕)。
你正在与几个问题作斗争:
I = 0.299 R + 0.587 G + 0.114 B
峰值对于不同的人可以处于不同的波长(像我一样,我将它们移动了一点,导致识别某些波长的差异,如某些色调的Aqua - 有些人认为它们是绿色的,有些甚至是蓝色的,即使它们都没有任何颜色失明残疾或其他)。如果将所有这些结合在一起,您可以使用平均值......或特殊(和昂贵)设备来衡量/标准化某些标准或针对校准人员(取决于行业)。
但这在家庭条件下要处理得太多,所以留下所有这些用于工业,并像世界上大多数人一样使用“平均”的权重......幸运的是我们的大脑可以处理它,因为除非你开始比较两个图像,否则你看不出差异并排或动画:)。所以我(会)这样做:
just use a spectroscope and see
有许多不同的转换为灰度的方法,虽然使用不同的输入彩色图像可能更容易看到差异,但它们确实给出了不同的结果。
正如我们在灰度中看不到的那样,“最佳”方法在某种程度上取决于应用程序,并且在某种程度上取决于旁观者。
您提到的替代公式是基于人眼对绿色调的变化更敏感,因此给予它们更大的权重 - 类似于相机中的拜耳阵列,其中每个红色和蓝色的像素有2个绿色像素。 I = 0.299 R + 0.587 G + 0.114 B
R = I
G = I
B = I
Luminance有很多公式,取决于R,G,B颜色的原色:
Wiki - Bayer array
这都是因为我们的眼睛对蓝色比对红色的敏感度低于对绿色的敏感度。
话虽如此,你可能正在计算Luma,而不是Luminance,所以无论如何公式都是错误的。对于恒亮度,您必须转换为线性光
Rec.601/NTSC: Y = 0.299*R + 0.587*G + 0.114*B ,
Rec.709/EBU: Y = 0.213*R + 0.715*G + 0.072*B ,
Rec.2020/UHD: Y = 0.263*R + 0.678*G + 0.059*B .
应用亮度公式,并转换回伽玛域
R = R' ^ 2.4 , G = G' ^ 2.4 , B = B' ^ 2.4 ,
此外,考虑将3D颜色空间转换为1D数量会丢失2/3的信息,这可能会在下一个处理步骤中咬你。根据问题,有时不同的公式更好,如V = MAX(R,G,B)(来自HSV颜色空间)。
我怎么知道?我是Poynton博士的追随者和朋友。
提供的答案已经足够,但我想以不同的方式讨论这个主题。
自从我学习数字绘画以来,我更常使用HSV。
在绘画过程中使用HSV更加可控,但保持简短,主要是S:饱和度将颜色与光的概念分开。并且将S转为0,已经是“计算机”灰度图像。
Y' = Y ^ (1/2.4) .
这种方法真正保留了原始颜色的“明亮”。但是,不考虑人眼如何处理数据。
在回答您的主要问题时,使用任何单一灰度测量都存在缺点。这取决于你想要的图像。例如,如果您在白色背景上有彩色文本,如果您想使文本突出,您可以使用最小值r,g,b值作为度量。但是,如果彩色背景上有黑色文本,则可以使用相同结果的最大值。在我的软件中,我提供了最大,最小或中值的选项供用户选择。连续色调图像的结果也很有启发性。在回应要求更多细节的评论时,像素的代码低于(没有任何防御措施)。
from PIL import Image
import colorsys
def togrey(img):
if isinstance(img,Image.Image):
r,g,b = img.split()
R = []
G = []
B = []
for rd,gn,bl in zip(r.getdata(),g.getdata(),b.getdata()) :
h,s,v = colorsys.rgb_to_hsv(rd/255.,gn/255.,bl/255.)
s = 0
_r,_g,_b = colorsys.hsv_to_rgb(h,s,v)
R.append(int(_r*255.))
G.append(int(_g*255.))
B.append(int(_b*255.))
r.putdata(R)
g.putdata(G)
b.putdata(B)
return Image.merge('RGB',(r,g,b))
else:
return None
a = Image.open('../a.jpg')
b = togrey(a)
b.save('../b.jpg')