外積

外積内積ほどは応用する事は出来ないのですが、外積を用いる事で、平面の向きの計算(ポリゴンの法線ベクトルの計算)や、2つのベクトルのなす平行四辺形の面積の計算、2つのベクトルのなす角度の計算などに応用できます。
2つのベクトルのなす角度については、内積でも計算できますが、外積を使うと、2つのベクトルの位置関係により、角度の値の+、-を使い分けることが可能になります。

 

内積の定義は以下の通りです。

 

三次元空間における2つのベクトル

 

 

において、ベクトルa とベクトルb の外積は、以下のようになります。

 

 

この外積の計算は少し覚えづらいのですが、ベクトルa とベクトルbの成分を上下に並べて、X成分を求める時はX成分を隠して、残りの4つの成分で下図のように斜めに掛けて引く、いわゆるたすき掛けという計算を行います。(と言っておきながら、私は覚えてないので、毎回調べてます...)

 

 

また、内積の計算結果はただの値(スカラー)でしたが、外積の結果はベクトルとなります。

 

ベクトルなので、外積の結果は向きと大きさを持ち、外積の向きは右ねじの方向となります。

(右ねじの回転方向は外積の掛ける順番となります。)

 

さらに外積の大きさ(ノルム)は2つのベクトルでなす平行四辺形の面積と一致します。

 

 

また、2つのベクトルが三次元の場合と言いましたが、二次元のベクトルであってもベクトルのZ成分を0(ゼロ)にする事で外積の特徴を応用する事も可能です。

 

使える数学へ戻る

 

Word/Excelの数式で分母分子にΣの上下に文字が書けない

Word/Excelの数式エディタで分母、分子にΣ(シグマ)を書くと、このように添え字部分がズレてしまいます。

これをズレないように表示する方法をいろいろ探してみたのですが、正式な方法は見つからなかったのですが、下図のように、それらしく表示する方法を紹介します。

 

Office(Word/Excel)で数式を書く場合、挿入 → 記号と特殊文字 → 数式 の π(パイ) の部分をクリックします。

すると、ここに数式を入力します。 と表示されます。

この状態で、Σの数式を書く場合は、大型演算子総和(上下端値あり)をクリックします。

すると、Σの部分だけは表示され、添え字の部分は □ で表示されます。

この □ の部分に必要な文字を書くと、このように↓なります。

と、ここまではいいのですが、Σを分数に書くと、添え字の位置がズレてしまいます。

そこで、以下の手順でそれらしく表示するようにします。

まず、分数ではなく、行列の2x1行列を追加します。

この状態で、分母分子にΣを書くと、添え字がズレる事なく、表示されます。
しかしながら、分数の線が無い状態です。

分数の線を書くのに、オーバーライン/アンダーラインを流用します。
まず、分母分子の文字が長い方の右端(もしくは左端)をカーソルで選択します。

次にアクセントのアンダーライン(分子の文字が長い場合は、オーバーライン)をクリックします。

すると、アンダーライン付きの □ が表示されます。

この□の部分に、Σの部分をカットし、ペーストすると、下図のようになります。

これで、分数の分母/分子にΣの上下に添え字付きの数式のように見えますでしょ?!

内積

内積は計算そのものは簡単な割にはとても応用範囲が広いので、私の好きな処理の一つなのです。

 

内積の定義は以下の通りです。

 

上図のようにベクトルaとベクトルbの成分を以下のように表すと

 

 

2つのベクトルの内積は

 

 

であらわされます。

 

ただし、ベクトルの大きさは、ノルムと呼ばれ、以下のように計算します。

 

 

さらに一般的にn次元ベクトルの場合、2つのベクトルを

 

a=(a1,a2,a3,・・・,an)

b=(b1,b2,b3,・・・,bn)

 

とすると(添え字の無いは太字で表します)、内積の計算は

 

 

となります。

 

ここまでが、普通に学校で習う内積なのですが、この内積の式をいろいろ変形させて使う事で、応用例が広がります。

 

例えば内積の式を cosθ= の式に変形して

 

 

とすると、定式で求めたcosθのアークコサインを計算すると、2つのベクトルのなす角を求める事ができます。

 

さらにcosθをそのまま用いて、cosθが1の時は2つのベクトルが向きが一致し、cosθが-1の時は2つのベクトルが向きが逆向きとなる事から2つのベクトルの大きさに関係なく、2つのベクトルがどれだけ似ているか?の指標に使われる場合があります。例えば、画像処理では正規化相関が、まさにそのものとなります。

 

また、内積の式を

 

 

のように変形すると、ベクトルbの成分の内、ベクトルaの方向にどれだけの成分があるか?の計算をする事ができます。

 

 

