IT TIP

거친 그레이 스케일 알고리즘 사용에 문제가 있습니까?

itqueen 2020. 12. 3. 21:32
반응형

거친 그레이 스케일 알고리즘 사용에 문제가 있습니까?


그래서 저는 python사용 중에 사진을 편집하기위한 몇 가지 프로그램을 설계 PIL중이며 그중 하나는 이미지를 그레이 스케일로 변환하는 것입니다 (에서 모든 기능을 사용하지 않습니다 PIL).

내가 사용한 알고리즘은 간단합니다. 각 픽셀 (색상 깊이는 24)에 대해 R, GB의 평균을 계산 하고 RGB 값을이 평균으로 설정했습니다.

내 프로그램은 정확 해 보이는 그레이 스케일 이미지를 생성하고 있었지만 올바른 알고리즘을 사용했는지 궁금 해서 '올바른'알고리즘이 계산하는 것으로 보이는 질문에 대한 을 찾았습니다 0.299 R + 0.587 G + 0.114 B.

내 프로그램을이 알고리즘과 비교하기로 결정했습니다. 내 프로그램을 사용하여 그레이 스케일 이미지를 생성하고 온라인 웹 사이트 에서 다른 이미지 (동일한 입력 사용)를 생성했습니다 ( 'image to grayscale'.

육안으로는 똑같은 것 같았고, 차이가 있으면 볼 수 없었습니다. 그러나이 웹 사이트 (에 대한 Google 상위 결과 'compare two images online')를 사용하여 그레이 스케일 이미지를 비교 하기로 결정했습니다 . 픽셀 깊숙한 곳에서 약간의 변화가 있었지만 한눈에 인간의 눈으로 인식 할 수있는 것은 없었습니다 (차이는 발견 할 수 있지만 일반적으로 이미지가 서로 위에 놓여 있거나 밀리 초 이내에 전환 될 때만) .

내 질문 (첫 번째는 주요 질문) :

  1. 내 '거친'그레이 스케일 알고리즘을 사용하는 데 단점이 있습니까?
  2. 누구든지 내 그레이 스케일 알고리즘이 '올바른'이미지와 눈에 띄게 다른 이미지를 생성하는 입력 이미지가 있습니까?
  3. 내 알고리즘이 잘 작동하지 않는 색상 / RBG 조합이 있습니까?

내 핵심 코드 (필요한 경우) :

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 %의 퍼즈를 사용하여 차이입니다) : 그레이 스케일 이미지를 온라인에서 비교할 때 (빨간색으로 강조 표시된 부분은 10 %의 퍼즈를 사용하여 차이입니다)

위에 강조 표시된 픽셀의 차이에도 불구하고 위의 그레이 스케일 이미지는 거의 똑같이 보입니다 (적어도 나에게는).

또한 내 첫 번째 질문과 관련하여 관심있는 사람이 있다면 이 사이트 는 그레이 스케일로의 변환을 위해 다양한 알고리즘에 대한 분석을 수행했으며 일부 사용자 지정 알고리즘도 있습니다.

편집 :

@Szulat의 답변에 대한 응답으로 내 알고리즘은 실제로이 이미지를 대신 생성합니다 (잘못 자르기는 무시하고 원본 이미지에는 세 개의 원이 있지만 첫 번째 원만 필요했습니다).

이것이 내 알고리즘이 ** 실제로 ** 생성하는 것입니다.

사람들이 그레이 스케일로 변환하는 이유가 무엇인지 궁금해하는 경우 (알고리즘이 목적에 따라 달라지는 것처럼 보임), 저는 python미니 포토샵을 가질 수 있도록 몇 가지 간단한 사진 편집 도구를 만들고 있습니다. 필터와 효과를 적용하려면 인터넷에 의존해야합니다.

