광 주파수를 RGB로 변환 하시겠습니까?


답변:


44

다음은 전체 변환 프로세스에 대한 자세한 설명입니다. http://www.fourmilab.ch/documents/specrend/ . 소스 코드 포함!


5
Fourmilab 기사는 우리의 물리학 교사가 말한대로 세 가지 기본 색상을 함께 추가하여 임의의 색상을 "만들"수 없기 때문에 일부 색상은 RGB (밝은 오렌지색이 좋은 예입니다)로 표현할 수 없다는 중요한 점을 지적합니다. 잘 했어). 너무 나쁘지만 실제로는 일반적으로 치명적이지는 않습니다.
Francis Davey

1
그 외에도 : en.wikipedia.org/wiki/Srgb 이 기사는 sRGB 표준이 널리 채택되기 전에 작성되었습니다. 또한 "계산은 2 ° 표준 색도계 관찰자를 가정합니다."라는 문구에 유의하십시오. 이는 CIE 1964가 아니라 논문에 첨부 된 출처에서 발견 된 CIE 1931 표를 사용해야 함을 의미합니다.
GrayFace

코드 사용 방법에 대한 몇 가지 예를 제공하면 좋을 것입니다. 인수로 기능이 필요하고 온도를 사용하여 색상 등을 계산합니다. 작업을 위해 무엇을 삭제하고 변경해야하는지 알고 있으면 기뻐할 것입니다.
토마스 Zato - 분석 재개 모니카

2
가능한 모든 가시 파장의 일부만 RGB 색상 공간에서 정확하게 표현할 수 있다는 점은 주목할 가치가 있습니다. 변환 프로세스는 매우 복잡하고 모호합니다. physics.stackexchange.com/a/94446/5089physics.stackexchange.com/a/419628/5089
Violet Giraffe

28

게으른 사람들 (나와 같은)의 경우 @ user151323의 답변에서 찾은 코드의 Java 구현이 있습니다 (즉 Spectra Lab Report 에서 찾은 파스칼 코드의 간단한 번역 ).

static private final double Gamma = 0.80;
static private final double IntensityMax = 255;

/**
 * Taken from Earl F. Glynn's web page:
 * <a href="http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm">Spectra Lab Report</a>
 */
public static int[] waveLengthToRGB(double Wavelength) {
    double factor;
    double Red, Green, Blue;

    if((Wavelength >= 380) && (Wavelength < 440)) {
        Red = -(Wavelength - 440) / (440 - 380);
        Green = 0.0;
        Blue = 1.0;
    } else if((Wavelength >= 440) && (Wavelength < 490)) {
        Red = 0.0;
        Green = (Wavelength - 440) / (490 - 440);
        Blue = 1.0;
    } else if((Wavelength >= 490) && (Wavelength < 510)) {
        Red = 0.0;
        Green = 1.0;
        Blue = -(Wavelength - 510) / (510 - 490);
    } else if((Wavelength >= 510) && (Wavelength < 580)) {
        Red = (Wavelength - 510) / (580 - 510);
        Green = 1.0;
        Blue = 0.0;
    } else if((Wavelength >= 580) && (Wavelength < 645)) {
        Red = 1.0;
        Green = -(Wavelength - 645) / (645 - 580);
        Blue = 0.0;
    } else if((Wavelength >= 645) && (Wavelength < 781)) {
        Red = 1.0;
        Green = 0.0;
        Blue = 0.0;
    } else {
        Red = 0.0;
        Green = 0.0;
        Blue = 0.0;
    }

    // Let the intensity fall off near the vision limits

    if((Wavelength >= 380) && (Wavelength < 420)) {
        factor = 0.3 + 0.7 * (Wavelength - 380) / (420 - 380);
    } else if((Wavelength >= 420) && (Wavelength < 701)) {
        factor = 1.0;
    } else if((Wavelength >= 701) && (Wavelength < 781)) {
        factor = 0.3 + 0.7 * (780 - Wavelength) / (780 - 700);
    } else {
        factor = 0.0;
    }


    int[] rgb = new int[3];

    // Don't want 0^x = 1 for x <> 0
    rgb[0] = Red == 0.0 ? 0 : (int)Math.round(IntensityMax * Math.pow(Red * factor, Gamma));
    rgb[1] = Green == 0.0 ? 0 : (int)Math.round(IntensityMax * Math.pow(Green * factor, Gamma));
    rgb[2] = Blue == 0.0 ? 0 : (int)Math.round(IntensityMax * Math.pow(Blue * factor, Gamma));

    return rgb;
}

