Преобразовать частоту света в RGB?

Кто-нибудь знает какую-либо формулу для преобразования частоты света в значение RGB?


person Shaul Behr    schedule 24.09.2009    source источник
comment
Очень технические вопросы по физике и программированию +1.   -  person whatnick    schedule 24.09.2009
comment
проверьте это приближение реальных спектральных цветов   -  person Spektre    schedule 27.10.2015


Ответы (9)


Вот подробное объяснение всего процесса преобразования: http://www.fourmilab.ch/documents/specrend/. Исходный код включен!

person Stephen Mesa    schedule 24.09.2009
comment
И в статье Fourmilab подчеркивается, что некоторые цвета не могут быть представлены в RGB (яркий оранжевый - хороший пример), потому что вы не можете создать произвольные цвета света, сложив вместе три основных цвета, что бы наши учителя физики ни говорили нам (ну, мой сделал). Жаль, но на практике не всегда смертельно. - person Francis Davey; 30.05.2010
comment
В дополнение к этому: en.wikipedia.org/wiki/Srgb Статья написана до sRGB. стандарт получил широкое распространение. Также обратите внимание, что в расчетах используется стандартная фраза колориметрического наблюдателя 2 °, что означает, что следует использовать таблицу CIE 1931, содержащуюся в сопроводительном источнике к статье, а не CIE 1964. - person GrayFace; 10.02.2014
comment
Было бы неплохо привести пример использования кода. Он требует функции в качестве аргумента, использует температуру для вычисления цветов и тому подобное. Было бы приятно узнать, что удалить и изменить, чтобы это заработало. - person Tomáš Zato - Reinstate Monica; 13.03.2014
comment
Стоит отметить, что только небольшое подмножество всех возможных видимых длин волн может быть точно представлено в цветовом пространстве RGB. Процесс конвертации довольно сложен и неоднозначен. См. Physics.stackexchange.com/a/94446/5089 и physics.stackexchange.com/a/419628/5089 - person Violet Giraffe; 30.12.2018

Для ленивых парней (таких как я) вот реализация на java кода, найденного в ответе @ user151323 (то есть просто простой перевод кода паскаля, найденного в Отчет Spectra Lab):

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;
}
person Tarc    schedule 17.02.2013
comment
Похоже, в вашем коде есть ошибка. Если длина волны, например, 439,5, ваша функция вернет черный цвет. Исходный код на сайте, я полагаю, работал с целыми числами (я вообще не знаю паскаль). Предлагаю поменять Wavelength<=439 на Wavelength<440. - person Hassedev; 25.02.2013
comment
Ты прав! Спасибо, что указали мне на это :) Уже исправили. - person Tarc; 03.03.2013
comment
Ожидается ли повторный RFB на некоторых частотах? (КРАСНЫЙ): 652 - rgb (255, 0, 0) | 660 - RGB (255, 0, 0) | 692 - RGB (255, 0, 0) | 700 - RGB (255, 0, 0) | ... - person Rodrigo Borba; 04.03.2020

Главная идея:

  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 нм:

введите описание изображения здесь

person amartynov    schedule 12.09.2016
comment
Мне это действительно интересно. У меня есть идея использовать что-то подобное, чтобы дать нормальный ответ, но использовать ответ WXYZ, чтобы имитировать реакцию тетрахроматов, у которых есть четвертый конус, который реагирует на частоту, достаточно далекую от любого из других трех типов колбочек. Это может позволить мне взять исходные изображения и сделать вывод о различиях, которые они видят. N.B. они не видят новых цветов, это то, что свет, который смешивается (сумма), например, с определенным желтым, кажется большинству из нас идентичным желтому с определенной частотой, но для них свет не будет смешиваться с что жёлтый вообще. - person phorgan1; 18.11.2016
comment
Конечно, для определенного цвета RGB это можно было получить разными способами. Зеленый цвет листа может быть результатом фильтрации всего, кроме зеленого, или зеленый может быть отфильтрован, но нано-характеристики могут привести к тому, что синий и желтый будут отражаться и выглядеть идентично зеленому. Могу ли я как-то отличить изображение, а не свет? - person phorgan1; 18.11.2016

Хотя это старый вопрос и уже получил несколько хороших ответов, когда я попытался реализовать такую ​​функцию преобразования в своем приложении, меня не удовлетворили алгоритмы, уже перечисленные здесь, и я провел собственное исследование, которое дало мне хороший результат. Итак, я отправлю новый ответ.

После некоторых исследований я наткнулся на этот документ, Простые аналитические приближения к функциям согласования цветов CIE XYZ, и попытался принять представленный алгоритм кусочно-гауссовой подгонки с несколькими лепестками в моем приложении. В документе описаны только функции преобразования длины волны в соответствующие значения XYZ, поэтому я реализовал функцию для преобразования XYZ в RGB. в цветовом пространстве sRGB и объединили их. Результат фантастический, и стоит поделиться:

/**
 * 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 и другие языки.

person Haochen Xie    schedule 03.01.2016
comment
Это выглядит необычно, но почему вы закодировали его в один int? Разве клиенту не пришлось бы снова вытащить значения обратно? - person Andrew; 24.01.2016
comment
Кодирование значения цвета (A) RGB одним целым является обычным соглашением, и инструментарий пользовательского интерфейса, над которым я работал, использует это соглашение. Вы можете точно изменить интерфейс в соответствии с вашим приложением. - person Haochen Xie; 25.01.2016
comment
DoubleUnaryOperator transfer = c -> c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1. / 2.4) - 0.055; transfer = transfer.compose(c -> c > 1 ? 1 : (c < 0 ? 0 : c)); Эта строка вызывает у меня ошибки. C # не знает, что такое двойной унарный оператор. Я тоже не знаю, я погуглил и, похоже, это Java. Есть ли способ сделать эту строку на C #? - person Baddack; 24.06.2016
comment
Думаю, я понял это после того, как подумал об этом больше. Я думаю, что все, что делает эта функция, - это решение, находится ли RGB в пределах диапазона, если он возвращает цвет, иначе он возвращает 0 для этого цвета. Это отличный способ сделать это. - person Baddack; 24.06.2016
comment
@Baddack, вы правы: это просто интересный способ сделать некоторые дальнейшие преобразования в вычисленных значениях. Я не могу точно вспомнить, но я думаю, что сначала применяется гамма-коррекция, а затем вырезаются значения, выходящие за пределы диапазона. Возможно, мне следовало бы сделать это отдельным методом, но на самом деле я не думал о том, чтобы делиться кодом во время его написания, и это был игрушечный проект, в котором мне было нужно это преобразование. - person Haochen Xie; 24.06.2016
comment
@HaochenXie, спасибо, что поделились, хороший код. Я смог заставить кое-что работать. Ваше здоровье - person Baddack; 24.06.2016
comment
@Baddack Я откопал проект, в котором мне было нужно это преобразование, и переписал эту часть без использования лямбда-выражения java 8, чтобы код был более понятным. На самом деле я неправильно вспомнил о том, что делал transfer DoubleUnaryOperator (поэтому объяснение в моем предыдущем комментарии неверно), поэтому проверьте новый код. - person Haochen Xie; 25.06.2016
comment
Спасибо за обновление, этот код очень полезен! Я ценю вашу помощь. - person Baddack; 26.06.2016
comment
@Baddack, я рад, что код тебе помогает. и если вы не возражаете, не могли бы вы проголосовать за него, чтобы он потенциально мог помочь большему количеству людей? - person Haochen Xie; 27.06.2016
comment
Что делает Math.pow(c, 1. / 2.4)? Я не могу сделать это на C #. Это делает c в силу? Что означает обозначение 1. / x в Math.pow? - person Baddack; 30.06.2016
comment
@Baddack Math.pow (c, 1. / 2.4) = c ^ (1 / 2.4), т.е. возвести c в степень 1 / 2.4; 1. равно 1, но тип будет double вместо int - person Haochen Xie; 30.06.2016
comment
@Baddack кажется, что в C # Math.pow просто Math.Pow. См. msdn.microsoft.com/en-us/library/system .math.pow.aspx - person Haochen Xie; 30.06.2016
comment
Спасибо, я просто не знал, что это за 1., но теперь я на той же странице. Ваше здоровье - person Baddack; 30.06.2016
comment
Хотя ответ Tarc выглядит более или менее убедительно, ваш имеет некоторую аномалию в фиолетовая зона. Обратите внимание на странную голубоватую линию. - person Ruslan; 08.09.2016
comment
@Ruslan, поскольку этот алгоритм является аналитической подгонкой стандартного наблюдателя CIE (который можно рассматривать как точную модель), есть ошибки. Но из статьи, если вы посмотрите на рисунок 1 на странице 7 (сравните (d) с (f)), этот метод дает довольно близкое приближение. Особенно если вы посмотрите на (f), вы увидите, что даже в стандартной модели есть голубоватая линия. Кроме того, восприятие цвета от чистого источника света индивидуально, поэтому этот уровень ошибки, вероятно, незначителен. - person Haochen Xie; 08.09.2016
comment
Спасибо, теперь понятно. Еще одна вещь: я понимаю, почему вы зажимаете результирующие значения от отрицательного до нуля, но почему вы зажимаете от большого (например, ›1) до 1 вместо изменения масштаба всего видимого диапазона на константу около 0,4, чтобы избежать насыщенности (что меняет оттенок, например, в диапазоне от зеленого до красного)? - person Ruslan; 08.09.2016
comment
@Ruslan Я думаю, вы говорите о функции # srgbXYZ2RGBPostprocess (..). Пожалуйста, взгляните на ссылку, которую я дал в комментариях к функции # srgbXYZ2RGB (..), я просто следовал алгоритму, указанному в ссылке. - person Haochen Xie; 09.09.2016
comment
@Ruslan вы наверняка могли бы разработать свою собственную функцию преобразования XYZ в RGB, поскольку лучшая матрица преобразования (а также метод обрезки) варьируется от приложения к приложению - person Haochen Xie; 09.09.2016
comment
Ах, может быть, обрезка до единицы действительно нужна, когда она обрезается до нуля. Думаю, это какая-то компенсация. Спасибо за ответы. - person Ruslan; 09.09.2016

Вы говорите о преобразовании из длины волны в значение RGB.

Посмотрите сюда, наверняка ответите на ваш вопрос. У вас есть утилита для этого с исходным кодом, а также некоторые пояснения.

WaveLengthToRGB

person Community    schedule 24.09.2009
comment
Просто чтение одной и той же страницы Не существует однозначного однозначного соответствия между длиной волны и значениями RGB - так что вы застряли с таблицей поиска и эвристикой. В качестве первого варианта я бы посмотрел на преобразования HSV в RGB, поскольку оттенок варьируется от синего до красного. Возможно, с небольшим сдвигом, поскольку в области RGB красный + синий = фиолетовый, а фиолетовый имеет самую короткую видимую длину волны. - person whatnick; 24.09.2009
comment
разве это не то же самое? freq = c / длина волны - person Mauricio Scheffer; 24.09.2009
comment
@Mauricio Scheffer Да, это ТОЧНО то же самое. - person Joseph Gordon; 01.07.2010
comment
этот алгоритм Брутона скорее эстетичен, чем реалистичен - person mykhal; 15.09.2010
comment
@ Джозеф Гордон - Совершенно не согласен. Представьте себе зеленоватый луч 400 нм, испускаемый в воздухе, ударяется о поверхность воды и затем распространяется в воде. Коэффициент преломления воды составляет, скажем, 1,33, поэтому длина волны луча в воде теперь составляет 300 нм, что, очевидно, не меняет ее цвет. Материя, которая окрашивает лучи, - это частота, а не длина волны. В одном и том же веществе (вакуум, воздух, вода) частоты (цвета) соответствуют одной и той же длине волны. В разных СМИ - нет. - person mbaitoff; 16.01.2011
comment
Это тот же код из stackoverflow.com/a/14917481/1048170 - person Dominic Cerisano; 15.05.2018

Думаю, я мог бы дополнить свой комментарий официальным ответом. Лучше всего использовать цветовое пространство HSV, хотя оттенок представляет длину волны, это не однозначное сравнение.

person whatnick    schedule 24.09.2009
comment
Ваша ссылка мертва. - person Ruslan; 08.09.2016

Я провел линейную аппроксимацию известных значений оттенков и частот (исключив красный и фиолетовый, потому что они настолько сильно растягиваются в значениях частоты, что немного искажают вещи), и я получил грубое уравнение преобразования.

Это выглядит как
частота (в ТГц) = 474 + (3/4) (Угол оттенка (в градусах))

Я попытался осмотреться и посмотреть, придумал ли кто-нибудь это уравнение, но по состоянию на май 2010 года ничего не нашел.

person David Elm    schedule 13.05.2010

Метод 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

График для цветов от 375 до 725 нм выглядит следующим образом:

введите описание изображения здесь

Проблема с этим методом заключается в том, что он работает только в диапазоне 400-700 нм, а за пределами этого диапазона резко падает до черного. Другой вопрос - более узкий синий.

Для сравнения ниже приведены цвета из FAQ по Vision на maxmax.com:

введите описание изображения здесь

Я использовал это для визуализации карты глубины, где каждый пиксель представляет значение глубины в метрах, и это выглядит так:

введите описание изображения здесь

Способ 2

Это реализовано как часть bitmap_image только для заголовка одного файла. библиотека 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;
}

График длины волны 375-725 нм выглядит следующим образом:

введите описание изображения здесь

Так что это более удобно при 400-725 нм. Когда я визуализирую ту же карту глубины, что и в методе 1, я попадаю ниже. Есть очевидная проблема с этими черными линиями, которые, как мне кажется, указывают на незначительную ошибку в этом коде, которую я не рассматривал более глубоко. Также в этом методе фиалки немного уже, что снижает контраст для удаленных объектов.

введите описание изображения здесь

person Shital Shah    schedule 23.08.2018

Проецируйте 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
person Roman Czyborra    schedule 08.01.2020