Işık frekansı RGB'ye dönüştürülsün mü?


Yanıtlar:


44

İşte tüm dönüştürme sürecinin ayrıntılı bir açıklaması: http://www.fourmilab.ch/documents/specrend/ . Kaynak kodu dahildir!


5
Ve Fourmilab makalesi, bazı renklerin RGB'de temsil edilemeyeceğini (parlak portakallar iyi bir örnektir), çünkü fizik öğretmenlerimiz bize ne söylediyse, üç ana rengi birbirine ekleyerek rastgele ışık renklerini "yapamayacağınız" konusunda önemli bir noktaya işaret ediyor ( iyi benim yaptı). Çok kötü, ama pratikte genellikle ölümcül değil.
Francis Davey

1
Buna ek olarak: en.wikipedia.org/wiki/Srgb Makale, sRGB standardı yaygın olarak benimsenmeden önce yazılmıştır. Ayrıca, "Hesaplamalarda 2 ° standart kolorimetrik gözlemci varsayılır" ifadesine dikkat edin; bu, kağıda eşlik eden kaynakta bulunan CIE 1931 tablosunun kullanılması gerektiği ve CIE 1964'ün kullanılmaması gerektiği anlamına gelir.
GrayFace

Kodun nasıl kullanılacağına dair bir örnek vermek güzel olurdu. Bir argüman olarak işlev gerektirir, renkleri ve benzeri şeyleri hesaplamak için sıcaklığı kullanır. Neyin silineceğini ve işe yaraması için değiştirileceğini bilmek mutlu olacaktır.
Tomáš Zato - Monica'yı eski durumuna getir

2
RGB renk uzayında tüm olası görünür dalga boylarının sadece küçük bir alt kümesinin tam olarak temsil edilebileceğini belirtmek gerekir. Dönüşüm süreci oldukça karmaşık ve belirsizdir. Bkz physics.stackexchange.com/a/94446/5089 ve physics.stackexchange.com/a/419628/5089
Menekşe Zürafa

28

Tembel adamlar için (benim gibi), işte @ user151323'ün cevabında bulunan kodun javada bir uygulaması (yani, Spectra Lab Raporunda bulunan pascal kodunun sadece basit bir çevirisi ):

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
Kodunuzda bir hata var gibi görünüyor. Dalgaboyu örneğin 439,5 ise, fonksiyonunuz siyahı döndürür. Sitedeki orijinal kod tamsayılarla çalışıyordu, sanırım (pascal'ı hiç bilmiyorum). Ben değiştirmek için önermek Wavelength<=439için Wavelength<440.
Hassedev

2
Haklısın! Bunu bana gösterdiğin için teşekkür ederim :) Zaten düzeltildi.
Tarc

RFB'nin bazı frekanslarda tekrarlanması bekleniyor mu? (KIRMIZI): 652 - rgb (255, 0, 0) | 660 - rgb (255, 0, 0) | 692 - rgb (255, 0, 0) | 700 - rgb (255, 0, 0) | ...
Rodrigo Borba

14

Genel fikir:

  1. Kullanım CEI renk eşleştirme fonksiyonları için dalga boyu dönüştürmek için XYZ renk .
  2. XYZ'yi RGB'ye dönüştürme
  3. Bileşenleri [0..1] 'e kırpın ve işaretsiz bayt aralığına sığdırmak için 255 ile çarpın.

Adım 1 ve 2 değişebilir.

Tablolar veya analitik yaklaşımlar olarak kullanılabilen birkaç renk eşleştirme işlevi vardır (@Tarc ve @Haochen Xie tarafından önerilen). Düzgün bir sonuç almanız gerekiyorsa tablolar en iyisidir.

Tek bir RGB renk alanı yoktur. Çoklu transformasyon matrisleri ve farklı türde gama düzeltmeleri kullanılabilir.

Son zamanlarda bulduğum C # kodu aşağıdadır. "CIE 1964 standart gözlemci" tablosu ve sRGB matrisi + gama düzeltmesi üzerinden doğrusal enterpolasyon kullanır .

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 aralığı için sonuç:

görüntü açıklamasını buraya girin