3
코드에 버그가있는 것 같습니다. 예를 들어 파장이 439.5이면 함수는 검은 색을 반환합니다. 사이트의 원래 코드는 정수로 작동하고 있다고 믿습니다 (나는 파스칼을 전혀 모릅니다). 로 변경 Wavelength<=439하는 것이 좋습니다 Wavelength<440.
Hassedev 2013

2
네가 옳아! 이것을 지적 해 주셔서 감사합니다. :) 이미 수정되었습니다.
Tarc 2013 년

일부 주파수에 대해 RFB를 반복 할 것으로 예상됩니까? (빨간색) : 652-rgb (255, 0, 0) | 660-rgb (255, 0, 0) | 692-rgb (255, 0, 0) | 700-rgb (255, 0, 0) | ...
Rodrigo Borba

14

일반적인 아이디어:

  1. CEI 색상 일치 기능 을 사용 하여 파장을 XYZ 색상 으로 변환 합니다 .
  2. XYZ를 RGB로 변환
  3. 구성 요소를 [0..1]로 자르고 부호없는 바이트 범위에 맞게 255를 곱합니다.

1 단계와 2 단계는 다를 수 있습니다.

또는 분석적 근사치 (@Tarc 및 @Haochen Xie에서 제안)로 사용할 수있는 몇 가지 색상 일치 함수가 있습니다 . 부드러운 정확한 결과가 필요한 경우 테이블이 가장 좋습니다.

단일 RGB 색 공간이 없습니다. 여러 변환 행렬 과 다른 종류의 감마 보정을 사용할 수 있습니다.

아래는 내가 최근에 생각 해낸 C # 코드입니다. "CIE 1964 표준 관찰자"테이블 및 sRGB 매트릭스 + 감마 보정에 대한 선형 보간을 사용합니다 .

static class RgbCalculator {

    const int
         LEN_MIN = 380,
         LEN_MAX = 780,
         LEN_STEP = 5;

    static readonly double[]
        X = {
                0.000160, 0.000662, 0.002362, 0.007242, 0.019110, 0.043400, 0.084736, 0.140638, 0.204492, 0.264737,
                0.314679, 0.357719, 0.383734, 0.386726, 0.370702, 0.342957, 0.302273, 0.254085, 0.195618, 0.132349,
                0.080507, 0.041072, 0.016172, 0.005132, 0.003816, 0.015444, 0.037465, 0.071358, 0.117749, 0.172953,
                0.236491, 0.304213, 0.376772, 0.451584, 0.529826, 0.616053, 0.705224, 0.793832, 0.878655, 0.951162,
                1.014160, 1.074300, 1.118520, 1.134300, 1.123990, 1.089100, 1.030480, 0.950740, 0.856297, 0.754930,
                0.647467, 0.535110, 0.431567, 0.343690, 0.268329, 0.204300, 0.152568, 0.112210, 0.081261, 0.057930,
                0.040851, 0.028623, 0.019941, 0.013842, 0.009577, 0.006605, 0.004553, 0.003145, 0.002175, 0.001506,
                0.001045, 0.000727, 0.000508, 0.000356, 0.000251, 0.000178, 0.000126, 0.000090, 0.000065, 0.000046,
                0.000033
            },

        Y = {
                0.000017, 0.000072, 0.000253, 0.000769, 0.002004, 0.004509, 0.008756, 0.014456, 0.021391, 0.029497,
                0.038676, 0.049602, 0.062077, 0.074704, 0.089456, 0.106256, 0.128201, 0.152761, 0.185190, 0.219940,
                0.253589, 0.297665, 0.339133, 0.395379, 0.460777, 0.531360, 0.606741, 0.685660, 0.761757, 0.823330,
                0.875211, 0.923810, 0.961988, 0.982200, 0.991761, 0.999110, 0.997340, 0.982380, 0.955552, 0.915175,
                0.868934, 0.825623, 0.777405, 0.720353, 0.658341, 0.593878, 0.527963, 0.461834, 0.398057, 0.339554,
                0.283493, 0.228254, 0.179828, 0.140211, 0.107633, 0.081187, 0.060281, 0.044096, 0.031800, 0.022602,
                0.015905, 0.011130, 0.007749, 0.005375, 0.003718, 0.002565, 0.001768, 0.001222, 0.000846, 0.000586,
                0.000407, 0.000284, 0.000199, 0.000140, 0.000098, 0.000070, 0.000050, 0.000036, 0.000025, 0.000018,
                0.000013
            },

