指定任意颜色作为伪彩

在掌握了RGB与HSB颜色空间转换后,我们可以指定任意颜色作为伪彩。

我们知道一个成像数据可能有多个通道,但是一个通道如果使用最简单的单色伪彩来表示,通常就是选择 RGB 中一个或者两个通道来进行线性映射:

  • R:红色伪彩

  • G:绿色伪彩

  • B:蓝色伪彩

  • RG:黄色伪彩

  • RB:粉色伪彩(Magenta)

  • GB:青色伪彩(Cyan)

如果我们做多色成像时,通道数量超过 6,其它通道也想使用单色伪彩来表示,该怎么办呢?这个时候可以通过指定 HSB 中任意一个色度,然后把信号的强度,映射为颜色的亮度或者对比度。

首先可以利用 HSB颜色空间来绘制一个 colormap,看看颜色的线性分布情况:

imagej_hsb_color_map.gif-55cd7ee8fe.gif

这里我们其实按照 Hue、Saturation、Brightness 三个分量,分别线性采样,看看颜色的分布。图像从左到右横轴是 Hue 从 0 到 360,可以很明显地看到颜色0的分区。从上到下是 Brightness,亮度从0-255;然后不同的 slice 从前往后,对应的色彩饱和度不断增大。其主体逻辑代码如下:

newImage("map","RGB white",360,255,10);
for(k=0;k<10;k++){
setSlice(k+1);
sat=(k+1)/10;
for(i=0;i<360;i++){
hue=i;
for(j=0;j<255;j++){
bri=j;
hsb = newArray(hue,sat,bri);
rgb = HSB2RGB(hsb); // 具体函数代码见下方
setColor(rgb[0],rgb[1],rgb[2]);
fillRect(i,j,1,1);
}
}
}

然后我们找一个单通道荧光图像,尝试使用任意颜色:

imagej_hsb_change_color.gif-ddd19b45c4.gif

接下来放上主要逻辑代码:

imgtype = bitDepth();
if (imgtype!=24){
exit("Error: Input is not RGB image.");
}
hues = newArray(30, 80, 130, 180, 230, 280, 330);
n = lengthOf(hues);
for (i = 0; i < n; i++) {
hue = hues[i];
main(hue);
}
/* --- 自定义函数 --- */
function main(hue){
w = getWidth();
h = getHeight();
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
hsb = getHSB(i,j);
v = hsb[2]; // 提取原图像亮度
if (v>0){
hsb = newArray(hue, 1, v);
// 保持原图像亮度,固定饱和度,修改色度
rgb = HSB2RGB(hsb);
setColor(rgb[0], rgb[1], rgb[2]);
fillRect(i, j, 1, 1);
}
}
}
}

这里整理一下RGB和HSB颜色空间互换和取值的函数:

function getHSB(x,y){
rgb = getRGB(x,y);
HSB = RGB2HSB(rgb);
return HSB;
}
function RGB2HSB(rgb){
r = rgb[0]; // red
g = rgb[1]; // green
b = rgb[2]; // blue
Array.getStatistics(rgb, min, max, mean, stdDev);
if (max==min) h = NaN; //h(0-360)
else if (max==r && g>=b) h = 60*(g-b)/(max-min);
else if (max==r && g<b) h = 60*(g-b)/(max-min)+360;
else if (max==g) h = 60*(b-r)/(max-min)+120;
else if (max==b) h = 60*(r-g)/(max-min)+240;
if (max==0) s = 0; //s(0-1)
else s = 1-min/max;
b = max; //b(0-255)
HSB = newArray(h,s,b);
return HSB;
}
function getRGB(x,y){
RGB = newArray(3);
v = getPixel(x,y);
RGB[0] = (v>>16)&0xff; // extract red byte (bits 23-17)
RGB[1] = (v>>8)&0xff; // extract green byte (bits 15-8)
RGB[2] = v&0xff; // extract blue byte (bits 7-0)
return RGB;
}
function HSB2RGB(hsb){
h = hsb[0]; //0~360
s = hsb[1]; //0~1
v = hsb[2]; //0~255
hi = floor(h/60);
f = h/60-hi;
p = v*(1-s);
q = v*(1-f*s);
t = v*(1-(1-f)*s);
if (hi==0) rgb=newArray(v,t,p);
if (hi==1) rgb=newArray(q,v,p);
if (hi==2) rgb=newArray(p,v,t);
if (hi==3) rgb=newArray(p,q,v);
if (hi==4) rgb=newArray(t,p,v);
if (hi==5) rgb=newArray(v,p,q);
return rgb;
}

另外加上一段网上收集的输入波长输出颜色的转换 ImageJ Macro代码:

/*
* This script implements the wavelength to RGB conversion
* illustrated at http://www.physics.sfasu.edu/astro/color/
* by linear approximation of the curves.
*/
function setColorByWavelength(wavelength) {
// These values will be between 0 and 1
red = green = blue = 0;
if (wavelength < 380 || wavelength > 780)
abort("Only wavelengths between 380 and 780 are supported");
else if (wavelength <= 440) {
red = (440 - wavelength) / (440 - 380);
blue = 1;
}
else if (wavelength <= 490) {
green = (wavelength - 440) / (490 - 440);
blue = 1;
}
else if (wavelength <= 510) {
green = 1;
blue = (510 - wavelength) / (510 - 490);
}
else if (wavelength <= 580) {
red = (wavelength - 510) / (580 - 510);
green = 1;
}
else if (wavelength <= 645) {
red = 1;
green = (645 - wavelength) / (645 - 580);
}
else
red = 1;
intensity = 1;
if (wavelength > 700)
intensity = 0.3 + 0.7 * (780 - wavelength) / (780 - 700);
else if (wavelength < 420)
intensity = 0.3 + 0.7 * (wavelength - 380) / (420 - 380);
red *= intensity;
green *= intensity;
blue *= intensity;
// assuming gamma == 1
setForegroundColor(red * 255, green * 255, blue * 255);
}
wavelength = getNumber("Wavelength", 480);
setColorByWavelength(wavelength);