현상금 이유 : 여기에있는 다른 답변은 서로 관련이 있고 도움이되는 다양한 내용을 다루고 있습니다. 이로 인해 받아 들일 답변을 선택하기가 매우 어렵습니다. 나는 여기에 나열된 몇 가지 답변을 좋아하기 때문에 현상금을 시작했지만이 질문에 필요한 모든 것을 포함하는 단일 답변이 있으면 좋을 것입니다.


이미지는 매우 비슷해 보이지만 특히 다른 하나를 대신 사용하면 눈으로 차이를 알 수 있습니다.

여기에 이미지 설명 입력

예를 들어, 평균 변환에서 배경의 꽃이 더 밝게 보입니다.

세 채널의 평균을 구하는 데 본질적으로 "나쁜"것이 없다는 것은 아닙니다. 이 공식의 이유는 우리가 빨강, 초록, 파랑을 똑같이 인식하지 못하기 때문입니다. 따라서 그레이 스케일 이미지의 강도에 대한 기여도가 동일하지 않아야합니다. 녹색을 더 강하게 인식하기 때문에 녹색 픽셀은 회색조에서 더 밝게 보입니다. 그러나 Mark언급 했듯이 우리는 색상으로 볼 수 있고 어떤 경우에도 모든 사람의 비전이 약간 다르기 때문에 그레이 스케일로의 고유 한 완벽한 변환은 없습니다. 따라서 모든 공식은 근사값을 만들어서 대부분의 픽셀 강도가 "올바른"느낌을줍니다. 사람들.


가장 확실한 예 :

  1. 실물

  2. Gimp에서 채도 감소 (밝기 모드-알고리즘이 수행하는 작업입니다)

  3. 김프에서 채도가 떨어짐 (광도 모드-이것이 우리 눈이하는 일입니다)

김프 채도 감소 : 밝기 대 광도

따라서 평균 RGB를 사용하지 마십시오. 평균 RGB는 단순히 잘못되었습니다!

(좋습니다. 평균화는 일부 애매한 응용 프로그램에서 유효 할 수 있습니다. RGB 값이 색상으로 취급 될 때 물리적 또는 생리적 의미가 없더라도 가중 평균화를 수행하는 "일반적인"방법도 올바르지 않습니다. 감마 때문에 더 미묘한 방식으로. sRGB는 먼저 선형화되어야하며 최종 결과는 sRGB로 다시 변환되어야합니다 (Lab 색상 공간에서 L 구성 요소를 검색하는 것과 동일).


모든 변환 방정식, 규모, 선형성을 사용할 수 있습니다. 당신이 찾은 것 :

I = 0.299 R + 0.587 G + 0.114 B

평균 인간의 눈 "평균"원색 (R, G, B) 인식 감도 (적어도 생성 된 기간 및 인구 / HW에 대해)를 기반으로합니다. 이러한 표준은 LED, TFT 등 이전에 만들어졌습니다. 스크린).

싸우고있는 몇 가지 문제가 있습니다.

  1. 우리 눈은 같지 않아

    모든 인간은 색을 같은 방식으로 인식하지 않습니다. 성별간에 큰 차이가 있고 지역 간에도 작은 차이가 있습니다. 세대와 나이조차도 역할을합니다. 따라서 평균도 "평균"으로 처리되어야합니다.

    우리는 가시 스펙트럼에서 빛의 강도에 대해 서로 다른 민감도를 가지고 있습니다. 가장 민감한 색상은 녹색입니다 (따라서 가장 높은 가중치). 그러나 XYZ 곡선 피크는 사람들마다 다른 파장에있을 수 있습니다. 색맹 장애 또는 기타).

  2. 모니터는 동일한 파장이나 스펙트럼 분산을 사용하지 않습니다.

    So if you take 2 different monitors, they might use slightly different wavelengths for R, G, B or even different widths of the spectral filter (just use a spectroscope and see). Yes they should be "normalized" by the HW but that is not the same as using normalized wavelengths. It is similar to problems using RGB vs. White Noise spectrum light sources.

  3. monitor linearity

    Humans do not see on a linear scale: we are usually logarithmic/exponential (depends how you look at it) so yes we can normalize that with HW (or even SW) but the problem is if we linearize for one human then means we damage it for another.