        Z = {
                0.000705, 0.002928, 0.010482, 0.032344, 0.086011, 0.197120, 0.389366, 0.656760, 0.972542, 1.282500,
                1.553480, 1.798500, 1.967280, 2.027300, 1.994800, 1.900700, 1.745370, 1.554900, 1.317560, 1.030200,
                0.772125, 0.570060, 0.415254, 0.302356, 0.218502, 0.159249, 0.112044, 0.082248, 0.060709, 0.043050,
                0.030451, 0.020584, 0.013676, 0.007918, 0.003988, 0.001091, 0.000000, 0.000000, 0.000000, 0.000000,
                0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
                0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
                0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
                0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
                0.000000
            };

    static readonly double[]
        MATRIX_SRGB_D65 = {
             3.2404542, -1.5371385, -0.4985314,
            -0.9692660,  1.8760108,  0.0415560,
             0.0556434, -0.2040259,  1.0572252
        };

    public static byte[] Calc(double len) {
        if(len < LEN_MIN || len > LEN_MAX)
            return new byte[3];

        len -= LEN_MIN;
        var index = (int)Math.Floor(len / LEN_STEP);
        var offset = len - LEN_STEP * index;

        var x = Interpolate(X, index, offset);
        var y = Interpolate(Y, index, offset);
        var z = Interpolate(Z, index, offset);

        var m = MATRIX_SRGB_D65;

        var r = m[0] * x + m[1] * y + m[2] * z;
        var g = m[3] * x + m[4] * y + m[5] * z;
        var b = m[6] * x + m[7] * y + m[8] * z;

        r = Clip(GammaCorrect_sRGB(r));
        g = Clip(GammaCorrect_sRGB(g));
        b = Clip(GammaCorrect_sRGB(b));

        return new[] { 
            (byte)(255 * r),
            (byte)(255 * g),
            (byte)(255 * b)
        };
    }

    static double Interpolate(double[] values, int index, double offset) {
        if(offset == 0)
            return values[index];

        var x0 = index * LEN_STEP;
        var x1 = x0 + LEN_STEP;
        var y0 = values[index];
        var y1 = values[1 + index];

        return y0 + offset * (y1 - y0) / (x1 - x0);
    }

    static double GammaCorrect_sRGB(double c) {
        if(c <= 0.0031308)
            return 12.92 * c;

        var a = 0.055;
        return (1 + a) * Math.Pow(c, 1 / 2.4) - a;
    }

    static double Clip(double c) {
        if(c < 0)
            return 0;
        if(c > 1)
            return 1;
        return c;
    }
}

400-700 nm 범위의 결과 :

여기에 이미지 설명 입력


이것은 나에게 정말 흥미 롭습니다. 나는 이와 같은 것을 사용하여 정상적인 응답을 제공하는 아이디어가 있지만, WXYZ 응답을 사용하여 다른 정상적인 세 가지 유형의 원뿔에서 충분히 멀리 떨어진 주파수에 반응하는 네 번째 원뿔을 가진 사색 체의 반응을 모방합니다. 그러면 소스 이미지를 가져와 그들이 보는 차이점을 추론 할 수 있습니다. NB 그들은 새로운 색상을 보지 못합니다. 예를 들어 특정 노란색에 대한 빛은 우리 대부분에게 특정 주파수의 노란색과 동일하게 보이지만 그들에게는 빛이 섞이지 않습니다. 그 노란색으로.
phorgan1 2016

물론 특정 RGB 색상의 경우 여러 가지 방법으로 도달했을 수 있습니다. 잎의 초록색은 초록색을 제외한 모든 것에서 필터링되거나 초록색이 필터링되었을 수 있지만 나노 특성으로 인해 파란색과 노란색이 반사되어 녹색과 동일하게 보일 수 있습니다. 빛이 아닌 이미지가 주어지면 차별화 할 수있는 방법이 있을까요?
phorgan1 2016

10

이것은 오래된 질문이고 이미 몇 가지 좋은 대답을 얻었지만 내 응용 프로그램에서 이러한 변환 기능을 구현하려고 할 때 이미 여기에 나열된 알고리즘에 만족하지 않았고 자체 조사를 수행하여 좋은 결과를 얻었습니다. 그래서 저는 새로운 답변을 게시 할 것입니다.

