fc2ブログ

OpenCVでカラーバーを作る

 サーモグラフィーのように温度データを色で表現すると、測定対象の温度の情報が平面的に(2Dで)理解できて、大変わかりやすいものです。通常のXYグラフはある一つの情報(位置や時間など)に対するデータ(点:1D)を表すことのみしかできないので、パラメータが多い情報を扱う場合、不便な気がします。

今回は、モノクロ画像(8bitデータの「輝度」)をカラー画像(24bitデータの「色」)に変換して、カラーバーを作成しました。これは、一般的に「擬似カラー(pseudo color」と言うそうです。このサイトの内容を参照いたしました。このサイトは、以前、画像処理の仕事をしていた時によくお世話になっていました。著者さんには直接面識はありませんが、著者さんの勤務されている会社は一度仕事で訪問したことがあります。

まず、モノクロのバー画像を作成します。160228monobar.pngプログラムはOpenCVで作成し、以下の通りです。

// 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行のプログラムに対応しています。
ColorEx.png 
 結果のカラーバーは以下の通りです。上図が直線的な変換、下図が曲線的な変換です。
160228colorbarL.png160228colorbarC.png 直線的な変換の方は、横軸が0.25の水色、0.75の黄色付近の領域が狭く、急激に色が変化している感があります(グラフを見ても明らかですが)。加えて、緑色の領域が広い。一方、曲線的な変換ではまんべんなく色が分布し、急激な色の変化もありません。データにもよりますが、曲線的な変換が私には好ましいように思いました。次のステップは、画像ではない実際のデータに適用していくことです。
スポンサーサイト



OpenCVで大王のコマを作る

 ベンハムのコマに引き続き、今回は大王のコマです。Daioh.png この図形を回転させます。前回、前々回(ベンハムのコマの巻)でお話しした方法で、動画を作成しました。[広告] VPS
 ネットで調べると、水色系、赤系の色が見えるとの話ですが、私には薄い黄色が確認できました。照明(蛍光灯、太陽光)の関係で色が変わるようですが、回転動画ではそこまでの色は再現できませんでした。発色原理がよく分かっていない現象なので、興味のままにぼちぼち調べていきたいと思います。

OpenCVでベンハムのコマを作る(2)

  今回は、前回に引き続いて動画作成のお話です。OpenCVのVideoWriterクラスを用いて、複数の静止画像をつなぎ合わせて動画を作成しました。

//MovieMake.cpp
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/videoio/videoio.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(void) {

Mat img; //読み込み画像
string filename = ""; //ファイル名
VideoWriter vw("/Users/xxx_xxx/Documents/pic/movie.mov", CV_FOURCC('m', 'p', '4', 'v'), 29.97, Size(240, 240), true);

for (int i = 0; i < 1200; i++) {
for (int j = 0; j < 60; j++) {
//6度刻みの画像を選択し、動画作成
filename = "/Users/xxx_xxx/Documents/pic/" + to_string(j*6) + ".png";
img = imread(filename, 1);
vw.write(img);
}
}
return 0;
}
 16行目が作成する動画設定に関するVideoWriterのコンストラクタで、以下の通りです。
VideoWriter::VideoWriter(const String&filename,int fourcc,double fps,Size frameSize,bool isColor = true)

filename:出力動画ファイル名
fourcc:コーデックを指定する4文字のキャラクター。今回は、movファイルを指定するため、mp4vを指定。
fps:動画のフレームレート
frameSize:動画のフレームサイズ
isColor:カラーの画像ファイルとして出力するかを指定(Windowsのみ有効のFlagとのこと)

18〜25行目は、画像を合成する部分です。6度刻みで1200回転分を合成しています。コマが回転している動画をどのように見せるかは、16行目のフレームレートと、何度刻みで動画を合成するかによっていろいろ変化します。前回のお話で1度刻みで画像を作成したのも、いろいろな条件で実験する必要があったからです。また、動画再生ソフトウェアや動画編集ソフトウェアが変わっても、見え方が変わったりしました。いろいろ遊べそうで、大変興味深いですが、難儀なことですね。
 次回は、ベンハムのコマの親戚のような、「大王のコマ」のお話をします。

OpenCVでベンハムのコマを作る(1)

 OpenCVで複数の静止画像を並べて動画にするソフトウェアをお遊びで作成しました。パラパラ漫画のようなものは簡単に作成できるのですが、満足できずに、何か意外な変化があるものが良いなと思っていたところでした。
 学生時代に「光学」の勉強をしていた時に、コラムで読んだ「ベンハムのコマ」。早速、下宿で作ってコマを回してみると確かに色が付いていることに驚きを感じました。ベンハムのコマは下図のような白黒の図形を描いた円形のコマです。これを回転させると、色が付いた模様が確認できるというもの。人によって見え方が異なるようで、この現象自身は完全に解明されていないとのこと。脳に関係した現象なのでしょうか・・・?Benhampic160221.png ベンハムのコマにはいろいろなデザインがあり、大王のコマなどの類似のコマもあります。少し、遊べそうです・・。実際に作成した動画は以下の通りです。私には紺色、緑色、黄土色が確認できました。[広告] VPS

 今回、OpenCVで基準の画像を読み込んで、一定角度を回転させた画像を複数枚作成し、それを順番に動画に書き込むことで、コマが回っている状態を再現しました。プログラムは、回転画像を保存するプログラムと、その画像を組み合わせて動画にするプログラムの2つに分けて作成しました。回転画像を保存するプログラム(BenhamPic.cpp)はXcode(C++)でOpenCV3.0を用いて作成し、以下の通りです。

//BenhamPic.cpp

#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(void) {
Mat loadimage; //読み込み画像
Mat grayimage; //グレー画像
Mat thresoldimage; //二値化画像
Mat rotateimage; //回転画像
double angle = 0.0;
double scale = 1.0;
string savefilename = "";

loadimage = imread("/Users/xxx_xxx/Documents/benham.png", IMREAD_COLOR); //画像読み込み
cvtColor(loadimage, grayimage, CV_RGB2GRAY); //グレー処理
threshold(grayimage, thresoldimage, 0, 255, THRESH_BINARY | THRESH_OTSU); //二値化処理

Mat resizeimage(Size(240,240),thresoldimage.type()); //リサイズ画像
resize(thresoldimage, resizeimage, resizeimage.size(), INTER_CUBIC);

for (int i = 0; i < 360; i++) {
angle = (double)i; //1度刻み
Point2d center(resizeimage.cols*0.5, resizeimage.rows*0.5);
const Mat affine_matrix = getRotationMatrix2D(center, angle, scale);

//回転処理
warpAffine(resizeimage, rotateimage, affine_matrix, rotateimage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar::all(255));

//画像保存
savefilename = "/Users/xxx_xxx/Documents/pic/" + to_string(i) + ".png";
imwrite(savefilename,rotateimage);
}
return 0;
}
 基準画像のbenham.pngを21行目で読み込み、グレー処理(22行目)、二値化処理(23行目)を行った後に、25、26行目で動画用の画像サイズ(今回は240×240pixelに固定)に揃えます。画像を回転させるため、元のbenham.pngの画像中心がずれないように余白を事前に調整しておきます。28行目から39行目は、画像を回転させて、保存する部分です。今回は1度刻みで360枚の画像を作成しました。画像のサンプリング精度として、1度刻みで選択可能です。
 動画作成については、次回にお話しします。