If you take all this together you can either use averages ... or special (and expensive) equipment to measure/normalize against some standard or against a calibrated person (depends on the industry).

But that is too much to handle in home conditions so leave all that for industry and use the weights for "average" like most of the world... Luckily our brain can handle it as you cannot see the difference unless you start comparing both images side by side or in an animation :). So I (would) do:

I = 0.299 R + 0.587 G + 0.114 B
R = I
G = I
B = I

There are many different methods for converting to greyscale, and they do give different results though the differences might be easier to see with different input colour images.

As we don't really see in greyscale, the "best" method is somewhat dependent on the application and somewhat in the eye of the beholder.

The alternative formula you refer to is based on the human eye being more sensitive to variations in green tones and therefore giving them a bigger weighting - similarly to a Bayer array in a camera where there are 2 green pixels for each red and blue one. Wiki - Bayer array


There are many formulas for the Luminance, depending on the R,G,B color primaries:

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 . 

This is all because our eyes are less sensitive to blue than to red than to green.

That being said, you are probably calculating Luma, not Luminance, so the formulas are all wrong anyway. For Constant-Luminance you must convert to linear-light

R = R' ^ 2.4 , G = G' ^ 2.4 , B = B' ^ 2.4 , 

apply the Luminance formula, and convert back to the gamma domain

Y' = Y ^ (1/2.4) . 

Also, consider that converting a 3D color space to a 1D quantity loses 2/3 of the information, which can bite you in the next processing steps. Depending on the problem, sometimes a different formula is better, like V = MAX(R,G,B) (from HSV color space).

How do I know? I'm a follower and friend of Dr. Poynton.


The answers provided are enough, but I want to discuss a bit more on this topic in a different manner.

Since I learnt digital painting for interest, more often I use HSV.

It is much more controllable for using HSV during painting, but keep it short, the main point is the S: Saturation separating the concept of color from the light. And turning S to 0, is already the 'computer' grey scale of image.

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')

This method truly reserved the 'bright' of original color. However, without considering how human eye process the data.


In answer to your main question, there are disadvantages in using any single measure of grey. It depends on what you want from your image. For example, if you have colored text on white background, if you want to make the text stand out you can use the minimum of the r, g, b values as your measure. But if you have black text on a colored background, you can use the maximum of the values for the same result. In my software I offer the option of max, min or median value for the user to choose. The results on continuous tone images are also illuminating. In response to comments asking for more details, the code for a pixel is below (without any defensive measures).

int Ind0[3] = {0, 1, 2};                 //all equal
int Ind1[3] = {2, 1, 0};                 // top, mid ,bot from mask...
int Ind2[3] = {1, 0, 2};
int Ind3[3] = {1, 2, 0};
int Ind4[3] = {0, 2, 1};
int Ind5[3] = {2, 0, 1};
int Ind6[3] = {0, 1, 2};
int Ind7[3] = {-1, -1, -1};              // not possible
int *Inds[8] = {Ind0, Ind1, Ind2, Ind3, Ind4, Ind5, Ind6, Ind7};
void grecolor(unsigned char *rgb, int bri, unsigned char *grey)
{                         //pick out bot, mid or top according to bri flag
    int r = rgb[0];
    int g = rgb[1];
    int b = rgb[2];
    int mask = 0;
    mask |= (r > g);
    mask <<= 1;
    mask |= (g > b);
    mask <<= 1;
    mask |= (b > r);
    grey[0] = rgb[Inds[mask][2 - bri]];  // 2, 1, 0 give bot, mid, top
}

참고 URL : https://stackoverflow.com/questions/51818193/problems-with-using-a-rough-greyscale-algorithm

반응형