몇 가지 연구 끝에 CIE XYZ Color Matching Functions에 대한 Simple Analytic Approximations라는 논문 을 접하고 도입 된 다중 로브 조각 별 가우스 피팅 알고리즘을 애플리케이션에 채택하려고했습니다. 이 논문에서는 파장을 해당 XYZ 값 으로 변환하는 기능 만 설명 했기 때문에 sRGB 색 공간에서 XYZ를 RGB로 변환하는 기능을 구현하여 결합했습니다. 결과는 환상적이고 공유 할 가치가 있습니다.

/**
 * Convert a wavelength in the visible light spectrum to a RGB color value that is suitable to be displayed on a
 * monitor
 *
 * @param wavelength wavelength in nm
 * @return RGB color encoded in int. each color is represented with 8 bits and has a layout of
 * 00000000RRRRRRRRGGGGGGGGBBBBBBBB where MSB is at the leftmost
 */
public static int wavelengthToRGB(double wavelength){
    double[] xyz = cie1931WavelengthToXYZFit(wavelength);
    double[] rgb = srgbXYZ2RGB(xyz);

    int c = 0;
    c |= (((int) (rgb[0] * 0xFF)) & 0xFF) << 16;
    c |= (((int) (rgb[1] * 0xFF)) & 0xFF) << 8;
    c |= (((int) (rgb[2] * 0xFF)) & 0xFF) << 0;

    return c;
}

/**
 * Convert XYZ to RGB in the sRGB color space
 * <p>
 * The conversion matrix and color component transfer function is taken from http://www.color.org/srgb.pdf, which
 * follows the International Electrotechnical Commission standard IEC 61966-2-1 "Multimedia systems and equipment -
 * Colour measurement and management - Part 2-1: Colour management - Default RGB colour space - sRGB"
 *
 * @param xyz XYZ values in a double array in the order of X, Y, Z. each value in the range of [0.0, 1.0]
 * @return RGB values in a double array, in the order of R, G, B. each value in the range of [0.0, 1.0]
 */
public static double[] srgbXYZ2RGB(double[] xyz) {
    double x = xyz[0];
    double y = xyz[1];
    double z = xyz[2];

    double rl =  3.2406255 * x + -1.537208  * y + -0.4986286 * z;
    double gl = -0.9689307 * x +  1.8757561 * y +  0.0415175 * z;
    double bl =  0.0557101 * x + -0.2040211 * y +  1.0569959 * z;

    return new double[] {
            srgbXYZ2RGBPostprocess(rl),
            srgbXYZ2RGBPostprocess(gl),
            srgbXYZ2RGBPostprocess(bl)
    };
}

/**
 * helper function for {@link #srgbXYZ2RGB(double[])}
 */
private static double srgbXYZ2RGBPostprocess(double c) {
    // clip if c is out of range
    c = c > 1 ? 1 : (c < 0 ? 0 : c);

    // apply the color component transfer function
    c = c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1. / 2.4) - 0.055;

    return c;
}

/**
 * A multi-lobe, piecewise Gaussian fit of CIE 1931 XYZ Color Matching Functions by Wyman el al. from Nvidia. The
 * code here is adopted from the Listing 1 of the paper authored by Wyman et al.
 * <p>
 * Reference: Chris Wyman, Peter-Pike Sloan, and Peter Shirley, Simple Analytic Approximations to the CIE XYZ Color
 * Matching Functions, Journal of Computer Graphics Techniques (JCGT), vol. 2, no. 2, 1-11, 2013.
 *
 * @param wavelength wavelength in nm
 * @return XYZ in a double array in the order of X, Y, Z. each value in the range of [0.0, 1.0]
 */