この処理は、いづれ出てくるであろう正規直行規定への斜影の処理と同じです。

 

さらに言うとフーリエ変換の一部とも考える事ができます。

 

と言う事で、内積そのものの計算は同じ成分同士を掛けて足し合わせだけなので簡単なのですが、応用範囲が広いので好きなのです。

 

内積はニュアンス的には、どれだけ似ているか? もしくは 似ている成分を抽出する手法として用いる事ができます。

 

例えば3×3フィルタの一つのソーベルフィルタも、

 

X方向へは

-1 0 1
-2 0 2
-1 0 1

 

Y方向へは

-1 -2 -1
0 0 0
1 2 1

 

と表せれますが、これは一般的には画像を微分している!という捉え方が多いですが、9次元ベクトルの内積を計算している!とも考えられます。

 

そう考えると、ソーベルフィルタの結果は、左から右(上から下)方法へ向かって暗から明へ変化する場合は値が正となり、明から暗へ変化する場合は負となるのも納得できます。

 

さらにエッジの強さが強い時は値が大きくなるのも内積の特性と一致します。

 

と、無理やり内積をこじつける必要もありませんが、正規化相関やフーリエ変換は内積と同じ!と思えると、一見難しそうなアルゴリズムでも、少し簡単に見えてくると思います。
 

使える数学へ戻る

 

ベクトルの引き算

ベクトルaからベクトルbを引く場合、

 

上図のように引く側のベクトルの終点から、引かれる側の終点へ向かうベクトルが引き算の結果となります。

 

といっても覚えづらいので、引く側のベクトルを負にして、向きを逆にし、ベクトルの足し算として捉えた方が覚えやすいと思います。

 

 

ベクトルの成分であらわすと、

 

 

となります。

学校で、このように答えると×をもらうかも?しれませんが、ベクトルは向きと大きさしか持たず、位置の情報は無いので、大人になってから覚えるベクトルの引き算はこれでOKです。

 

使える数学へ戻る

 

ベクトルの足し算

ベクトルaとベクトルbを足す場合、

 

上図のように始点⇒終点⇒始点⇒終点とつなき合わせていき、最初のベクトルの始点から最後のベクトルの終点へ向かうベクトルが足し算の結果となります。

 

 

ベクトルの成分で表すと

 

 

となります。

足す順番を入れ替えても結果は同じです。

 

 

使える数学へ戻る

 

OpenCVをスタティックリンクライブラリでビルドする方法

スタティックリンクライブラリを使うと他のPCへ作成した実行ファイルを移植する時にexeファイルをコピーすればOpenCVをインストールする事なく実行できるので、メリットはあるので、使ってみたいのですが、これが、なかなか分かりづらい。

 

OpenCV2.3.1ではファイル(OpenCV-2.3.1-win-superpack.exe)をダウンロードし、解凍するとスタティックリンクライブラリらしきフォルダ(staticlib)があるのですが、*.libファイルが不足しているらしく、未解決のシンボルのエラーが出て使う事が出来なさそうです。

 

そのため、OpenCV-2.3.1-win-superpack.exeのソースからCMakeを使ってスタティックリンクライブラリをビルドしてみました。

 

CMakeでビルドする方法はOpenCV2.3の入手、ダウンロード、インストール、環境設定のページを参考にして頂きたいのですが、上記のページから異なるポイントを示したいと思います。

 

今回OpenCV-2.3.1-win-superpack.exeを c:\ に解凍している事を想定しています。

 

まずCMakeで作成するプロジェクトの保存先のフォルダを作成しておきます。

 

今回は C:\opencv\userbuild というフォルダを作成するとします。

 

CMakeを起動し

 

Where is the source codeに C:/opencv

Where is the source codeに C:/opencv/userbuid

 

を指定します。

 

 

次にConfigureボタンをクリックします。

すると下図のようなウィンドウが開くので、使用するVisual Studioのバージョンを選択します。

 

 

Finishボタンをクリックすると下図のように表示されるので、BUILD_SHARED_LIBSのチェックを外します。CUDAに対応していないPCの場合はWHITH_CUDAのチェックも外して下さい。

 

 

次にConfigureボタンをクリックするとBUILD_WITH_STATIC_CRTという項目が表示されるので、チェックを入れたまま、Configureボタンをクリックします。

 

 

すると赤く表示された部分が無くなるので、Generateボタンをクリックします。

 

 

するとWhere is the source codeで指定したフォルダ(\userbuild)にソリューションファイル(OpenCV.sln)が作成されるので、このファイルをダブルクリックし、VisualStudioを開きます。

 

開いたVisualStudioのメニューからビルド→構成マネージャーを選択します。

 

