Adi*_*219 60 python algorithm image-comparison image-conversion python-imaging-library
所以我设计了编辑照片的几个方案中python使用PIL,其中之一是将图像转换为灰度图像(我避免因使用任何功能PIL).
我已经采用的算法是简单的:对于每个像素(色彩深度为24),我已经计算出的平均R,G和B值,并设置RGB值到该平均.
我的程序生成的灰度图像似乎很准确,但我想知道我是否使用了正确的算法,并且我遇到了一个问题的答案,似乎"正确"的算法是计算的0.299 R + 0.587 G + 0.114 B.
我决定将我的程序与这个算法进行比较.我使用我的程序生成了一个灰度图像,另一个(使用相同的输入)来自一个网站(谷歌的最高结果)'image to grayscale'.
在我的肉眼看来,它们似乎完全相同,如果有任何变化,我看不到它.但是,我决定使用这个网站(谷歌的最佳结果'compare two images online')来比较我的灰度图像.事实证明,在像素的深处,它们有轻微的变化,但没有一个人乍一看是可感知的(差异可以被发现,但通常只有当图像相互叠加或在几毫秒之间切换时) .
我的问题(第一个是主要问题):
我的关键代码(如果需要):
def greyScale(pixelTuple):
return tuple([round(sum(pixelTuple) / 3)] * 3)
Run Code Online (Sandbox Code Playgroud)
'正确'的算法(似乎重量很重):
def greyScale(pixelTuple):
return tuple([round(0.299 * pixelTuple[0] + 0.587 * pixelTuple[1] + 0.114 * pixelTuple[2])] * 3)
Run Code Online (Sandbox Code Playgroud)
当在线比较灰度图像时(突出显示红色是差异,使用10%的模糊):

