잡다/시각·영상

파장을 칼라로 변환하기 (Wavelength to RGB color) (1/2)

산을좋아한라쯔 2019. 9. 1. 17:36
반응형

진동수 혹은 파장별로 RGB칼라를 변환할 일이 있었다. 쉽게 찾을 수 있을거라 생각했는데 쉽지 않았다. 간단한 공식이 있거나 파장별 RGB테이블이 있을거라 생각했는데.

해서, 정리해 봤다. 약 2주 정도 걸렸다. 꽤나 복잡한 이론이 밑 바닥에 깔려 있다. '19.8.31. HJ



파장에 따라 색이 변한다는 것은 널리 알려진 사실이다.

무지개 색인 "빨주노초파남보"의 색깔을 파장별로 보면 빨간색이 긴 파장이고 보라색이 가장 짧은 파장이다.


[표] 무지개 색깔 별 파장 크기

한글

영어

파장[nm]

주파수[thz]

RGB[16진수]

 색상

빨강

red

750 - 610

400 - 491

FF 00 00

 

주황

orange

610 - 590

491 - 508

FF 8C 00

 

노랑

yellow

590 - 570

508 - 526

FF FF 00

 

초록

green

570 - 500

526- 600

00 80 00

 

파랑

blue

500 - 450

600 - 666

00 00 FF

 

indigo

450 - 425

666 - 705

4B 00 82

 

보라

violet

425 - 400

705 - 750

80 00 80

 

참조: https://www.colourtherapyhealing.com/colour/colour-properties


  • 파장(T)와 주파수(F)의 관계는 역수 관계이다. F=C/T  (C=빛의 속도=300000000)
    ex) 750nm일 때 주파수 F = 3e8 / 750e-9 = 400e12 hz = 400 thz
    참조) nano = 10e-9,  tera=10e12
  • 위 표를 참조한 사이트에서 주파수 계산값이 좀 다르다. 해서, 새로 계산한 주파수 값으로 대체했음
  • 색깔별 파장 값은 자료마다 조금씩 다르다. 파장별 색깔값을 명확하게 정의하기 힘들기 때문이다. 
  • 무지개 색을 영어로 외울 때는 VIB G YOR


무지개는 햇빛이 서로 다른 매질을 통과할 때 파장에 따라 굴절률이 다른 성질에 의해서 각 파장별로 빛이 분해되고, 이 파장별로 분해된 색깔이 보이는 현상이다.

프리즘은 이러한 현상을 이용해서 인위적으로 무지개를 만들어 낼 수 있는데, 유리로 된 프리즘이 공기와 굴절률이 다르고, 또한 파장에 따라서 굴절률이 다르기에, 한 줄로 들어온 빛이 프리즘을 통과하게 되면 여러 색깔을 가진 여러 줄로 보이게 된다.

  • 유리가 공기보다 밀도가 높기에 굴절률이 높다. 즉, 유리를 통과한 빛은 굴절하게 된다.
  • 짧은 파장의 빛이 긴 파장의 빛보다 굴절률이 높다. 따라서, 프리즘을 통과하게 되면 짧은 파장의 빛인 보라색이 가장 많이 꺽이고, 긴 파장인 빨간색이 덜 꺽인 형태로 보인다.


[그림] 빛이 프리즘에 의해서 굴절되어 분해되는 모습


* 무지개는 7가지 색으로 이루어진걸까?

무지개 색이 "빨주노초파남보"의 7가지 색이라는 것은 뉴튼에 의해 주장된 것이다. 


아리스토텔레스는 4색으로, 로마시대 세네카는 5색으로 봤다하고, 동양에서는 5색(흑백청홍황), 이슬람에서는 4색(빨노초파)으로 봤다하고, 근세 영국/미국에서는 "빨주노초파보"의 6색으로 보다가, 뉴튼이 프리즘에 의해 무지개를 인위적으로 만드는 실험을 통해 7색으로 재정립하고나서부터는 7색으로 통용되었다.


그러나, 무지개를 구성하는 실제 색상은 7가지 보다 훨씬 많고, 각 색간의 구분이 명확하게 띠처럼 되어 있지 않고 서서히 다른 색과 중첩되면서 이루어져 있다.