次にINSTALLの項目にあるビルドの部分にチェックを入れます。

左上のアクティブソリューション構成の部分のDebugReleaseを選択しそれぞれにチェックを入れます。

 

この状態で、Visual Studioのメニューのビルド→ソリューションのビルドを選択し、OpenCVをビルドします。このビルドには、かなり時間がかかりますが、ReleaseDebugの両方でビルドを行って下さい。

 

これでWhere is the source codeで指定したフォルダ(\userbuild)内に必要なヘッダファイルとライブラリファイルが作成されます。

 

スタティックリンクライブラリを使用する方法

まず、Visual Studioを起動し、ファイル⇒新規作成と選択し、Visual C++⇒Win32内のWin32コンソールアプリケーションを選択し、ウィンドウの下の方にある名前をプロジェクトを作成する場所を指定します。

 

次に表示されたウィンドウで完了をクリックするとスケルトンプログラムが作成されます。

 

次にVisual Studioのメニューのプロジェクト⇒(プロジェクト名)のプロパティを選択し、構成のプロパティ⇒C/C++⇒全般と選択し、追加のインクルードディレクトリの部分に先程作成したOpenCVのopencv2のあるフォルダを指定します。(右上の構成の部分でDebugとReleaseを切り替えて同じ設定をして下さい。)

C:\opencv\userbuild\install\include

 

同じウィンドウで構成のプロパティ⇒リンカー⇒入力と選択し特性の指定のライブラリの無視の部分で

 

(Debugの場合)

LIBCMTD.lib;msvcprtd.lib

(Releaseの場合)

LIBCMT.lib;msvcprt.lib

 

を指定します。

 

これで、ソースコード上に下記のようにヘッダファイル(*.hpp)とライブラリファイル(*.lib)の設定を行うと、OpenCVをスタティックリンクライブラリとして使用する事が可能になります。

 

下記はWebカメラを撮影する簡単なコードです。

 

// OpenCVStaticSample.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

//*******************************************************************************
// プロジェクトの設定
// ①プロジェクトのプロパティ⇒C/C++⇒全般 の追加のインクルードディレクトリに
//      C:\opencv\userbuild\install\include
//   を追加
// ②プロジェクトのプロパティ⇒リンカー⇒入力 の特定の規定のライブラリの無視に
//    Debugのとき
//        LIBCMTD.lib;msvcprtd.lib
//    Releaseのとき
//        LIBCMT.lib;msvcprt.lib
//    を追加
//*******************************************************************************

#include "opencv2\\opencv.hpp"

#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"vfw32.lib")	// Video for Windows

#ifdef _DEBUG
    //Debugモードの場合
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_core231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_imgproc231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_highgui231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_objdetect231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_contrib231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_features2d231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_flann231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_gpu231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_legacy231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_ts231d.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_video231d.lib")

    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\zlibd.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libtiffd.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libpngd.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libjpegd.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libjasperd.lib")

#else
    //Releaseモードの場合
	#pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_core231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_imgproc231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_highgui231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_objdetect231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_contrib231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_features2d231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_flann231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_gpu231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_legacy231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_ts231.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\lib\\opencv_video231.lib")

    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\zlib.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libtiff.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libpng.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libjpeg.lib")
    #pragma comment(lib,"C:\\opencv\\userbuild\\install\\share\\OpenCV\\3rdparty\\lib\\libjasper.lib")

#endif