Bu benim için gerçekten ilginç. Normal bir yanıt vermek için bunun gibi bir şey kullanma fikrim var, ancak diğer normal üç tür koniden yeterince uzak bir frekansa yanıt veren dördüncü bir koniye sahip tetrakromatların yanıtını taklit etmek için bir WXYZ yanıtı kullanın. Bu, kaynak görüntüler çekmeme ve gördükleri farklılıkları anlamama izin verebilir. Not: Yeni renkler görmüyorlar, örneğin belirli bir sarıya karışan ışıklar (toplam), çoğumuz için belirli bir frekanstaki sarı ile aynı görünüyor, ancak onlar için ışık karışmıyor hiç o sarıya.
phorgan1

Elbette, belirli bir RGB rengi için pek çok yoldan ulaşılabilirdi. Bir yaprağın yeşili, yeşil dışındaki her şeyin filtrelenmesinden gelebilir veya yeşil filtrelenmiş olabilir, ancak nano özellikler mavi ve sarının yansımasına ve yeşile özdeş görünmesine neden olabilir. Işık yerine bir görüntü verildiğinde, ayırt edebileceğim herhangi bir yol var mı?
phorgan1

10

Bu eski bir soru olmasına ve zaten bir avuç iyi yanıt almasına rağmen, uygulamamda bu tür dönüştürme işlevlerini uygulamaya çalıştığımda burada listelenen algoritmalardan memnun kalmadım ve kendi araştırmamı yaptım, bu da bana iyi bir sonuç verdi. Bu yüzden yeni bir cevap göndereceğim.

Bazı araştırmalardan sonra , CIE XYZ Renk Eşleştirme İşlevlerine Basit Analitik Yaklaşımlar adlı bu makaleye rastladım ve tanıtılan çok loblu parçalı Gauss uydurma algoritmasını uygulamamda kullanmaya çalıştım. Makale yalnızca bir dalga boyunu karşılık gelen XYZ değerlerine dönüştürme işlevlerini açıkladı, bu nedenle sRGB renk uzayında XYZ'yi RGB'ye dönüştürmek için bir işlev uyguladım ve bunları birleştirdim. Sonuç harika ve paylaşmaya değer:

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

kodum Java 8 ile yazılmış, ancak onu Java'nın ve diğer dillerin daha düşük sürümlerine taşımak zor olmamalı.


1
@Baddack, haklısın: bu, hesaplanan değerler üzerinde daha fazla dönüşüm yapmanın süslü bir yolu. Tam olarak hatırlayamıyorum ama sanırım önce bir gama düzeltmesi uyguluyor, sonra aralık dışı değerleri kesiyor. Belki de ayrı bir yöntemle yaptırmalıydım ama aslında kodu yazarken paylaşmayı düşünmüyordum ve bu dönüşüme ihtiyacım olan oyuncak bir projeydi.
Haochen Xie

1
@Baddack Bu dönüşüme ihtiyacım olan projeyi çıkardım ve kodun daha net olması için bu bölümü java 8 lambda kullanmadan yeniden yazdım. transferDoubleUnaryOperator'ın ne yaptığını aslında yanlış hatırladım (bu nedenle önceki yorumumdaki açıklama doğru değil), bu yüzden lütfen yeni kodu kontrol edin.
Haochen Xie

1
@Baddack kodun size yardımcı olmasına sevindim. ve eğer sakıncası yoksa, potansiyel olarak daha fazla insana yardım edebilmesi için lütfen ek oy verebilir misiniz?
Haochen Xie

1
@Baddack Math.pow (c, 1. / 2.4) = c ^ (1 / 2.4), yani c'yi 1 / 2.4 kuvvetine yükselt; 1.sadece 1 ama doublebunun yerine tür olacakint
Haochen Xie

3
@Ruslan, bu algoritma CIE standart gözlemcisinin analitik bir uyumu olduğu için ("kesin" model olarak düşünülebilir), hatalar var. Ancak kağıttan, 7. sayfadaki Şekil 1'e bakarsanız ((d) ile (f) 'yi karşılaştırın), bu yöntem oldukça yakın bir yaklaşım sağlar. Özellikle (f) 'e bakarsanız standart modelde bile mavimsi bir çizgi olduğunu görebilirsiniz. Ayrıca, saf ışık kaynağının renk algısı kişisel olarak değişir, bu nedenle bu hata seviyesi muhtemelen ihmal edilebilir düzeydedir.
Haochen Xie

7

Dalga uzunluğunu RGB değerine dönüştürmekten bahsediyorsunuz .

Buraya bak, muhtemelen soruna cevap verecek. Bunu kaynak kodla ve bazı açıklamalarla yapmak için bir yardımcı programınız var.

WaveLengthToRGB


