Chuyển đổi tần số ánh sáng sang RGB?


Câu trả lời:


44

Dưới đây là giải thích chi tiết về toàn bộ quá trình chuyển đổi: http://www.fourmilab.ch/documents/specrend/ . Mã nguồn bao gồm!


5
Và bài báo của Fourmilab đưa ra điểm quan trọng rằng một số màu không thể biểu diễn trong RGB (màu cam sáng là một ví dụ điển hình) vì bạn không thể "tạo" màu tùy ý của ánh sáng bằng cách thêm ba màu cơ bản với nhau, bất cứ điều gì mà giáo viên vật lý của chúng tôi có thể đã nói với chúng tôi ( tốt của tôi đã làm). Quá tệ, nhưng trong thực tế thường không gây tử vong.
Francis Davey

1
Thêm vào đó: en.wikipedia.org/wiki/Srgb Bài báo được viết trước khi tiêu chuẩn sRGB được áp dụng rộng rãi. Cũng lưu ý là "tính toán giả định 2 ° chuẩn đo màu quan sát viên" cụm từ, có nghĩa là CIE 1931 bảng tìm thấy trong kèm theo nguồn để giấy nên được sử dụng và không CIE 1964.
GrayFace

Sẽ rất tuyệt nếu cung cấp một số ví dụ về cách sử dụng mã. Nó yêu cầu chức năng như một đối số, sử dụng nhiệt độ để tính toán màu sắc và những thứ như vậy. Một người sẽ rất vui khi biết những gì cần xóa và thay đổi để nó hoạt động.
Tomáš Zato - Phục hồi Monica

2
Cần lưu ý rằng chỉ một tập con nhỏ của tất cả các bước sóng khả kiến ​​có thể được biểu diễn chính xác trong không gian màu RGB. Quá trình chuyển đổi khá phức tạp và mơ hồ. Xem Physics.stackexchange.com/a/94446/5089Physics.stackexchange.com/a/419628/5089
Violet Giraffe

28

Đối với những kẻ lười biếng (như tôi), đây là một triển khai trong java của mã được tìm thấy trong câu trả lời của @ user151323 (nghĩa là, chỉ là một bản dịch đơn giản từ mã pascal được tìm thấy trong 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
Dường như có một lỗi trong mã của bạn. Nếu bước sóng là ví dụ 439,5, hàm của bạn trả về màu đen. Tôi tin rằng mã gốc trên trang web đang hoạt động với số nguyên (tôi không biết pascal). Tôi đề nghị thay đổi Wavelength<=439thành Wavelength<440.
Hassedev

2
Bạn đúng! Cảm ơn bạn đã chỉ ra điều này cho tôi :) Đã được sửa chữa.
Tarc

Có phải nó được mong đợi sẽ có RFB lặp lại cho một số tần số? (ĐỎ): 652 - rgb (255, 0, 0) | 660 - rgb (255, 0, 0) | 692 - rgb (255, 0, 0) | 700 - rgb (255, 0, 0) | ...
Rodrigo Borba

14

Ý tưởng chung:

  1. Sử dụng các chức năng so khớp màu CEI để chuyển đổi bước sóng sang màu XYZ .
  2. Chuyển đổi XYZ sang RGB
  3. Kẹp các thành phần thành [0..1] và nhân với 255 để vừa với dải byte không dấu.

Bước 1 và 2 có thể khác nhau.

Có một số chức năng kết hợp màu sắc, có sẵn dưới dạng bảng hoặc xấp xỉ phân tích (do @Tarc và @Haochen Xie đề xuất). Bảng là tốt nhất nếu bạn cần một kết quả chính xác suôn sẻ.

Không có không gian màu RGB duy nhất. Nhiều ma trận biến đổiCó thể sử dụng và các loại hiệu chỉnh gamma khác nhau.

Dưới đây là mã C # mà tôi đã nghĩ ra gần đây. Nó sử dụng phép nội suy tuyến tính trên bảng "người quan sát tiêu chuẩn CIE 1964" và ma trận sRGB + hiệu chỉnh gamma .

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;
    }
}

Kết quả cho dải 400-700 nm:

nhập mô tả hình ảnh ở đây


Điều này thực sự thú vị đối với tôi. Tôi có ý tưởng sử dụng một cái gì đó như thế này để cung cấp phản hồi bình thường, nhưng sử dụng phản ứng WXYZ để bắt chước phản ứng của tetrachromat có hình nón thứ tư đáp ứng với tần số đủ xa so với bất kỳ loại hình nón thông thường nào trong ba loại hình nón thông thường khác. Điều đó có thể cho phép tôi lấy hình ảnh nguồn và suy ra sự khác biệt mà họ thấy. NB họ không nhìn thấy màu mới, đó là ánh sáng pha trộn, (tổng), chẳng hạn, với một màu vàng cụ thể có vẻ giống với màu vàng của một tần số cụ thể đối với hầu hết chúng ta, nhưng đối với họ, ánh sáng sẽ không hòa trộn sang màu vàng đó.
phorgan1