int _tmain(int argc, _TCHAR* argv[])
{

	// カメラオープン
	cv::VideoCapture cap(0);

	// カメラがオープンできたかの確認
	if(!cap.isOpened()) return -1;

	cv::namedWindow("Capture", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
	while(1) {
		cv::Mat frame;
		cap >> frame;  // キャプチャ
		// 様々な処理
		// ...
		cv::imshow("Capture", frame); // 表示
		if(cv::waitKey(30) >= 0)
		{
			cv::imwrite("cap.png", frame);
			break;
		}
	}

	return 0;
}

※実際に開発したPCとは別のPCへ実行ファイル(*.exe)を移植する場合、別途、C++のランタイムが必要となります。

 

移植先のPCに開発したPCで使用したVisual Studioと同じバージョンがインストールされていない場合、C++の再頒布パッケージというものをインストールします。

 

Visual Studio 2010 C++(32bit)の場合、以下の場所よりダウンロードして下さい。

Microsoft Visual C++ 2010 SP1 再頒布可能パッケージ (x86)

 

これで、OpenCVをスタティックリンクライブラリで使えていると思うのですが、この特定のライブラリの無視する部分と、comctl32.libとvfw32.libを追加しないといけない部分が分かりづらかった...

ただ、warning LNK4049のワーニングが幾つか出てしまい、いまいち、このwarningの消し方は分かりませんでした...

 

OpenCVへ戻る

 

ベクトルの基礎

ベクトルは向き大きさをもった量を表します。

向きが逆の場合、ベクトルは負となります。

 

ベクトルを座標で表すと以下のようになります。

 

ベクトルの大きさは三平方の定理より、以下のようになり、この大きさをノルムと言います。

 

三次元の場合も同様に点P(X,Y,Z)から点P(X,Y,Z)へ向かうベクトルの場合は

 

となり、ノルムも

となります。

 

ここまでは、ベクトルを矢印で表す事のできるベクトルで幾何ベクトルと言います。

 

画像処理においては、より一般的にn個の数の組み合わせからなるn次元ベクトルとして扱う場合があります。

 

表記方法は一般に文字を太字にして

 

 

のように書きます。

 

こうなると、矢印で表す事ができなく、数ベクトルと言います。

 

画像処理的には、例えば、輝度値の平均、最小値、最大値、分散を使った

 

  a=(平均、最小値、最大値、分散)

 

のような4次ベクトル!みたいな扱いをしたりします。(かなりいいかげんな例ですが。)

 

最初はなんでこんな難しそうに聞こえる(頭が良さそうに聞こえる)表現をするんだろうな~と思っていましたが、ベクトルとして扱う事で、内積などのベクトルの演算が使えたり、行列としても扱えたりするので、それなりのメリットも出てきます。

 

使える数学へ戻る

 

奇関数・偶関数の積分

奇関数・偶関数でも説明したように、

関数f(x)が奇関数の場合、-a~aの範囲での積分は

 

関数f(x)が偶関数の場合、-a~aの範囲での積分は

となります。

 

一般的な式では奇関数と偶関数に分けて積分を行います。

(計算例)

となります。

こうする事で計算量を格段に減らす事ができます。

 

現実的には積分の範囲が-a~aになる事はまれで、普通はa~bのような任意範囲で積分する場合が多いかと思います。

そこで、グラフ(関数)全体をabの中心[(a+b) / 2]が原点位置に来るように平行移動を行い、積分範囲が原点に対し対称になるように調整します。

(この処理を行っても積分の結果には影響はありません。)

 

 

こうする事で、一般的な積分においても、奇関数・偶関数の特徴を用いる事が可能となります。

 

なぜ、わざわざそんな事をするかと言うと、積分の部分をfor文に置き換えて関数の合計を計算するプログラムを考えると、この奇関数・偶関数の特徴を用いると計算量が、

 

奇関数の部分で0へ

偶関数の部分で半分に

 

減らせる可能性があるので、トータルで1/4に計算量を減らせるかも?しれないので、その分、処理が高速になります。

 

その例を最小二乗法の最適化で紹介しています。

 

使える数学へ戻る

 

C#(.NET)からOpenCVを使う方法、OpenCvSharpのインストール方法

OpenCVはシンプルな画像表示用のウィンドウも用意されているので、簡単に画像処理を試したい場合には非常に良いのですが、少し凝ったウィンドウを作成しようとすると、やっぱり.NETからOpenCVを触りたくなります。

 

.NETの言語(VB.NET、C#、C++/CLI)からOpenCVを使う方法は、だいたい以下の通り

  • .NETのラッパーライブラリを使う
  • 自作でラッパーライブラリを作成する
  • C++/CLIから直接使う

となりますが、実質的にはOpenCvSharpを使う事になると思います。

 

OpenCvSharpのインストール方法は、現在ではNuGetによりプロジェクトごとにインストールします。

 

OpenCvSharpのインストール方法

まず、インストールするC#プロジェクトを用意します。

新規でC#プロジェクトを作成する場合は、次のように行います。

Visual Studio を起動後、新しいプロジェクトの作成をクリックします。

次に Windowsフォームアプリケーション(.NET FRamework)を選択し、次へをクリックします。

プロジェクト名、場所を指定し、作成をクリックします。

これで、以下のようにフォームが表示されたらベースとなるC#プロジェクトが作成されています。

 

次に、このC#プロジェクトにNuGetでOpenCvSharpをインストールします。

メニューのプロジェクト→NuGetパッケージの管理をクリックします。

表示された画面で、 参照 を選択し、その下のテキストボックスに OpenCvSharp と入力すると、OpenCvSharpの一覧が表示されるので、この中から OpenCvSharp.Windows を選択します。

OpenCvSharp.Windows を選択すると、右側に インストール ボタンが表示されるので、これをクリックします。

しばらくすると、インストールが完了します。

OpenCvSharpがインストールされたか?確認するには、プロジェクトの参照の部分の表示を展開すると、OpenCvSharpの名前空間が表示されている事を確認します。

簡単にOpenCvSharpの動作を確認します。

ツールボックスの中から Button を選択し、フォーム上にドラッグ&ドロップします。

ボタンをダブルクリックし、C#コードを表示し、上の方に、

using OpenCvSharp;

と入力し、ボタンイベントの部分に以下のように記載します。

private void button1_Click(object sender, EventArgs e)
{
    var img = new Mat(new OpenCvSharp.Size(256, 256), MatType.CV_8UC3, new Scalar(35, 123, 254));

    Cv2.ImShow("Image", img);
}

参考までに、Formの全コードを示します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using OpenCvSharp;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var img = new Mat(new OpenCvSharp.Size(256, 256), MatType.CV_8UC3, new Scalar(35, 123, 254));
            Cv2.ImShow("Image", img);
        }
    }
}