1
Sadece aynı sayfayı okuyorum "Dalgaboyu ve RGB değerleri arasında benzersiz bire bir eşleştirme yoktur" - o kadar iyi ki, bir arama tablosu ve buluşsal yöntemlerle sıkışıp kalıyorsunuz. "Ton" maviden kırmızıya değiştiğinden, ilk kesim olarak HSV'den RGB'ye dönüşümlere bakardım. Muhtemelen hafif bir kayma ile, çünkü RGB alanında kırmızı + mavi = mor ve mor görünür en kısa dalga boyuna sahiptir.
whatnick

3
pratik olarak aynı değil mi? frek = c / dalga boyu
Mauricio Scheffer

1
@Mauricio Scheffer Evet, TAMAMEN aynı.
Joseph Gordon

Bu Bruton algoritması gerçekçi olmaktan çok estetik
mykhal

8
@ Joseph Gordon - Kesinlikle katılmıyorum. Havada yayılan yeşilimsi bir ışının 400nm su yüzeyine çarptığını ve daha sonra suda yayıldığını düşünün. Suyun kırılma katsayısı, diyelim ki 1.33'tür, yani sudaki ışın dalga boyu şimdi 300 nm'dir ve bu açıkça rengini değiştirmez. Işınları "renklendiren" madde, dalga boyu değil frekanstır. Aynı maddede (vakum, hava, su) frekanslar (renkler) aynı dalga boylarına eşlenir. Farklı medyada - hayır.
mbaitoff

3

Sanırım yorumumu resmi bir cevapla takip edebilirim. En iyi seçenek HSV renk uzayını kullanmaktır - ton dalgaboyunu temsil etse de bire bir karşılaştırma değildir.


1
Bağlantınız kesildi.
Ruslan

3

Bilinen renk tonu değerleri ve frekansları için doğrusal bir uyum yaptım (kırmızı ve menekşeyi çıkararak, frekans değerlerinde şeyleri biraz çarpıtacak kadar uzadıkları için) ve kabaca bir dönüşüm denklemi elde ettim.


Frekans gibi gider (THz cinsinden) = 474 + (3/4) (Ton Açısı (derece cinsinden))

Etrafa bakmaya ve bu denklemi bulan var mı diye bakmaya çalıştım, ancak Mayıs 2010 itibariyle hiçbir şey bulamadım.


2

Yöntem 1

Bu, biraz temizlendi ve @ haochen-xie'nin C ++ 11 sürümü test edildi. Ayrıca 0 değerini 1'e, görünür spektrumda bu yöntemle kullanılabilen bir dalga boyuna dönüştüren bir fonksiyon ekledim. Aşağıyı bir başlık dosyasına koyabilir ve herhangi bir bağımlılık olmadan kullanabilirsiniz. Bu versiyon burada muhafaza edilecektir .

#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'den 725nm'ye kadar olan renklerin grafiği aşağıdaki gibidir:

görüntü açıklamasını buraya girin

Bu yöntemle ilgili bir sorun, yalnızca 400-700nm arasında çalışması ve bunun dışında keskin bir şekilde siyaha düşmesidir. Diğer bir konu ise daha dar mavi.

Karşılaştırma için, maxmax.com'daki Vision SSS'deki renkler aşağıdadır:

görüntü açıklamasını buraya girin

Bunu, her pikselin metre cinsinden derinlik değerini temsil ettiği ve aşağıdaki gibi görünen derinlik haritasını görselleştirmek için kullandım:

görüntü açıklamasını buraya girin

Yöntem 2

Bu bir parçası olarak uygulanmaktadır bitmap_image Aeash Partow tarafından tek dosya başlığı okunur kütüphanede:

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 dalga boyunun grafiği aşağıdaki gibidir:

görüntü açıklamasını buraya girin

Yani bu, 400-725nm'de daha kullanışlı. Yöntem 1'deki ile aynı derinlik haritasını görselleştirdiğimde, aşağıya iniyorum. Bu kodda daha derinlemesine bakmadığım küçük bir hatayı işaret ettiğini düşündüğüm bu siyah çizgilerin bariz bir sorunu var. Ayrıca menekşeler bu yöntemde biraz daha dardır ve uzaktaki nesneler için daha az kontrasta neden olur.

görüntü açıklamasını buraya girin


0

Dalga boyunun CIExy'sini D65 beyazına doğru sRGB gamut üzerine yansıtın

#!/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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.