public static double[] cie1931WavelengthToXYZFit(double wavelength) {
    double wave = wavelength;

    double x;
    {
        double t1 = (wave - 442.0) * ((wave < 442.0) ? 0.0624 : 0.0374);
        double t2 = (wave - 599.8) * ((wave < 599.8) ? 0.0264 : 0.0323);
        double t3 = (wave - 501.1) * ((wave < 501.1) ? 0.0490 : 0.0382);

        x =   0.362 * Math.exp(-0.5 * t1 * t1)
            + 1.056 * Math.exp(-0.5 * t2 * t2)
            - 0.065 * Math.exp(-0.5 * t3 * t3);
    }

    double y;
    {
        double t1 = (wave - 568.8) * ((wave < 568.8) ? 0.0213 : 0.0247);
        double t2 = (wave - 530.9) * ((wave < 530.9) ? 0.0613 : 0.0322);

        y =   0.821 * Math.exp(-0.5 * t1 * t1)
            + 0.286 * Math.exp(-0.5 * t2 * t2);
    }

    double z;
    {
        double t1 = (wave - 437.0) * ((wave < 437.0) ? 0.0845 : 0.0278);
        double t2 = (wave - 459.0) * ((wave < 459.0) ? 0.0385 : 0.0725);

        z =   1.217 * Math.exp(-0.5 * t1 * t1)
            + 0.681 * Math.exp(-0.5 * t2 * t2);
    }

    return new double[] { x, y, z };
}

내 코드는 Java 8로 작성되었지만 Java 및 기타 언어의 하위 버전으로 이식하는 것은 어렵지 않습니다.


1
@Baddack, 당신 말이 맞아요 : 계산 된 값을 좀 더 변형시키는 멋진 방법입니다. 정확히 기억할 수는 없지만 먼저 감마 보정을 적용한 다음 범위를 벗어난 값을 잘라냅니다. 아마도 별도의 방법으로 수행해야 할 수도 있지만 실제로 코드를 작성하는 동안 코드를 공유 할 생각이 없었고이 변환이 필요한 장난감 프로젝트였습니다.
Haochen Xie

1
@Baddack 나는이 변환이 필요한 프로젝트를 파고 자바 8 람다를 사용하지 않고이 부분을 다시 작성하여 코드가 더 명확합니다. transferDoubleUnaryOperator가하는 일 에 대해 실제로 잘못 기억 했으므로 이전 의견의 설명이 올바르지 않으므로 새 코드를 확인하십시오.
Haochen Xie

1
@Baddack 나는 코드가 당신을 도울 수있어서 기쁩니다. 괜찮으 시다면 더 많은 사람들에게 도움이 될 수 있도록 찬성 해주시겠습니까?
Haochen Xie 2016

1
@Baddack Math.pow (c, 1. / 2.4) = c ^ (1 / 2.4), 즉 c를 1 / 2.4의 거듭 제곱으로 올립니다. 1.단지 1 만 종류가 될 것 double대신에int
Haochen시에

3
@Ruslan이 알고리즘은 CIE 표준 관찰자 ( "정확한"모델로 간주 될 수 있음)의 분석적 적합성이므로 오류가 있습니다. 그러나 논문에서 7 페이지의 그림 1을 보면 ((d)와 (f) 비교)이 방법은 매우 근접한 근사치를 제공합니다. 특히 (f)를 보면 표준 모델에서도 푸르스름한 선이 있음을 알 수 있습니다. 또한 순수한 광원의 색상 인식은 개인적으로 다르므로이 수준의 오류는 무시할 수있을 것입니다.
Haochen Xie 2016 년

7

파장 을 RGB 값으로 변환하는 것에 대해 이야기하고 있습니다.

여기를보세요, 아마도 귀하의 질문에 답할 것입니다. 소스 코드와 몇 가지 설명으로이 작업을 수행 할 수있는 유틸리티가 있습니다.

WaveLengthToRGB


1
"파장과 RGB 값 사이에 고유 한 일대일 매핑이 없습니다"라는 동일한 페이지를 읽는 것만으로도 룩업 테이블과 휴리스틱에 얽매이게됩니다. 첫 번째 컷으로 "Hue"가 파란색에서 빨간색으로 범위가 넓기 때문에 HSV에서 RGB 로의 변환을 살펴 보겠습니다. RGB 도메인에서 빨간색 + 파란색 = 보라색과 보라색이 가장 짧은 가시 파장을 가지므로 약간의 이동이 있습니다.
whatnick

3
실질적으로 동일하지 않습니까? freq = c / 파장
Mauricio Scheffer

1
@Mauricio Scheffer 네, 똑같습니다.
Joseph Gordon

이 Bruton의 알고리즘은 현실적이기보다는 심미적입니다
mykhal

8
@ Joseph Gordon-매우 동의하지 않습니다. 대기에서 방출되는 400nm의 녹색 광선이 수면에 닿은 다음 물로 전파되는 것을 고려하십시오. 물의 굴절 계수는 예를 들어 1.33입니다. 따라서 물의 광선 파장은 이제 300nm입니다. 이는 분명히 색상을 변경하지 않습니다. 광선을 "색칠"하는 문제는 파장이 아니라 주파수입니다. 동일한 물질 (진공, 공기, 물)에서 주파수 (색상)는 동일한 파장에 매핑됩니다. 다른 매체에서-아닙니다.
mbaitoff