이는 인간이 인지할 수 있는 파장대가 380nm~780nm이고, 이 파장대의 빛들이 분해되어 보이는 것이기에 그렇다. 즉, 무지개 색은 거의 무한대에 가까운 색으로 이루어져 있는 것이다. 이를 인간의 눈이 세밀하게 분해해서 보지 못할 뿐인 것이다.


파장별로 색깔이 다르다는 것은 알겠는데, 그렇다면 파장 크기가 주어졌을 때 해당하는 색(칼라)이 무엇인지 알 수 있을까?


가능하긴 하지만 간단하지 않다.


간단하지 않은 이유는, 파장에 따라서 "빨강~보라"의 색이 선형관계이지 않기 때문이다. 

선형관계가 아니라는 것은, 빨간색이 750nm이고 보라색이 400nm이면 그 사이에 우리가 알고 있는 색깔들이 존재하는데, 그 색깔들이 정확한 간격을 가지며 존재하는 것이 아니라는 것이다.


이제 방법을 제시하겠다. 여기서는, 2가지 변환 방법을 제시한다. (양이 많아서, 두번 째 방법은 다음 페이지에 작성한다.)



[방법 1]파장 구간별 비례식을 이용해서 "파장 --> 색상" 변환

파장별 RGB색상을 비교적 간단하게 계산할 수 있는 방법이 있다.

380nm-780nm 사이에 기준되는 칼라를 몇 개 배치하고, 그 기준되는 칼라들 사이의 값을 비례식으로 구한는 것이 그 방법이다. 

그래프로 보면 이해될 수 있을 것이다.


 

[그림] 파장에 따른 RGB 변환식의 그래프 표현   


그래프에서 구간을 분리해서 R,G,B값의 변화를 보면,

① 구간

G=0, B가 선형으로 증가, R은 조금 증가하다가 감소

② 구간

R=0, B=255, G가 증가

 구간

R=0, G=255, B가 감소

 구간

G=255, B=0, R이 증가

 구간

R=255, B=0, G가 감소

 구간

G=0, B=0, R이 감소


이와 같은 방법으로 코딩을 해본다. 

함수를 만드는데, 입력으로 주어진 파장값에 해당하는 RGB 값을 리턴할 것이다.

프로그램 언어는 3가지로 짜 보겠다. 1)Excel VBA  2)Java 3)C++


1)Excel VBA

Option Explicit

Type RGBDATA
  r As Integer
  g As Integer
  b As Integer
End Type
Function Wavelength2RGB(w As Double) As RGBDATA
  Dim r As Double, g As Double, b As Double
  Dim a As Double 'attenuation
  Dim gamma As Double 'gamma
  Dim data As RGBDATA
  gamma = 0.8
  If (w >= 380 And w <= 440) Then
    a = 0.3 + 0.7 * (w - 380) / (440 - 380)
    r = ((-(w - 440) / (440 - 380)) * a) ^ gamma
    g = 0#
    b = a ^ gamma
  ElseIf (w > 440 And w <= 490) Then
    r = 0#
    g = ((w - 440) / (490 - 440)) ^ gamma
    b = 1#
  ElseIf (w > 490 And w <= 510) Then
    r = 0#
    g = 1#
    b = (-(w - 510) / (510 - 490)) ^ gamma
  ElseIf (w > 510 And w <= 580) Then
    r = ((w - 510) / (580 - 510)) ^ gamma
    g = 1#
    b = 0#
  ElseIf (w > 580 And w <= 645) Then
    r = 1#
    g = (-(w - 645) / (645 - 580)) ^ gamma
    b = 0#
  ElseIf (w > 645 And w <= 780) Then
    a = 0.3 + 0.7 * (780 - w) / (780 - 645)
    r = (1# * a) ^ gamma
    g = 0#
    b = 0#
  Else
    r = 0#
    g = 0#
    b = 0#
  End If
  data.r = Int(r * 255)
  data.g = Int(g * 255)
  data.b = Int(b * 255)
  Wavelength2RGB = data
End Function
Sub DoConvert()
  Dim i As Long
  Dim data As RGBDATA
  Dim lambda As Double
  Sheets("Sheet1").Activate
  i = 3
  For lambda = 380 To 780
    data = Wavelength2RGB(lambda)
    Cells(i, 1).Value = lambda
    Cells(i, 2).Value = data.r
    Cells(i, 3).Value = data.g
    Cells(i, 4).Value = data.b
    Cells(i, 5).Interior.Color = RGB(data.r, data.g, data.b)
    i = i + 1
  Next lambda
End Sub

위 Excel VBA 프로그램에서 DoConvert 프로시져를 실행시키면, 엑셀의 "Sheet1" 시트에 380~780까지의 파장 값에 대한 RGB 값이 표출될 것이다.

이 프로그램이 들어 있는 엑셀 파일은 "파장별 색깔_단순변환.xlsm" 이다.


파장별 색깔_단순 변환.xlsm


2)Java 코드

