2016/02/27
OpenCVでカラーバーを作る
サーモグラフィーのように温度データを色で表現すると、測定対象の温度の情報が平面的に(2Dで)理解できて、大変わかりやすいものです。通常のXYグラフはある一つの情報(位置や時間など)に対するデータ(点:1D)を表すことのみしかできないので、パラメータが多い情報を扱う場合、不便な気がします。今回は、モノクロ画像(8bitデータの「輝度」)をカラー画像(24bitデータの「色」)に変換して、カラーバーを作成しました。これは、一般的に「擬似カラー(pseudo color」と言うそうです。このサイトの内容を参照いたしました。このサイトは、以前、画像処理の仕事をしていた時によくお世話になっていました。著者さんには直接面識はありませんが、著者さんの勤務されている会社は一度仕事で訪問したことがあります。
まず、モノクロのバー画像を作成します。

// ColorBar.cpp
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
using namespace cv;
using namespace std;
int brightness = 0; //画像輝度
Mat monoimage = Mat(Size(640, 60), CV_8UC1); //モノクロ画像
Mat colorimageL = Mat(Size(640, 60), CV_8UC3); //カラー画像(Liner)
Mat colorimageC = Mat(Size(640, 60), CV_8UC3); //カラー画像(Circle)
//カラー変換(Liner)
void ExchangeColorLnr(int x, int y, int brt) {
// 輝度を0.0〜1.0に変換
double xbrt = (double)brt/(double)255;
//カラー変換
if (xbrt >= 0 && xbrt <= 0.25) {
colorimageL.data[y * colorimageL.step + x * 3 + 0] = 255; //B
colorimageL.data[y * colorimageL.step + x * 3 + 1] = (int)(xbrt / 0.25*(double)255); //G
colorimageL.data[y * colorimageL.step + x * 3 + 2] = 0; //R
}
else if (xbrt > 0.25 && xbrt <= 0.5) {
colorimageL.data[y * colorimageL.step + x * 3 + 0] = (int)(510 - (xbrt / 0.25)*(double)255); //B
colorimageL.data[y * colorimageL.step + x * 3 + 1] = 255; //G
colorimageL.data[y * colorimageL.step + x * 3 + 2] = 0; //R
}
else if (xbrt > 0.5 && xbrt <= 0.75) {
colorimageL.data[y * colorimageL.step + x * 3 + 0] = 0; //B
colorimageL.data[y * colorimageL.step + x * 3 + 1] = 255; //G
colorimageL.data[y * colorimageL.step + x * 3 + 2] = (int)(xbrt / 0.25 * 255 - (double)510); //R
}
else {
colorimageL.data[y * colorimageL.step + x * 3 + 0] = 0; //B
colorimageL.data[y * colorimageL.step + x * 3 + 1] = (int)(1020 - (xbrt / 0.25)*(double)255); //G
colorimageL.data[y * colorimageL.step + x * 3 + 2] = 255; //R
}
}
//カラー変換(Circle)
void ExchangeColorCrc(int x, int y, int brt) {
// 輝度を0.0〜1.0に変換
double xbrt = (double)brt / (double)255;
//カラー変換
if (xbrt >= 0 && xbrt <= 0.25) {
colorimageC.data[y * colorimageC.step + x * 3 + 0] = 255; //B
colorimageC.data[y * colorimageC.step + x * 3 + 1] = (int)((double)255 * sin(xbrt *2 * M_PI)); //G
colorimageC.data[y * colorimageC.step + x * 3 + 2] = 0; //R
}
else if (xbrt > 0.25 && xbrt <= 0.5) {
colorimageC.data[y * colorimageC.step + x * 3 + 0] = (int)((double)255 * sin(xbrt * 2 * M_PI)); //B
colorimageC.data[y * colorimageC.step + x * 3 + 1] = 255; //G
colorimageC.data[y * colorimageC.step + x * 3 + 2] = 0; //R
}
else if (xbrt > 0.5 && xbrt <= 0.75) {
colorimageC.data[y * colorimageC.step + x * 3 + 0] = 0; //B
colorimageC.data[y * colorimageC.step + x * 3 + 1] = 255; //G
colorimageC.data[y * colorimageC.step + x * 3 + 2] = (int)(-(double)255 * sin(xbrt * 2 * M_PI)); //R
}
else {
colorimageC.data[y * colorimageC.step + x * 3 + 0] = 0; //B
colorimageC.data[y * colorimageC.step + x * 3 + 1] = (int)(-(double)255 * sin(xbrt * 2 * M_PI)); //G
colorimageC.data[y * colorimageC.step + x * 3 + 2] = 255; //R
}
}
//メイン
int main(void) {
for (int y = 0; y < monoimage.rows; y++) {
for (int x = 0; x < monoimage.cols; x++) {
brightness = (int)((double)x / (double)monoimage.cols*(double)255); //モノクロバー
monoimage.data[y * monoimage.cols + x] = brightness;
ExchangeColorLnr(x, y, brightness); //カラー変換(Liner)
ExchangeColorCrc(x, y, brightness); //カラー変換(Circle)
}
}
//画像保存
imwrite("c:\\pic\\monobar.png", monoimage);
imwrite("c:\\pic\\colorbarL.png", colorimageL);
imwrite("c:\\pic\\colorbarC.png", colorimageC);
return 0;
}
ここで、80行目がモノクロバーを作成するところです。カラー変換は、直線的に変換する方法と曲線的に(三角関数を用いて)変換する方法の2パターンを確認しました。上図が直線的な変換、下図が曲線的な変換を表しています。
横軸は、モノクロ画像の輝度値に対応する値です。輝度値0〜255が、0〜1に対応しています。こうしておけば、横軸のデータレンジがどのように変わっても対応しやすくなります。縦軸は、RGBのそれぞれの輝度を表しています。直線的な変換は18〜45行、曲線的な変換は47〜74行のプログラムに対応しています。

結果のカラーバーは以下の通りです。上図が直線的な変換、下図が曲線的な変換です。