Tất nhiên, đối với một màu RGB cụ thể, nó có thể được tạo ra theo nhiều cách. Màu xanh của lá có thể đến từ việc lọc bỏ mọi thứ trừ màu xanh lá cây, hoặc màu xanh lá cây có thể đã được lọc ra, nhưng các đặc tính nano có thể khiến màu xanh lam và màu vàng phản chiếu và trông giống hệt màu xanh lá cây. Với một hình ảnh chứ không phải là ánh sáng, có cách nào tôi có thể phân biệt được không?
phorgan1

10

Mặc dù đây là một câu hỏi cũ và đã nhận được một số câu trả lời tốt, nhưng khi tôi cố gắng triển khai chức năng chuyển đổi như vậy trong ứng dụng của mình, tôi không hài lòng với các thuật toán đã được liệt kê ở đây và đã thực hiện nghiên cứu của riêng tôi, điều này đã mang lại cho tôi một số kết quả tốt. Vì vậy, tôi sẽ đăng một câu trả lời mới.

Sau một số nghiên cứu, tôi đã xem qua bài báo này, Các phương pháp gần đúng phân tích đơn giản đối với các chức năng so khớp màu CIE XYZ và cố gắng áp dụng thuật toán điều hợp Gaussian nhiều thùy được giới thiệu trong ứng dụng của tôi. Bài báo chỉ mô tả các chức năng để chuyển đổi một bước sóng thành các giá trị XYZ tương ứng , vì vậy tôi đã triển khai một chức năng để chuyển đổi XYZ thành RGB trong không gian màu sRGB và kết hợp chúng. Kết quả là tuyệt vời và đáng chia sẻ:

/**
 * 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 };
}

mã của tôi được viết bằng Java 8, nhưng không khó để chuyển nó sang các phiên bản thấp hơn của Java và các ngôn ngữ khác.


1
@Baddack, bạn nói đúng: đó chỉ là một cách ưa thích để thực hiện một số chuyển đổi thêm trên các giá trị được tính toán. Tôi không thể nhớ chính xác, nhưng tôi nghĩ trước tiên nó áp dụng hiệu chỉnh gamma, sau đó cắt các giá trị ra khỏi phạm vi. Có lẽ tôi nên thực hiện nó theo một phương pháp riêng, nhưng tôi không thực sự nghĩ đến việc chia sẻ mã trong khi viết nó, và đó là một dự án đồ chơi mà tôi cần chuyển đổi này.
Haochen Xie

1
@Baddack Tôi đã đào ra dự án mà tôi cần chuyển đổi này và đã viết lại phần này mà không sử dụng java 8 lambda để mã rõ ràng hơn. Tôi thực sự nhớ không chính xác về những gì transferDoubleUnaryOperator đang làm (do đó, lời giải thích trong nhận xét trước của tôi không đúng), vì vậy hãy kiểm tra mã mới.
Haochen Xie

1
@Baddack Tôi rất vui vì mã này giúp ích cho bạn. và nếu bạn không phiền, bạn có thể vui lòng ủng hộ nó để nó có thể giúp được nhiều người hơn không?
Haochen Xie

1
@Baddack Math.pow (c, 1. / 2.4) = c ^ (1 / 2.4), tức là nâng c lên lũy thừa của 1 / 2.4; 1.chỉ 1 là nhưng loại sẽ doublethay vìint
Haochen Xie

3
@Ruslan vì thuật toán này phù hợp với phân tích của trình quan sát tiêu chuẩn CIE (có thể được coi là mô hình "chính xác"), nên có sai sót. Nhưng từ bài báo, nếu bạn nhìn vào Hình 1 ở trang 7 (so sánh (d) với (f)), phương pháp này cung cấp một giá trị gần đúng. Đặc biệt nếu bạn nhìn vào (f), bạn có thể thấy rằng cũng có một đường hơi xanh ngay cả trong mô hình tiêu chuẩn. Ngoài ra, cảm nhận về màu sắc của nguồn sáng tinh khiết khác nhau, do đó mức độ sai số này có thể không đáng kể.
Haochen Xie

7

Bạn đang nói về việc chuyển đổi từ độ dài sóng sang giá trị RGB.

Nhìn vào đây, có thể sẽ trả lời câu hỏi của bạn. Thy có một tiện ích để thực hiện việc này với mã nguồn cũng như một số giải thích.

WaveLengthToRGB


1
Chỉ cần đọc cùng một trang "Không có ánh xạ một-một duy nhất giữa các giá trị bước sóng và RGB" - vì vậy bạn đang bị mắc kẹt với bảng tra cứu và phép suy đoán. Như phần đầu tiên, tôi sẽ xem xét các chuyển đổi HSV sang RGB vì "Hue" nằm trong khoảng từ xanh lam sang đỏ. Có thể có một chút thay đổi vì trong miền RGB, màu đỏ + xanh lam = tím và tím có bước sóng nhìn thấy ngắn nhất.
whatnick 24/09/09

3
nó không thực tế giống nhau? freq = c / bước sóng
Mauricio Scheffer

1
@Mauricio Scheffer Vâng, chính xác là như vậy.
Joseph Gordon

thuật toán này của Bruton mang tính thẩm mỹ hơn là thực tế
mykhal 15/09/10

8
@ Joseph Gordon - Hoàn toàn không đồng ý. Coi một tia lục có bước sóng 400nm phát ra trong không khí chạm vào mặt nước rồi truyền trong nước. Hệ số khúc xạ của nước là 1,33, vì vậy bước sóng tia trong nước bây giờ là 300nm, rõ ràng là không làm thay đổi màu sắc của nó. Vật chất làm "màu" các tia là tần số, không phải bước sóng. Trong cùng một chất (chân không, không khí, nước) tần số (màu sắc) ánh xạ với cùng bước sóng. Trong các phương tiện khác nhau - không.
mbaitoff

3

Tôi đoán tôi cũng có thể theo dõi nhận xét của mình với một câu trả lời chính thức. Tùy chọn tốt nhất là sử dụng không gian màu HSV - mặc dù màu đại diện cho bước sóng, nó không phải là so sánh một đối một.


1
Liên kết của bạn đã chết.
Ruslan

3

Tôi đã thực hiện một sự phù hợp tuyến tính của các giá trị và tần số màu đã biết (loại bỏ màu đỏ và tím vì chúng kéo dài quá xa về giá trị tần số khiến chúng làm lệch mọi thứ một chút) và tôi nhận được một phương trình chuyển đổi sơ bộ.

Nó giống như
tần số (tính bằng THz) = 474 + (3/4) (Góc Huế (tính bằng độ))

Tôi đã cố gắng quan sát xung quanh và xem có ai đã nghĩ ra phương trình này hay không, nhưng tôi vẫn chưa tìm thấy gì vào tháng 5 năm 2010.


2

Phương pháp 1

Điều này đã được dọn dẹp và thử nghiệm phiên bản C ++ 11 của @ haochen-xie. Tôi cũng đã thêm một chức năng chuyển đổi giá trị 0 thành 1 thành bước sóng trong quang phổ khả kiến ​​có thể sử dụng được với phương pháp này. Bạn chỉ có thể đặt bên dưới vào một tệp tiêu đề và sử dụng nó mà không cần bất kỳ phụ thuộc nào. Phiên bản này sẽ được duy trì tại đây .

#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

Biểu đồ màu từ 375nm đến 725nm trông như sau:

nhập mô tả hình ảnh ở đây

Một vấn đề với phương pháp này là thực tế là nó chỉ hoạt động trong khoảng 400-700nm và bên ngoài nó giảm hẳn xuống màu đen. Một vấn đề khác là màu xanh lam hẹp hơn.

Để so sánh, dưới đây là các màu từ Câu hỏi thường gặp về Vision tại maxmax.com:

nhập mô tả hình ảnh ở đây

Tôi đã sử dụng nó để trực quan hóa bản đồ độ sâu trong đó mỗi pixel đại diện cho giá trị độ sâu tính bằng mét và điều này trông giống như bên dưới:

nhập mô tả hình ảnh ở đây

Phương pháp 2

Này được thực hiện như một phần của bitmap_image tập tin duy nhất tiêu đề chỉ thư viện bằng cách Aeash Partow:

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;
}

Biểu đồ của bước sóng từ 375-725nm trông như sau:

nhập mô tả hình ảnh ở đây

Vì vậy, điều này có thể sử dụng được nhiều hơn ở bước sóng 400-725nm. Khi tôi hình dung bản đồ độ sâu tương tự như trong phương pháp 1, tôi nhận được bên dưới. Có một vấn đề rõ ràng về những đường đen đó mà tôi nghĩ chỉ ra một lỗi nhỏ trong mã này mà tôi chưa xem xét sâu hơn. Ngoài ra, hoa violet hẹp hơn một chút trong phương pháp này, gây ra ít độ tương phản hơn cho các đối tượng ở xa.

nhập mô tả hình ảnh ở đây


0

Chiếu CIExy của bước sóng về phía trắng D65 lên gam 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.