OpenCV3.1をインストールする(2)

 前回お話ししたMacBook AirのWindow10にインストールしたOpenCV3.1の動作確認を行いました。Visual Studio 2015を起動し、Visual C++のWin32コンソールアプリで、テストコードを書きました。opencv310_inst_4.png プログラムをビルドする前に、追加のインクルードディレクトリ、追加のライブラリディレクトリ、追加の依存ファイルの3点セットを設定しました。opencv310_inst_5.pngopencv310_inst_6.png 追加の依存ファイルには、プログラムで利用するライブラリを設定します。opencv310_inst_7.png ビルドすると、エラーが2件、警告が4件発生。警告(よくある警告)はともかく、エラーの原因はimreadの実行にありました。ネットで調べていたところ、OpenCV3.0からimreadは、imgcodecs.libの関数になっていました・・。早速、コードにヘッダファイルを追加し、ライブラリにもimgcodecs.libを追加し、再ビルドすると、エラーなくパスしました。opencv310_inst_8.png 実行すると、無事に動作を確認できました。OpenCV3.1は無事にインストールできているようです。opencv310_inst_9.png そういえば、以前MacにOpenCV3.0をインストールし、動かした際にも同じ問題に直面していたことを思い出しました。3ヶ月前のブログにも書いていました。すっかり記憶から飛んでいました。情けない話です・・。老化現象ですね・・。

OpenCV3.1をインストールする(1)

 今回は、Bootcampで動かしているMacBook AirのWindows10OpenCV3.1をインストールしました。その備忘録です。

1.opencvサイトからopencv-3.1.0.exeをダウンロードします。
2.opencv-3.1.0.exeを実行します。Zipファイルの解凍先をc:\にします。opencv310_inst_0.pngc:\ドライブにopencvフォルダが作成されます。この時点のディスクサイズは565 MBです。フォルダ名をopencv310に変更します。
3.CMake(GUI版)を起動して、opencvをビルドするためのソリューションファイルを作成します。CMakeはこのサイトからダウンロードします。
 Where is the source code: に  c:¥opencv310¥sources
 Where to build the binaries に c:¥opencv310¥build
を設定し、Configureボタンを押します。Windowが開くので、ビルドに使用する「Visual Studio 14 2015」を選択し、「Use default native compilers」を選択し、finishボタンを押します。opencv310_inst_1.png ビルドのオプション一覧とその有効/無効を設定できる画面が表示されます。初めは背景が赤色状態で表示されますが、その状態でオプションの変更を行いました。私の環境では、「WITH CUDA」のチェックを外すだけで、その他の設定は初期状態のままにしました。再度、Confifgureボタンを押した後に、Generateボタンを押します。背景の赤色が消えて、「Generating done」が表示されると、完了です。opencv310_inst_2.png この段階で、フォルダのディスクサイズは761 MBになっていました。加えて、フォルダ内に、opencv.slnファイルが作成されます。
4.ビルド実行のために、opencv.slnをダブルクリックして、Visual Studioを起動させます。起動後、「ビルド」-「バッチビルド」を選択し、ALL_BUILDのDebugとReleaseのビルド欄にチェックを入れ、ビルドボタンを押すと、ビルドが開始されます。opencv310_inst_3.png 待つこと約15分でビルドは無事終了しました。この段階でフォルダのディスクサイズは6.91GBに巨大膨張していました。ただ、最終的に実使用で必要なのは、bin、include、lib関係の限られたもので、ビルドにのみ使用されたファイル類は不要?なので、後日整理しようと思いました。
 
 久々に、OpenCVをCMakeからセットアップしたのですが、何となくあっけなく終了しました。以前セットアップした時は、CMakeで背景の赤色が消えずに何もわからないままチェックを外したりして、その後のビルドでもエラーが出たりと、苦労した経験しかなかったです。今回すんなり行ったことに本当に大丈夫かなと思いました。次回は、実際にコードを書いてプログラムを動かした内容をお話しします。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: