◆波長(スペクトル)RGB変換プログラムコード
光の波長(スペクトル)からRGB値を導く簡易プログラムです。
この方法は◆2色型色覚の画像表現法;色覚シミュレータ
でスペクトル図をビットマップデータとして得るために使っています。
参考までに波長を選択しRGBを得るUIを付けたものを下に載せます。スライダーまたは
波長数値入力で波長を指定するとRGB値、XYZ値と色が表示されます。
なお、RGBテーブル生成時のガンマ補正値を変更可能としています。R,G,Bそれぞれ
別々に設定することができます。代表値を変更するとR,G,Bに同じ値が設定されます、
定数テーブルと関数
10nm毎のテーブルを作成し、中間は線形補完しています。
下に載せた関数はAS3用ですが、他の言語への転用は難しくないはずです。
引数はnm単位の波長で380≦λ≦780,戻り値はR,G,Bそれぞれ0.0≦v≦1.0の浮動少数値です。
var rgbTable:Array = [
{ L:380 ,R:0.06076 ,G:0.00000 ,B:0.11058 }
,{ L:390 ,R:0.08700 ,G:0.00000 ,B:0.16790 }
,{ L:400 ,R:0.13772 ,G:0.00000 ,B:0.26354 }
,{ L:410 ,R:0.20707 ,G:0.00000 ,B:0.39852 }
,{ L:420 ,R:0.31129 ,G:0.00000 ,B:0.60684 }
,{ L:430 ,R:0.39930 ,G:0.00000 ,B:0.80505 }
,{ L:440 ,R:0.40542 ,G:0.00000 ,B:0.87684 }
,{ L:450 ,R:0.34444 ,G:0.00000 ,B:0.88080 }
,{ L:460 ,R:0.11139 ,G:0.00000 ,B:0.86037 }
,{ L:470 ,R:0.00000 ,G:0.15233 ,B:0.77928 }
,{ L:480 ,R:0.00000 ,G:0.38550 ,B:0.65217 }
,{ L:490 ,R:0.00000 ,G:0.49412 ,B:0.51919 }
,{ L:500 ,R:0.00000 ,G:0.59271 ,B:0.40008 }
,{ L:510 ,R:0.00000 ,G:0.69549 ,B:0.25749 }
,{ L:520 ,R:0.00000 ,G:0.77773 ,B:0.00000 }
,{ L:530 ,R:0.00000 ,G:0.81692 ,B:0.00000 }
,{ L:540 ,R:0.00000 ,G:0.82625 ,B:0.00000 }
,{ L:550 ,R:0.00000 ,G:0.81204 ,B:0.00000 }
,{ L:560 ,R:0.47369 ,G:0.77626 ,B:0.00000 }
,{ L:570 ,R:0.70174 ,G:0.71523 ,B:0.00000 }
,{ L:580 ,R:0.84922 ,G:0.62468 ,B:0.00000 }
,{ L:590 ,R:0.94726 ,G:0.49713 ,B:0.00000 }
,{ L:600 ,R:0.99803 ,G:0.31072 ,B:0.00000 }
,{ L:610 ,R:1.00000 ,G:0.00000 ,B:0.00000 }
,{ L:620 ,R:0.95520 ,G:0.00000 ,B:0.00000 }
,{ L:630 ,R:0.86620 ,G:0.00000 ,B:0.00000 }
,{ L:640 ,R:0.76170 ,G:0.00000 ,B:0.00000 }
,{ L:650 ,R:0.64495 ,G:0.00000 ,B:0.00000 }
,{ L:660 ,R:0.52857 ,G:0.00000 ,B:0.00000 }
,{ L:670 ,R:0.41817 ,G:0.00000 ,B:0.00000 }
,{ L:680 ,R:0.33202 ,G:0.00000 ,B:0.00000 }
,{ L:690 ,R:0.25409 ,G:0.00000 ,B:0.00000 }
,{ L:700 ,R:0.19695 ,G:0.00000 ,B:0.00000 }
,{ L:710 ,R:0.15326 ,G:0.00000 ,B:0.00000 }
,{ L:720 ,R:0.11902 ,G:0.00000 ,B:0.00000 }
,{ L:730 ,R:0.09063 ,G:0.00000 ,B:0.00000 }
,{ L:740 ,R:0.06898 ,G:0.00000 ,B:0.00000 }
,{ L:750 ,R:0.05150 ,G:0.00000 ,B:0.00000 }
,{ L:760 ,R:0.04264 ,G:0.00000 ,B:0.00000 }
,{ L:770 ,R:0.03666 ,G:0.00000 ,B:0.00794 }
,{ L:780 ,R:0.00000 ,G:0.00000 ,B:0.00000 }
];
var wlenStep:Number = 10;
var wlenMin:Number = 380;
var wlenMax:Number = 780
function wlen2rgb(wlen:Number):Object{
wlen = Math.max(wlenMin,wlen);
wlen = Math.min(wlenMax,wlen);
var widx:int = (wlen-wlenMin)/wlenStep;
var wlenSub:Number = (wlen-wlenMin)-(widx*wlenStep);
var ret = new Object();
ret.R = rgbTable[widx].R;
ret.G = rgbTable[widx].G;
ret.B = rgbTable[widx].B;
if( wlenSub==0 ) return ret;
var rate:Number = wlenSub/wlenStep;
ret.R += (rgbTable[widx+1].R-rgbTable[widx].R)*rate;
ret.G += (rgbTable[widx+1].G-rgbTable[widx].G)*rate;
ret.B += (rgbTable[widx+1].B-rgbTable[widx].B)*rate;
return ret;
}
テーブルのグラフ表示
波長ごとのRGB感度を示します。
このグラフは、RGBテーブルをExcelに読ませてグラフ化したものです。
プログラムはガンマ補正値が1/2.7で、このテーブルは1/2.2であり、
差はありますが、見た目としてグラフに大きな差はありません。
L,R,G,B 380,0.03215,0.00000,0.06704 390,0.04994,0.00000,0.11193 400,0.08777,0.00000,0.19463 410,0.14477,0.00000,0.32333 420,0.23877,0.00000,0.54172 430,0.32410,0.00000,0.76634 440,0.33021,0.00000,0.85104 450,0.27034,0.00000,0.85575 460,0.06765,0.00000,0.83146 470,0.00000,0.09932,0.73634 480,0.00000,0.31041,0.59180 490,0.00000,0.42097,0.44733 500,0.00000,0.52628,0.32488 510,0.00000,0.64040,0.18917 520,0.00000,0.73454,0.00000 530,0.00000,0.78022,0.00000 540,0.00000,0.79118,0.00000 550,0.00000,0.77451,0.00000 560,0.39971,0.73284,0.00000 570,0.64746,0.66278,0.00000 580,0.81826,0.56132,0.00000 590,0.93567,0.42412,0.00000 600,0.99758,0.23823,0.00000 610,1.00000,0.00000,0.00000 620,0.94530,0.00000,0.00000 630,0.83838,0.00000,0.00000 640,0.71600,0.00000,0.00000 650,0.58376,0.00000,0.00000 660,0.45727,0.00000,0.00000 670,0.34301,0.00000,0.00000 680,0.25843,0.00000,0.00000 690,0.18611,0.00000,0.00000 700,0.13614,0.00000,0.00000 710,0.10007,0.00000,0.00000 720,0.07337,0.00000,0.00000 730,0.05252,0.00000,0.00000 740,0.03756,0.00000,0.00000 750,0.02624,0.00000,0.00000 760,0.02081,0.00000,0.00000 770,0.01729,0.00000,0.00264 780,0.00000,0.00000,0.00000
RGBで画像化する例
AS3で画像に出す例を載せます。冒頭のスペクトル図はこのプログラムで出したものです。
..上記のプログラムに加えて..
var bmd:BitmapData= new BitmapData(390,100,false,0x00000000);
var bm:Bitmap = new Bitmap(bmd);
addChild(bm);
var posx:int = 0;
for(var wlen:int=380;wlen<730;++wlen){
var rgb:Object = wlen2rgb(wlen);
var r:uint = rgb.R*255;
var g:uint = rgb.G*255;
var b:uint = rgb.B*255;
var color:uint = (r<<16)+(g<<8)+b;
bmd.fillRect(new Rectangle(++posx+20,35,1,30),color);
}
テーブルを作成するプログラム
波長/RGB変換テーブルは、 波長から三刺激値(XYZ値)を得、それをRGBに
変換したものです。
三刺激値のテーブルは理科年表から得ました。
今回ガンマ補正を(1/2.7)乗
の形で行ってあります。この値は少し調整が必要かもしれません。
RGBテーブル作成とその利用を別プログラムとしてありますが、
もちろん、初期化と利用の形の単一プログラムとしてもかまいません。
// 理科年表
// 測光と測色
// 単色光の色度座標と等エネルギー単色放射の三刺激値
// から3刺激値 Lは波長nm
var triStimulusVals:Array = [
{L:380, X:0.0014, Y:0.0000, Z:0.0065}
,{L:390, X:0.0042, Y:0.0001, Z:0.0201}
,{L:400, X:0.0143, Y:0.0004, Z:0.0679} // 紫..
,{L:410, X:0.0435, Y:0.0012, Z:0.2074}
,{L:420, X:0.1344, Y:0.0040, Z:0.6456}
,{L:430, X:0.2839, Y:0.0116, Z:1.3856}
,{L:440, X:0.3483, Y:0.0230, Z:1.7471} // 青..
,{L:450, X:0.3362, Y:0.0380, Z:1.7721}
,{L:460, X:0.2908, Y:0.0600, Z:1.6692}
,{L:470, X:0.1954, Y:0.0910, Z:1.2876}
,{L:480, X:0.0956, Y:0.1390, Z:0.8130}
,{L:490, X:0.0320, Y:0.2080, Z:0.4652}
,{L:500, X:0.0049, Y:0.3230, Z:0.2720} // 緑..
,{L:510, X:0.0093, Y:0.5030, Z:0.1582}
,{L:520, X:0.0633, Y:0.7100, Z:0.0782}
,{L:530, X:0.1655, Y:0.8620, Z:0.0422}
,{L:540, X:0.2904, Y:0.9540, Z:0.0203}
,{L:550, X:0.4334, Y:0.9950, Z:0.0087}
,{L:560, X:0.5945, Y:0.9950, Z:0.0039}
,{L:570, X:0.7621, Y:0.9520, Z:0.0021} // 黄..
,{L:580, X:0.9163, Y:0.8700, Z:0.0017}
,{L:590, X:1.0263, Y:0.7570, Z:0.0011}
,{L:600, X:1.0622, Y:0.6310, Z:0.0008}
,{L:610, X:1.0026, Y:0.5030, Z:0.0003} // 赤..
,{L:620, X:0.8544, Y:0.3810, Z:0.0002}
,{L:630, X:0.6424, Y:0.2650, Z:0.0000}
,{L:640, X:0.4479, Y:0.1750, Z:0.0000}
,{L:650, X:0.2835, Y:0.1070, Z:0.0000}
,{L:660, X:0.1649, Y:0.0610, Z:0.0000}
,{L:670, X:0.0874, Y:0.0320, Z:0.0000}
,{L:680, X:0.0468, Y:0.0170, Z:0.0000}
,{L:690, X:0.0227, Y:0.0082, Z:0.0000}
,{L:700, X:0.0114, Y:0.0041, Z:0.0000}
,{L:710, X:0.0058, Y:0.0021, Z:0.0000}
,{L:720, X:0.0029, Y:0.0010, Z:0.0000}
,{L:730, X:0.0014, Y:0.0005, Z:0.0000}
,{L:740, X:0.0007, Y:0.0003, Z:0.0000}
,{L:750, X:0.0003, Y:0.0001, Z:0.0000}
,{L:760, X:0.0002, Y:0.0001, Z:0.0000}
,{L:770, X:0.0001, Y:0.0000, Z:0.0000}
,{L:780, X:0.0000, Y:0.0000, Z:0.0000}
];
// XYZからRGB作成
function xyz2rgb(elm:Object):Object{
var ret:Object = new Object;
ret.L = elm.L;
ret.R = 3.5064*elm.X - 1.7400*elm.Y - 0.5441*elm.Z;
ret.G = -1.0690*elm.X + 1.9777*elm.Y + 0.0352*elm.Z;
ret.B = 0.0563*elm.X - 0.1970*elm.Y + 1.0511*elm.Z;
return ret;
}
// 三刺激テーブルからRGBテーブルを作成
function createRGBtable_0(tbl:Array):Array{
var array:Array= new Array();
for each(var elm:Object in tbl){
array.push(xyz2rgb(elm));
}
return array;
}
// ガンマ補正と正規化を行う
function gammaCorrection(tbl:Array,gammaFactor:Number){
var max:Number=0;
for(var idx:int=0;idx<tbl.length;++idx){
tbl[idx].R= Math.pow(Math.max(0,tbl[idx].R),gammaFactor);
tbl[idx].G= Math.pow(Math.max(0,tbl[idx].G),gammaFactor);
tbl[idx].B= Math.pow(Math.max(0,tbl[idx].B),gammaFactor);
max= Math.max(max,tbl[idx].R);
max= Math.max(max,tbl[idx].G);
max= Math.max(max,tbl[idx].B);
}
for(idx=0;idx<tbl.length;++idx){
tbl[idx].R /= max;
tbl[idx].G /= max;
tbl[idx].B /= max;
}
}
// テーブル生成
function createRGBtable():Array{
var rgbTable:Array= createRGBtable_0(triStimulusVals);
gammaCorrection(rgbTable,1/2.7);
return rgbTable;
}
// RGBテーブルダンプ
function dumpRGB(tbl:Array){
trace("var rgbTable:Array = [");
var dlmt:String=" ";
for each(var elm:Object in tbl){
trace(" "+dlmt+"{ L:"+elm.L
+" ,R:"+elm.R.toFixed(5)+" ,G:"+elm.G.toFixed(5)
+" ,B:"+elm.B.toFixed(5)+" }");
dlmt=",";
}
trace(" ];");
trace("var wlenStep:int = "+(tbl[2].L-tbl[1].L)+";");
trace("var wlenMin:int = "+tbl[0].L+";");
trace("var wlenMax:int = "+tbl[tbl.length-1].L+";");
}
var rgbTable:Array= createRGBtable();
dumpRGB(rgbTable);
XYZテーブル
三刺激値のグラフを載せます。
小さな値が見づらくXとYの広がりが分からないので、対数軸にしたグラフも載せます。
L,X,Y,Z 380,0.0014,0.0000,0.0065 390,0.0042,0.0001,0.0201 400,0.0143,0.0004,0.0679 410,0.0435,0.0012,0.2074 420,0.1344,0.0040,0.6456 430,0.2839,0.0116,1.3856 440,0.3483,0.0230,1.7471 450,0.3362,0.0380,1.7721 460,0.2908,0.0600,1.6692 470,0.1954,0.0910,1.2876 480,0.0956,0.1390,0.8130 490,0.0320,0.2080,0.4652 500,0.0049,0.3230,0.2720 510,0.0093,0.5030,0.1582 520,0.0633,0.7100,0.0782 530,0.1655,0.8620,0.0422 540,0.2904,0.9540,0.0203 550,0.4334,0.9950,0.0087 560,0.5945,0.9950,0.0039 570,0.7621,0.9520,0.0021 580,0.9163,0.8700,0.0017 590,1.0263,0.7570,0.0011 600,1.0622,0.6310,0.0008 610,1.0026,0.5030,0.0003 620,0.8544,0.3810,0.0002 630,0.6424,0.2650,0.0000 640,0.4479,0.1750,0.0000 650,0.2835,0.1070,0.0000 660,0.1649,0.0610,0.0000 670,0.0874,0.0320,0.0000 680,0.0468,0.0170,0.0000 690,0.0227,0.0082,0.0000 700,0.0114,0.0041,0.0000 710,0.0058,0.0021,0.0000 720,0.0029,0.0010,0.0000 730,0.0014,0.0005,0.0000 740,0.0007,0.0003,0.0000 750,0.0003,0.0001,0.0000 760,0.0002,0.0001,0.0000 770,0.0001,0.0000,0.0000 780,0.0000,0.0000,0.0000
###
(2009/6/16) ExcelによるRGB感度図を追加。プログラムを表示/非表示とした。
(2009/6/21) XYZ図に対数軸版追加
(2011/6/25) 波長、ガンマ補正値を指定するUI付きプログラムを追加
(2011/7/2) XYZ値(3刺激値)も表示するようにした
(2011/11/18) ガンマ補正値を2.2から2.7に変更
2009年6月14日 (日) パソコン・インターネット | 固定リンク
Tweet