3

나는 공식적인 답변으로 내 의견을 따르는 것이 좋을 것 같습니다. 가장 좋은 방법은 HSV 색 공간 을 사용하는 것입니다 . 색조는 파장을 나타내지 만 일대일 비교는 아닙니다.


1
당신의 링크는 죽었습니다.
루슬란

3

나는 알려진 색조 값과 주파수의 선형 피팅을 수행했습니다 (빨간색과 보라색은 주파수 값에서 지금까지 확장되어 약간 왜곡되기 때문에 제거). 그리고 대략적인 변환 방정식을 얻었습니다.

그것은 같이 간다
(테라 헤르츠에서) 주파수 = 474 + (3/4) (도 색조 각도 ())

나는 주위를 둘러보고 누군가이 방정식을 생각해 냈는지 확인하려고 노력했지만 2010 년 5 월 현재 아무것도 찾지 못했습니다.


2

방법 1

이것은 약간 정리되고 @ haochen-xie의 C ++ 11 버전을 테스트했습니다. 이 방법으로 사용할 수있는 가시 광선 파장으로 0을 1로 변환하는 기능도 추가했습니다. 아래에 하나의 헤더 파일에 넣고 종속성없이 사용할 수 있습니다. 이 버전은 여기에서 유지 됩니다 .

#ifndef common_utils_OnlineStats_hpp
#define common_utils_OnlineStats_hpp

namespace common_utils {

class ColorUtils {
public:

    static void valToRGB(double val0To1, unsigned char& r, unsigned char& g, unsigned char& b)
    {
        //actual visible spectrum is 375 to 725 but outside of 400-700 things become too dark
        wavelengthToRGB(val0To1 * (700 - 400) + 400, r, g, b);
    }

    /**
    * Convert a wavelength in the visible light spectrum to a RGB color value that is suitable to be displayed on a
    * monitor
    *
    * @param wavelength wavelength in nm
    * @return RGB color encoded in int. each color is represented with 8 bits and has a layout of
    * 00000000RRRRRRRRGGGGGGGGBBBBBBBB where MSB is at the leftmost
    */
    static void wavelengthToRGB(double wavelength, unsigned char& r, unsigned char& g, unsigned char& b) {
        double x, y, z;
        cie1931WavelengthToXYZFit(wavelength, x, y, z);
        double dr, dg, db;
        srgbXYZ2RGB(x, y, z, dr, dg, db);

        r = static_cast<unsigned char>(static_cast<int>(dr * 0xFF) & 0xFF);
        g = static_cast<unsigned char>(static_cast<int>(dg * 0xFF) & 0xFF);
        b = static_cast<unsigned char>(static_cast<int>(db * 0xFF) & 0xFF);
    }

    /**
    * Convert XYZ to RGB in the sRGB color space
    * <p>
    * The conversion matrix and color component transfer function is taken from http://www.color.org/srgb.pdf, which
    * follows the International Electrotechnical Commission standard IEC 61966-2-1 "Multimedia systems and equipment -
    * Colour measurement and management - Part 2-1: Colour management - Default RGB colour space - sRGB"
    *
    * @param xyz XYZ values in a double array in the order of X, Y, Z. each value in the range of [0.0, 1.0]
    * @return RGB values in a double array, in the order of R, G, B. each value in the range of [0.0, 1.0]
    */
    static void srgbXYZ2RGB(double x, double y, double z, double& r, double& g, double& b) {
        double rl = 3.2406255 * x + -1.537208  * y + -0.4986286 * z;
        double gl = -0.9689307 * x + 1.8757561 * y + 0.0415175 * z;
        double bl = 0.0557101 * x + -0.2040211 * y + 1.0569959 * z;

        r = srgbXYZ2RGBPostprocess(rl);
        g = srgbXYZ2RGBPostprocess(gl);
        b = srgbXYZ2RGBPostprocess(bl);
    }

    /**
    * helper function for {@link #srgbXYZ2RGB(double[])}
    */
    static double srgbXYZ2RGBPostprocess(double c) {
        // clip if c is out of range
        c = c > 1 ? 1 : (c < 0 ? 0 : c);

        // apply the color component transfer function
        c = c <= 0.0031308 ? c * 12.92 : 1.055 * std::pow(c, 1. / 2.4) - 0.055;

        return c;
    }