この状態で、▶開始 ボタンをクリックし、以下のように表示されれば OpenCvSharp のインストールは成功しています。

 

OpenCvSharpを使うポイント

2021年現在、OpenCVの情報はPythonであふれているので、OpenCvSharpの情報を探すのは難しくなってきています。

OpenCvSharpの情報を入手するには、少し古くなりましたが、作者であるSchimaさんのページを参照することをお勧めします。

http://schima.hatenablog.com/archive/category/OpenCvSharp

リファレンスは英語になりますが、こちらです。

http://shimat.github.io/opencvsharp/api/OpenCvSharp.html

 

あとは、OpenCvSharpは基本的にOpenCVのC++版のラッパーライブラリであるため、OpenCVのC++の情報を検索し、OpenCvSharpでは、どうするのか?を推測するのもイイかと思います。

 

参考

https://github.com/shimat/opencvsharp

http://shimat.github.io/opencvsharp/api/OpenCvSharp.html

OpenCVへ戻る

疑似逆行列(一般逆行列)の計算と使用方法

逆行列の計算では行数と列数が等しい正方行列のみ計算が可能でした。
しかし、下記のようにn個の未知数(a1~an)に対して、式がm本 (m>n)ある場合、
逆行列を使って解くことができません。

 

 

上記式を行列であらわすと

 

 

となり、行列部分を記号で書くと

 

・・・式①

 

のようになり行列Xに関して逆行列が求まれば、未知数の行列Aについて解く事が可能となります。

行列Xの行数が列数よりも多い時(未知数よりもデータ数が多い時)、擬似逆行列(一般逆行列ともいう)という物を使って、行列Aに関して近似的に解く事が可能となります。

擬似逆行列は、通常の逆行列転置行列を使って、下記のように現されます。

 

・・・式②

 

この擬似逆行列を使って式①は

 

 

のように未知数を解く事が可能となります。

 

ちなみに、この計算は最小二乗法と同じ結果になるそうです。

という事で、この擬似逆行列を使って、Excelで近似式を求めてみたいと思います。

 

ExcelではXとYのデータのグラフを書くと、近似曲線の追加という機能を使えば簡単に求まります。

でも、この近似式をエクセルの計算を使って求めたいな~という事もあるかと思います。

 

 

そこで、擬似逆行列を使って、データを二次関数で近似する時の例で説明したいと思います。

まず、二次関数の一般式

 

 

を少し変形して

 

 

のようにします。このXとYに近似するm個のデータ(X, Y)を代入し、行列で表すと

 

のようになり、行列の各要素をエクセルで計算します。

 

 

次が少々難しいのですが、エクセルのワークシート関数の

  • 行列の積:mmult
  • 逆行列 :minverse
  • 転置行列:transpose

を使って、疑似逆行列を求めます。

 

今回は未知数が3つなので、縦に3つのセルを選択し、

=MMULT(MMULT(MINVERSE(MMULT(TRANSPOSE(行列Xの範囲),行列Xの範囲)),TRANSPOSE(行列Xの範囲)),行列Yの範囲)

 

のように入力します。

(入力例)

=MMULT(MMULT(MINVERSE(MMULT(TRANSPOSE(B45:D54),B45:D54)),TRANSPOSE(B45:D54)),G45:G54)

 

のように入力し、Ctrl+Shift+Enterキーを同時に押すと、a1~a3が求まり、二次の近似式が求まります。

 

 

このエクセルのファイルはエクセルにより疑似逆行列の計算よりダウンロードできます。

 

エクセルで行列の計算は少し分かりづらいので、【Excel】行列の積、逆行列、転置行列の計算のページも参考にして下さい。

 