public class Main {

  private static RGBData wavelength2RGB(double w) {
    double r,g,b;
    double a; //attenuation
    double gamma; 
    RGBData rgbData;
    gamma = 0.8;
    rgbData = new RGBData();
    if (w >= 380 && w <= 440) {
      a = 0.3 + 0.7 * (w - 380) / (440 - 380);
        r = Math.pow(((-(w - 440) / (440 - 380)) * a), gamma);
        g = 0.0;
        b = Math.pow(a, gamma);
    }else if (w > 440 && w <= 490){
      r = 0.0;
      g = Math.pow(((w - 440) / (490 - 440)), gamma); 
        b = 1.0;
      }else if (w > 490 && w <= 510){
        r = 0.0;
        g = 1.0;
        b = Math.pow((-(w - 510) / (510 - 490)),gamma);
      }else if (w > 510 && w <= 580) {
        r = Math.pow(((w - 510) / (580 - 510)), gamma); 
        g = 1.0;
      b = 0.0;
      }else if (w > 580 && w <= 645) {
        r = 1.0;
        g = Math.pow((-(w - 645) / (645 - 580)) , gamma); 
        b = 0.0;
      }else if (w > 645 && w <= 780) {
        a = 0.3 + 0.7 * (780 - w) / (780 - 645);
        r = Math.pow(a, gamma);
        g = 0.0;
        b = 0.0;
      }else{
        r=0.0;
        g=0.0;
        b=0.0;
      }
      rgbData.r = (int)(r * 255);
      rgbData.g = (int)(g * 255);
      rgbData.b = (int)(b * 255);
    return rgbData;
  }
  public static void main(String[] args) {
    RGBData rgbData;
    String str = "";
    for(int lambda=380; lambda<=780; lambda++) {
      rgbData =  wavelength2RGB(lambda);
      str = lambda + "," + rgbData.r + "," +rgbData.g+","+rgbData.b;
      System.out.println(str);
    }
  }
}
class RGBData{
  int r, g, b;
}

이 Java 코드의 main 함수를 실행하면, 콘솔 화면에 380~780 파장크기의 RGB값을 표출한다.
자바 소스 파일은, 

Main.java


3)C 코드