コメント
無学なのですが、レーザーを購入し(650nm)このスクリプトと比較しましたところ
全然色が違うように思われました
実物は真っ赤であるのにスクリプトによって表示されたものは黒成分が入ってえんじ色になっています
感度図のピークを右にずれたことをR***の値を下げて反映しているものと思いますが実際は赤成分だけを刺激する光ということで強弱関わらず綺麗な赤色です
とはいえ一般に普及しているスペクトル図が得られているわけですからどうしたらいいものか私にもわかりせん
小話程度に
投稿: まったく素人 | 2014年2月22日 (土) 23時29分
びっくりしたまま、変な投稿をしてしまいました
しかし他の波長の色も十分な光量が得られるとすれば印象が違うのでしょうね
モニターのバックライトとかも関係するのでしょうが
投稿: まったく素人 | 2014年2月22日 (土) 23時44分
610nm辺りからはほぼ純粋な赤となっています。
ただし、徐々に感度が落ち暗くなります。
レーザー光が強いため650nmでも明瞭な赤が見えているものと思われます。
正確に色相を保持するわけではありませんが、例えばガンマ補正値を30くらいに設定すると610nmから上は単純な赤となります。
全体に渡り色相を保ちながら輝度を一定にする仕組みもその内いれようと思います。
投稿: 万象酔歩 | 2014年2月23日 (日) 04時35分