今回は二次関数の近似を行いましたが、これさえ知っていれば、未知数が定数で行列で式を表せればいいだけなので、かなり応用が効くと思います。

 

使える数学へ戻る

 

バイラテラルフィルタ

ガウシアンフィルタなどのフィルタでは、ノイズをできるだけ除去しようとすると、輪郭もボケてしまうという欠点がありました。
この欠点を解決しようとした処理アルゴリズムがバイラテラルフィルタ(bilateral filter)です。

 

バイラテラルフィルタは処理前の画像データの配列をf(i, j)、処理後の画像データの配列をg(i, j)とすると

 

 

となります。

 

ただし、がカーネルのサイズ、σがガウシアンフィルタを制御、σが輝度差を制御しています。

と言われても、何だか式が難しくて良く分かりません。

 

でも、分母分子に出てくる最初のexpの部分はガウシアンフィルタで見たことがあるな~

という事に気が付けば、突破口が開けます。

 

2つ目のexpの部分が良く分からないので、とりあえず取っちゃってみて、

 

 

とすると、分母の部分がガウシアンフィルタと少し違うけど、Σの範囲が-W~Wなので、(2W+1)×(2W+1)のカーネルサイズのガウシアンフィルタになっています。

 

結局、分母はカーネルの値の合計なので、やっぱりガウシアンフィルタそのものだという事に気が付きます。

 

ここで、ガウシアンフィルタを使って、どうやれば輪郭をぼやかさず、ノイズだけを除去できるか?を考えると、カーネルの中心の輝度値と差の少ないところだけをガウシアンフィルタで平滑化すればいいのではないか?という発想が浮かびます。

 

例えば次のような画像において、

 

四角で囲まれた部分を拡大し注目すると、5x5のガウシアンフィルタの場合、中心の輝度値に近い部分を重み「1」、輝度差が大きい部分は重みが「0」になるようにして、

 

 

一般的な5x5のガウシアンフィルタの係数↓、

 

 

に重みをかけると、それぞれのカーネルの値は

 

カーネルの合計の169で割る カーネルの合計の209で割る

 

となります。

このようにして、場所、場所のカーネルの値を画像に合わせて変えて行くと、輪郭を残しつつノイズだけを除去できそうな感じがします。

 

しかし、輪郭付近の重みは「1」にするべきか?「0」にするべきか?少し悩みます。

そこで、カーネルの中心の輝度値との差に基づいて、重みを「0.3」や「0.8」のようなグレーゾーンを設けるために、輝度差を横軸にした正規分布を用いてみます。

 

正規分布のグラフはこんな感じ↓

 

になっていて、中心の0付近ほど値が大きく、外側(+方向、-方向)へ行くに従って値が小さくなります。

この性質を使ってカーネルの中心の輝度値との差「f(i, j)- f(i + m, j + n) 」を横軸にとった正規分布の式は

 

 

となり、これを重みに使うと、「1」「0」だった重みが、輝度差が小さいと重みが大きく、輝度差が大きいと重みが小さくなるように、なだらかに変化します。

 

この正規分布を重みとしたのが、まさにバイラテラルフィルタなのです。

つまりバイラテラルフィルタは

 

正規分布の重み付きガウシアンフィルタ

 

なのです。

 

ここで、最初に示したバイラテラルフィルタの式を見てみると、

分子は(2W+1)×(2W+1)サイズのカーネルの範囲内の輝度値に、ガウシアンフィルタの係数をかけ、さらにカーネルの中心との輝度差を用いた正規分布の値を掛け合わせた値、

分母はカーネルの合計値で、カーネルの合計値が1になるように調整しています。

 

Wはカーネルの大きさ、σは通常のガウシアンフィルタの係数と同じ。

σの値が、カーネルの中心の輝度値との差をどの程度許容するか?を制御している事が分かります。

そのため、σの値を大きくしていくと、ただのガウシアンフィルタの処理に近づき、輪郭もぼやけてしまいます。逆にσの値を小さくし過ぎると、ノイズ除去効果が弱くなります。

 

そこで、実際にはエッジを保持しつつノイズをできるだけ除去したい場合は、1回のバイラテラルフィルタでσ1、σの値を調整しようとするよりも、バイラテラルフィルタを何回か繰り返した方が効果的です。

 

原画 1回目 2回目
3回目 4回目 5回目

 

このように、バイラテラルフィルタはガウシアンフィルタのカーネルに輝度差に基づいて重みを付けている訳ですが、この、×××に基づいて重みを付ける処理という考え方は、処理を安定させるポイントになったりします。

 

例えばロバスト推定法なんかも近い感じ。

 

画像処理アルゴリズムへ戻る

OpenCV2.3.1の入手、ダウンロード、インストール、環境設定