    /**
    * A multi-lobe, piecewise Gaussian fit of CIE 1931 XYZ Color Matching Functions by Wyman el al. from Nvidia. The
    * code here is adopted from the Listing 1 of the paper authored by Wyman et al.
    * <p>
    * Reference: Chris Wyman, Peter-Pike Sloan, and Peter Shirley, Simple Analytic Approximations to the CIE XYZ Color
    * Matching Functions, Journal of Computer Graphics Techniques (JCGT), vol. 2, no. 2, 1-11, 2013.
    *
    * @param wavelength wavelength in nm
    * @return XYZ in a double array in the order of X, Y, Z. each value in the range of [0.0, 1.0]
    */
    static void cie1931WavelengthToXYZFit(double wavelength, double& x, double& y, double& z) {
        double wave = wavelength;

        {
            double t1 = (wave - 442.0) * ((wave < 442.0) ? 0.0624 : 0.0374);
            double t2 = (wave - 599.8) * ((wave < 599.8) ? 0.0264 : 0.0323);
            double t3 = (wave - 501.1) * ((wave < 501.1) ? 0.0490 : 0.0382);

            x = 0.362 * std::exp(-0.5 * t1 * t1)
                + 1.056 * std::exp(-0.5 * t2 * t2)
                - 0.065 * std::exp(-0.5 * t3 * t3);
        }

        {
            double t1 = (wave - 568.8) * ((wave < 568.8) ? 0.0213 : 0.0247);
            double t2 = (wave - 530.9) * ((wave < 530.9) ? 0.0613 : 0.0322);

            y = 0.821 * std::exp(-0.5 * t1 * t1)
                + 0.286 * std::exp(-0.5 * t2 * t2);
        }

        {
            double t1 = (wave - 437.0) * ((wave < 437.0) ? 0.0845 : 0.0278);
            double t2 = (wave - 459.0) * ((wave < 459.0) ? 0.0385 : 0.0725);

            z = 1.217 * std::exp(-0.5 * t1 * t1)
                + 0.681 * std::exp(-0.5 * t2 * t2);
        }
    }

};

} //namespace

#endif

375nm에서 725nm까지의 색상 플롯은 다음과 같습니다.

여기에 이미지 설명 입력

이 방법의 한 가지 문제는 400-700nm 사이에서만 작동하고 그 밖에서는 급격히 검은 색으로 떨어 졌다는 사실입니다. 또 다른 문제는 더 좁은 파란색입니다.

비교를 위해 아래는 maxmax.com에서 Vision FAQ의 색상입니다.

여기에 이미지 설명 입력

나는 이것을 사용하여 각 픽셀이 깊이 값을 미터 단위로 나타내는 깊이 맵을 시각화했습니다.

여기에 이미지 설명 입력

방법 2

이것은 Aeash Partowbitmap_image 단일 파일 헤더 전용 라이브러리의 일부로 구현됩니다 .