#include "pch.h"
#include <iostream>
#include <string>
using namespace std;
struct RGBData {
  int r, g, b;
};
RGBData wavelength2RGB(double w) {
  double r, g, b;
  double a; //attenuation
  double gamma;
  struct RGBData rgbData;
  gamma = 0.8; 
  if (w >= 380 && w <= 440) {
    a = 0.3 + 0.7 * (w - 380) / (440 - 380);
    r = pow(((-(w - 440) / (440 - 380)) * a), gamma);
    g = 0.0;
    b = pow(a, gamma);
  }
  else if (w > 440 && w <= 490) {
    r = 0.0;
    g = pow(((w - 440) / (490 - 440)), gamma);
    b = 1.0;
  }
  else if (w > 490 && w <= 510) {
    r = 0.0;
    g = 1.0;
    b = pow((-(w - 510) / (510 - 490)), gamma);
  }
  else if (w > 510 && w <= 580) {
    r = pow(((w - 510) / (580 - 510)), gamma);
    g = 1.0;
    b = 0.0;
  }
  else if (w > 580 && w <= 645) {
    r = 1.0;
    g = pow((-(w - 645) / (645 - 580)), gamma);
    b = 0.0;
  }
  else if (w > 645 && w <= 780) {
    a = 0.3 + 0.7 * (780 - w) / (780 - 645);
    r = pow(a, gamma);
    g = 0.0;
    b = 0.0;
  }
  else {
    r = 0.0;
    g = 0.0;
    b = 0.0;
  }
  rgbData.r = (int)(r * 255);
  rgbData.g = (int)(g * 255);
  rgbData.b = (int)(b * 255);
  return rgbData;
}
int main()
{
  struct RGBData rgbData;  
  for (int lambda = 380; lambda <= 780; lambda++) {
    rgbData = wavelength2RGB(lambda);
    cout << lambda << "," << rgbData.r << "," << rgbData.g << rgbData.b << endl;
  }
}



이 C++ 코드를  실행시키면 380~780까지 파장에 대한 RGB값을 표출한다.
소스코드 파일은,

Wavelength2RGB.cpp



---------------------------------------------------

위 방법은 380nm - 780nm까지 1nm씩 증가시키면서 RGB칼라로 변환한다.

이를 더 세밀하게 0.1nm씩 증가시키면서 변환하려면 아래와 같이 하면 된다. Excel VBA를 예로 들었다.


* 코드의 핵심 포인트

- [380,780]구간을, [3800, 7800]구간으로 변경하고, 각 구간 값들을 x 10을 한 값으로 대체

- 원하는 파장값이 380.1이면, 그 10배인 3801을 파장값으로 해서


Excel VBA 코드


Option Explicit
Type RGBDATA
  r As Integer
  g As Integer
  b As Integer
End Type
Function Wavelength2RGB(w As Double) As RGBDATA
  Dim r As Double, g As Double, b As Double
  Dim a As Double 'attenuation
  Dim gamma As Double 'gamma
  Dim data As RGBDATA
  gamma = 0.8
  If (w >= 3800 And w <= 4400) Then
    a = 0.3 + 0.7 * (w - 3800) / (4400 - 3800)
    r = ((-(w - 4400) / (4400 - 3800)) * a) ^ gamma
    g = 0#
    b = a ^ gamma
  ElseIf (w > 4400 And w <= 4900) Then
    r = 0#
    g = ((w - 4400) / (4900 - 4400)) ^ gamma
    b = 1#
  ElseIf (w > 4900 And w <= 5100) Then
    r = 0#
    g = 1#
    b = (-(w - 5100) / (5100 - 4900)) ^ gamma
  ElseIf (w > 5100 And w <= 5800) Then
    r = ((w - 5100) / (5800 - 5100)) ^ gamma
    g = 1#
    b = 0#
  ElseIf (w > 5800 And w <= 6450) Then
    r = 1#
    g = (-(w - 6450) / (6450 - 5800)) ^ gamma
    b = 0#
  ElseIf (w > 6450 And w <= 7800) Then
    a = 0.3 + 0.7 * (7800 - w) / (7800 - 6450)
    r = (1# * a) ^ gamma
    g = 0#
    b = 0#
  Else
    r = 0#
    g = 0#
    b = 0#
  End If
  data.r = Int(r * 255)
  data.g = Int(g * 255)
  data.b = Int(b * 255)
  Wavelength2RGB = data
End Function
Sub DoConvert()
  Dim i As Long
  Dim data As RGBDATA
  Dim lambda As Double
  Sheets("Sheet1").Activate
  i = 3
  For lambda = 3800 To 7800
    data = Wavelength2RGB(lambda)
    Cells(i, 1).Value = lambda / 10
    Cells(i, 2).Value = data.r
    Cells(i, 3).Value = data.g
    Cells(i, 4).Value = data.b
    Cells(i, 5).Interior.Color = RGB(data.r, data.g, data.b)
    i = i + 1
  Next lambda
End Sub

 




파장별 색깔_단순 변환.xlsm
0.04MB
반응형