2012.5現在、OpenCVの最新バージョンはVer2.4です。
こちらのインストール方法についてはOpenCV2.4の入手、ダウンロード、インストール、環境設定を参照願います。


OpenCV2.3.1の入手、ダウンロード先

OpenCVの入手は下記ページよりダウンロードして下さい。

http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.3.1/

 

上記リンク先へ行き、OpenCV-2.3.1-win-superpack.exeをクリックしてファイルをダウンロードして下さい。

 

OpenCV2.3.1のインストール方法

ダウンロードしたファイル(OpenCV-2.3.1-win-superpack.exe)をダブルクリックすると、ファイルの解凍が始まるので、ここでは c:\ に解凍する事にします。(慣れている人はどこでも構いません。)
Extract to:の部分に  c:\ と入力して下さい。

 

 

ファイルを解凍すると、主な構成は下記のようになっています。

c:\opencv
┣ 3rdparty
┣ android
┣ build
┃┣ common
┃┣ gpu
┃┣ include
┃┣ python
┃┣ x64
┃┃ ┣mingw
┃┃ ┣vc9
┃┃ ┗vc10
┃┗ x86
┃  ┣mingw
┃  ┣vc9
┃  ┗vc10
┃   ┣bin
┃   ┣lib
┃   ┗staticlib
┣ data
┣ doc
┣ include
┣ module
┗ samples

 

次にダウンロードしたdllファイルをPCから使えるようにPathの設定というのを行います。

 

Pathの設定はWindowsのスタートボタンをクリック → コンピュータ右クリックプ ロパティをクリックします。

 

 

開いたウィンドウの システムの詳細設定 をク リックします。

 

 

次に詳細設定タブの環境変数のボ タンをクリックします。

 

 

次にシステム環境変数のPathを選択し、編集をクリックします。

 

 

変数値のテキストボックスの最後の部分に、OpenCVのdllファイルのあるフォルダを指定します。

 

;C:\opencv\build\x86\vc10\bin

 

※もともとあった文字は消さないようにご注意ください。
最初の ; (セミコロン)を付けるのもお忘れなく。
PATHの設定後、PCを再起動して下さい。(ログオフ⇒ログインでも可)

 

上記の設定は32bitOSでVisual Studio2010を使う場合の設定です。
使用する環境に合わせてディレクトリを指定して下さい。

 

32bit、Visual Studio2008の場合 C:\opencv\build\x86\vc9\bin
32bit、Visual Studio2010の場合 C:\opencv\build\x86\vc10\bin
64bit、Visual Studio2008の場合 C:\opencv\build\x64\vc9\bin
64bit、Visual Studio2010の場合 C:\opencv\build\x64\vc10\bin

 

64bitOSを使っていても32bitでプログラムする場合は x86 フォルダ内のbinディレクトリを指定して下さい。
64bitを使う場合は、VisualStudioのプラットフォームをx64にして下さい。

 

TBBの入手、ダウンロード

OpenCV2.3.1のバイナリ版(コンパイルされているdll)を使う場合は、TBB(Intel Threading Building Blocks)という並列処理用のライブラリが必要となります。
OpenCVをダウンロードすると \opencv\build\common\tbb のフォルダにTBBのdllが入っているのですが、debug版が不足してます。

 

そのため、TBBのライブラリを入手します。

 

入手先
http://threadingbuildingblocks.org/

上記リンクのページへ行き、上側のメニューのDownloads⇒ページ中央部のStable Releaseと移動し、最新のバージョンのTBBをクリックします。(2012年1月現在、tbb40_20111130oss

 

次にWindows版のDownloadボタンをクリックし、ファイルをダウンロードします。
Windows版の場合、tbb40_20111130oss_win.zip

 

ダウンロードしたファイルを解凍すると下記のようなフォルダ構成になっています。
\tbb40_20111130oss_win\tbb40_20111130oss
┣ bin
┃┣ ia32
┃┃ ┣vc8
┃┃ ┣vc9
┃┃ ┗vc10
┃┗ intel64
┣ doc
┣ examples
┣ include
┗ lib

 

そこで、今回は32bitOSでVisualStudio2010を使う事を想定しているので、
フォルダ(\tbb40_20111130oss_win\tbb40_20111130oss\bin\ia32\vc10)内にあるdllファイル

 

tbb.dll、tbb_debug.dll、tbb_preview.dll、tbb_preview_debug.dll、tbbmalloc.dll、tbbmalloc_debug.dll、tbbmalloc_proxy.dll、tbbmalloc_proxy_debug.dll

 

を全てOpenCVのbinフォルダ(C:\opencv\build\x86\vc10\bin)へコピーして下さい。
32bit/64bit、VisualStudioのバージョンによりフォルダが異なるので、環境に合わせてフォルダを選択して下さい。

 

これで、OpenCVの設定は完了です。

 

VisualStudio2010での使用方法

使用するのはVisualStudio C++ 2010 Express以上のエディションで大丈夫です。

 

VisualStudio2010を起動し、新しいプロジェクトをクリックし、左側のメニューのWin32を選択した状態でWin32コンソールアプリケーションをクリックします。

 

 

適当な名前(上図ではOpenCV2.3.1Sample)を付け、保存する場所を指定し、OKボタンをクリックします。

 

すると下記のようなソースコードが表示されます。

 

// OpenCV2.3.1Sample.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	return 0;
}

 