尽管上面突出显示了像素的变化,但上面的灰度图像看起来几乎完全相同(至少对我而言).
另外,关于我的第一个问题,如果有人感兴趣,这个网站已经对转换为灰度的不同算法做了一些分析,并且还有一些自定义算法.
编辑:
为了回应@ Szulat的回答,我的算法实际上生成了这个图像(忽略了糟糕的裁剪,原始图像有三个圆圈,但我只需要第一个):
如果人们想知道转换为灰度的原因是什么(因为看起来算法取决于目的),我只是制作了一些简单的照片编辑工具,python以便我可以使用迷你Photoshop而不是需要依靠互联网来应用过滤器和效果.
赏金的原因:这里的不同答案涵盖了不同的事物,这些都是相关的和有用的.这使得选择接受哪个答案变得非常困难.我已经开始获得赏金,因为我喜欢这里列出的一些答案,但也因为有一个答案涵盖了我对这个问题所需要的一切,这很好.
jde*_*esa 46
这些图像看起来非常相似,但是你的眼睛可以区分它们,特别是如果你用一个代替另一个:
例如,您可以注意到背景中的花在平均转换中看起来更亮.
并不是说平均三个频道有任何本质上"坏"的东西.这个公式的原因是我们不能同等地感知红色,绿色和蓝色,因此它们对灰度图像中强度的贡献应该不相同; 因为我们更强烈地感知绿色,所以绿色像素在灰度级上应该看起来更亮.然而,正如Mark所评论的,没有独特的完美转换为灰度,因为我们看到的是彩色,并且无论如何每个人的视觉都略有不同,所以任何公式都会尝试进行近似,因此对于大多数人而言像素强度感觉"正确"人.
szu*_*lat 42
最明显的例子:
原版的
在Gimp中去饱和(亮度模式 - 这是你的算法所做的)
在Gimp中去饱和(亮度模式 - 这是我们的眼睛所做的)
所以,不要平均RGB.平均RGB是完全错误的!
(好吧,你是对的,平均可能在一些不起眼的应用程序中有效,即使当RGB值被视为颜色时它没有物理或生理意义.顺便说一句,加权平均的"常规"方式也是错误的在因为伽玛的更微妙的方式.sRGB的应该是第一线性化,然后将最终的结果转换回为sRGB(这将是等效检索在Lab颜色空间中的L成分的))
Spe*_*tre 19
您可以使用任何转换方程,比例,线性.你找到的那个:
I = 0.299 R + 0.587 G + 0.114 B
Run Code Online (Sandbox Code Playgroud)
基于平均人眼"平均"原色(R,G,B)感知灵敏度(至少对于它创建的时间段和人口/ HW;记住这些标准是在LED,TFT等之前创建的.屏幕).
你正在与几个问题作斗争:
我们的眼睛不一样
所有人都不会以同样的方式感知颜色.性别之间存在重大差异,区域之间也存在较小差异; 甚至一代人和年龄都发挥了作用.因此,即使平均值也应该被视为"平均值".
我们对可见光谱中的光强度有不同的敏感性.最敏感的颜色是绿色(因此它的重量最高).但是对于不同的人来说,XYZ曲线的峰值可以处于不同的波长(就像我一样,我将它们移动了一点,导致识别某些波长的差异,比如一些Aqua色调 - 有些人认为它们是绿色的,有些甚至是蓝色的,即使它们都没有色盲或其他什么).
监视器不使用相同的波长,也不使用光谱色散
因此,如果您使用2个不同的显示器,他们可能会使用稍微不同的波长用于R,G,B甚至不同宽度的光谱滤波器(只需使用光谱仪并查看).是的,他们应该由HW"标准化",但这与使用标准化波长不同.它类似于使用RGB与白噪声光谱光源的问题.
监控线性度
人类没有看到线性尺度:我们通常是对数/指数(取决于你如何看待它)所以是的我们可以用HW(甚至SW)对其进行标准化,但问题是如果我们对一个人进行线性化则意味着我们会损坏它是另一个.
如果将所有这些结合在一起,您可以使用平均值......或特殊(和昂贵)设备来衡量/标准化某些标准或针对校准人员(取决于行业).
但这在家庭条件下要处理得太多,所以留下所有这些用于工业,并像世界上大多数人一样使用"平均"的权重......幸运的是我们的大脑可以处理它,因为除非你开始比较两个图像,否则你看不出差异并排或动画:).所以我(会)这样做:
I = 0.299 R + 0.587 G + 0.114 B
R = I
G = I
B = I
Run Code Online (Sandbox Code Playgroud)
小智 8
Luminance有很多公式,取决于R,G,B颜色的原色:
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 .
Run Code Online (Sandbox Code Playgroud)
这都是因为我们的眼睛对蓝色比对红色的敏感度低于对绿色的敏感度.
话虽如此,你可能正在计算Luma,而不是Luminance,所以无论如何公式都是错误的.对于恒亮度,您必须转换为线性光
R = R' ^ 2.4 , G = G' ^ 2.4 , B = B' ^ 2.4 ,
Run Code Online (Sandbox Code Playgroud)
应用亮度公式,并转换回伽玛域
Y' = Y ^ (1/2.4) .
Run Code Online (Sandbox Code Playgroud)
此外,考虑将3D颜色空间转换为1D数量会丢失2/3的信息,这可能会在下一个处理步骤中咬你.根据问题,有时不同的公式更好,如V = MAX(R,G,B)(来自HSV颜色空间).
我怎么知道?我是Poynton博士的追随者和朋友.
提供的答案已经足够,但我想以不同的方式讨论这个主题.
自从我学习数字绘画以来,我更常使用HSV.
在绘画过程中使用HSV更加可控,但保持简短,主要是S:饱和度将颜色与光的概念分开.并且将S转为0,已经是"计算机"灰度图像.
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')
Run Code Online (Sandbox Code Playgroud)
这种方法真正保留了原始颜色的"明亮".但是,不考虑人眼如何处理数据.