inline rgb_t convert_wave_length_nm_to_rgb(const double wave_length_nm)
{
   // Credits: Dan Bruton http://www.physics.sfasu.edu/astro/color.html
   double red   = 0.0;
   double green = 0.0;
   double blue  = 0.0;

   if ((380.0 <= wave_length_nm) && (wave_length_nm <= 439.0))
   {
      red   = -(wave_length_nm - 440.0) / (440.0 - 380.0);
      green = 0.0;
      blue  = 1.0;
   }
   else if ((440.0 <= wave_length_nm) && (wave_length_nm <= 489.0))
   {
      red   = 0.0;
      green = (wave_length_nm - 440.0) / (490.0 - 440.0);
      blue  = 1.0;
   }
   else if ((490.0 <= wave_length_nm) && (wave_length_nm <= 509.0))
   {
      red   = 0.0;
      green = 1.0;
      blue  = -(wave_length_nm - 510.0) / (510.0 - 490.0);
   }
   else if ((510.0 <= wave_length_nm) && (wave_length_nm <= 579.0))
   {
      red   = (wave_length_nm - 510.0) / (580.0 - 510.0);
      green = 1.0;
      blue  = 0.0;
   }
   else if ((580.0 <= wave_length_nm) && (wave_length_nm <= 644.0))
   {
      red   = 1.0;
      green = -(wave_length_nm - 645.0) / (645.0 - 580.0);
      blue  = 0.0;
   }
   else if ((645.0 <= wave_length_nm) && (wave_length_nm <= 780.0))
   {
      red   = 1.0;
      green = 0.0;
      blue  = 0.0;
   }

   double factor = 0.0;

   if ((380.0 <= wave_length_nm) && (wave_length_nm <= 419.0))
      factor = 0.3 + 0.7 * (wave_length_nm - 380.0) / (420.0 - 380.0);
   else if ((420.0 <= wave_length_nm) && (wave_length_nm <= 700.0))
      factor = 1.0;
   else if ((701.0 <= wave_length_nm) && (wave_length_nm <= 780.0))
      factor = 0.3 + 0.7 * (780.0 - wave_length_nm) / (780.0 - 700.0);
   else
      factor = 0.0;

   rgb_t result;

   const double gamma         =   0.8;
   const double intensity_max = 255.0;

   #define round(d) std::floor(d + 0.5)

   result.red   = static_cast<unsigned char>((red   == 0.0) ? red   : round(intensity_max * std::pow(red   * factor, gamma)));
   result.green = static_cast<unsigned char>((green == 0.0) ? green : round(intensity_max * std::pow(green * factor, gamma)));
   result.blue  = static_cast<unsigned char>((blue  == 0.0) ? blue  : round(intensity_max * std::pow(blue  * factor, gamma)));

   #undef round

   return result;
}

375-725nm의 파장 플롯은 다음과 같습니다.

여기에 이미지 설명 입력

따라서 이것은 400-725nm에서 더 유용합니다. 방법 1과 동일한 깊이 맵을 시각화하면 다음과 같습니다. 이 코드에서 더 깊이 보지 않은 사소한 버그를 나타내는 검은 선에 대한 명백한 문제가 있습니다. 또한 제비꽃은이 방법에서 조금 더 좁아 져 멀리있는 물체의 대비가 낮아집니다.

여기에 이미지 설명 입력


0

파장의 CIExy를 D65 흰색을 향해 sRGB 영역에 투영합니다.

#!/usr/bin/ghci
ångstrømsfromTHz terahertz = 2997924.58 / terahertz
tristimulusXYZfromÅngstrøms å=map(sum.map(stimulus))[
 [[1056,5998,379,310],[362,4420,160,267],[-65,5011,204,262]],
 [[821,5688,469,405],[286,5309,163,311]],
 [[1217,4370,118,360],[681,4590,260,138]]]
 where stimulus[ω,μ,ς,σ]=ω/1000*exp(-((å-μ)/if å<μ then ς else σ)^2/2)

standardRGBfromTristimulusXYZ xyz=
 map(gamma.sum.zipWith(*)(gamutConfine xyz))[
 [3.2406,-1.5372,-0.4986],[-0.9689,1.8758,0.0415],[0.0557,-0.2040,1.057]]
gamma u=if u<=0.0031308 then 12.92*u else (u**(5/12)*211-11)/200
[red,green,blue,black]=
 [[0.64,0.33],[0.3,0.6],[0.15,0.06],[0.3127,0.3290,0]]
ciexyYfromXYZ xyz=if xyz!!1==0 then black else map(/sum xyz)xyz
cieXYZfromxyY[x,y,l]=if y==0 then black else[x*l/y,l,(1-x-y)*l/y]
gamutConfine xyz=last$xyz:[cieXYZfromxyY[x0+t*(x1-x0),y0+t*(y1-y0),xyz!!1]|
 x0:y0:_<-[black],x1:y1:_<-[ciexyYfromXYZ xyz],i<-[0..2],
 [x2,y2]:[x3,y3]:_<-[drop i[red,green,blue,red]],
 det<-[(x0-x1)*(y2-y3)-(y0-y1)*(x2-x3)],
 t <-[((x0-x2)*(y2-y3)-(y0-y2)*(x2-x3))/det|det/=0],0<=t,t<=1]

sRGBfromÅ=standardRGBfromTristimulusXYZ.tristimulusXYZfromÅngstrøms
x s rgb=concat["\ESC[48;2;",
               intercalate";"$map(show.(17*).round.(15*).max 0.min 1)rgb,
               "m",s,"\ESC[49m"]
spectrum=concatMap(x" ".sRGBfromÅ)$takeWhile(<7000)$iterate(+60)4000
main=putStrLn spectrum
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.