次にOpenCVのヘッダファイルが、今回作成したプロジェクトから参照できるように設定を行います。

 

プロジェクトの名前の部分を右クリックし、プロパティを選択します。

 

 

表示されたウィンドウ右上の構成を全ての構成にし、構成のプロパティ⇒C/C++⇒全般を選択して、OpenCV2のフォルダのあるディレクトリ(C:\opencv\build\include)を追加のインクルードディレクトリに指定します。

 

 

以上の設定を行い、ソース部分を下記のようにすると、ガウシアンフィルタ処理を行う簡単なサンプルプログラムが実行できると思います。

 


// OpenCV2.3.1Sample.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

//プロジェクトのプロパティ⇒C/C++⇒全般 の追加のインクルードディレクトリに
// opencv2のあるフォルダ『C:\OpenCV\include』などを追加のこと
#include "opencv2\opencv.hpp"

#ifdef _DEBUG
    //Debugモードの場合
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_core231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_imgproc231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_highgui231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_objdetect231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_contrib231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_features2d231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_flann231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_gpu231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_haartraining_engined.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_legacy231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_ts231d.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_video231d.lib")
#else
    //Releaseモードの場合
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_core231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_imgproc231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_highgui231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_objdetect231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_contrib231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_features2d231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_flann231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_gpu231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_haartraining_engined.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_legacy231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_ts231.lib")
    #pragma comment(lib,"C:\\opencv\\build\\x86\\vc10\\lib\\opencv_video231.lib")
#endif

int _tmain(int argc, _TCHAR* argv[])
{

    //画像データの読込
    IplImage* src_img = cvLoadImage("C:\\opencv\\samples\\c\\lena.jpg", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
    if (src_img == NULL){
        return 0;
    }

    //表示ウィンドウの作成
    cvNamedWindow("src");
    cvNamedWindow("dst");

    //処理後画像データの確保
    IplImage* dst_img = cvCreateImage(cvGetSize(src_img), src_img->depth, src_img->nChannels);

    //画像処理例(ガウシアンフィルタ)
    cvSmooth(src_img, dst_img, CV_GAUSSIAN, 9);

    //画像の表示
    cvShowImage ("src", src_img);
    cvShowImage ("dst", dst_img);

    //キー入力待ち
    cvWaitKey (0);

    //全てのウィンドウの削除
    cvDestroyAllWindows();

    //画像データの解放
    cvReleaseImage(&src_img);
    cvReleaseImage(&dst_img);

    return 0;
}

 

ちなみに、libファイルは全て必要ではなく、おおむね

 

opencv_core231、opencv_imgproc231、opencv_highgui231、opencv_objdetect231

 

の4種類ぐらいである程度動作すると思います。(使用する関数によって必要なlibファイルが決まります。)

 

また、libファイルのファイル名に含まれる数字部分がOpenCVのバージョンを示し、ファイル名の最後についているdの文字がDebug用のlibファイル、dの無いファイルがRelease用のlibファイルとなっています。

 

その他

OpenCV2.3.1においてもCMakeを使って自分でOpenCVをコンパイルする事が可能です。

 

CMakeのソースコードにはOpenCV-2.3.1-win-superpack.exeを解凍したフォルダ(opencv)を指定して下さい。

 

OpenCV2.3の場合の説明ですが、詳細は下記ページを参照下さい。

 

https://imagingsolution.net/program/opencv/opencv2-3/opencv2-3-downlaod-install/#compile-opencv

 

基本的にOpenCVなどのライブラリを使う場合は、ライブラリの3点セット(*.dll、*.lib、*.h)をいかに使うか?がポイントになります。

 

余裕のある方は下記ページも参考にしてみて下さい。

ライブラリの使用方法、VisualStudioの設定方法

 

これを覚えておくと、OpenCVがバージョンアップした場合や他のライブラリを使う場合など、応用ができると思います。
 

OpenCVへ戻る