진동수 혹은 파장별로 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 변환식의 그래프 표현
① 구간 | 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
이 프로그램이 들어 있는 엑셀 파일은 "파장별 색깔_단순변환.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; }
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; } }
---------------------------------------------------
위 방법은 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
'잡다 > 시각·영상' 카테고리의 다른 글
[스크랩]막대 그래프 그리기 원칙 (0) | 2020.05.17 |
---|---|
[스크랩]가독성 좋은 색깔 선택하기 (0) | 2020.05.17 |
파장을 칼라로 변환하기 (Wavelength to RGB color) (2/2) (0) | 2019.09.14 |