2018年10月27日土曜日

MinGWでOpenCVを使えるようにする

前回、MSYS2をインストールして、gcc関連のパッケージを導入しましたので、C++のプログラムがコンパイルできるはずです。で、以下のようなC++プログラムを「test01.cpp」というファイル名で用意したとします。
#include <iostream>
using namespace std;
int main(){
  cout << "Hello, World." << endl;
  return 0;
}
MinGW 64bitのshellを起動し、test01.cppを作ったディレクトリに移動します。
cd f:\OpecCV
で、以下のコマンドを入力します。
gcc test01.cpp -o test01 -lstdc++
そうしますと、「test01.exe」という実行ファイルができます。これを実行しますと、「Hello, World」が表示されます。

次に、OpenCVを導入します。OpenCVは、コンピュータ・ビジョンに関する機能を持つライブラリです。MSYS2ではOpenCVパッケージが用意されていて、以下のようにするとインストールできます。
pacman -S mingw-w64-x86_64-opencv
で、以下のようなC++プログラムを「test02.cpp」というファイル名で用意したとします。
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
    Mat img = cv::imread("image.png");
    imshow("image", img);
    waitKey(0);
    return 0;
}
このプログラムをgccでコンパイルするのですが、設定が色々面倒ですので、以下のようなMakefileを作ります。
#Makefile
INC = -I "C:\msys64\mingw64\include\opencv4"
DIR =  -L "C:\msys64\mingw64\bin"
FLAGS = -lstdc++ -lopencv_core410 -lopencv_highgui410 -lopencv_imgcodecs410
OBJS = test02

main:
 gcc $(INC) $(DIR) test02.cpp -o $(OBJS) $(FLAGS)
で、以下のようにmakeすると、実行ファイルができます。
make
この実行ファイルを実行すれば、画像が表示されます。

2018年10月6日土曜日

MSYS2を通してMinGWを使う

Windows10でC++プログラミングをするとき、統合開発環境としてVisual Studioを使うのが一般的だと思います。ただ、Visual Studioをインストールすると、Windows10の設定を色々変えるので、不都合が生じるのが不安です(実際に生じたわけではありません)。代替措置としてgccを使うことを考えます。Windowsでgccを使えるようにする開発環境として、MinGW (Minimalist GNU for Windows)があります。今回は、これをWindows10にインストールします。

ただ、MinGWの最低限の機能だけをインストールするというわけにもいかないでしょう。gccのバージョンは一定期間ごとにupdateされますし、ライブラリだって利用したいでしょう。そうすると、色んなパッケージをインストールすることになるのですが、それらも管理していかないといけません。で、MSYS2 (Minimal SYStem 2)というのを使います。これは、Windows上で動作するプログラム開発環境を提供するシステムです。UnixやLinuxと同様にshell(bash)端末でコマンドを使って操作します。 ということで、これからやることは、

  • MSYS2をインストールする
  • MinGWをインストールする
  • です。

    まず、MSYS2のページにアクセスして、MSYS2のインストーラー「msys2-x86_64-20190524.exe」をダウンロードします。

    次に、ダウンロードした「msys2-x86_64-20190524.exe」を実行します。基本的にポップアップ画面の右下の「次へ」をクリックしていけば、「C:\msys64」にMSYS2がインストールされます。Windowsのメニューで「MSYS2 64bit」をクリックして、以下のようなshellが現れればインストール成功です。

    で、パッケージのデータベースを更新します。
    pacman -Sy
    そして、以下のコマンドでパッケージを更新します。
    pacman -Su
    ここで、以下のような警告メッセージが出たら、MSYS2を一旦閉じ、もう一度立ち上げ直して上記コマンドを再度実行します。
    警告: terminate MSYS2 without returning to shell and check for updates again
    警告: for example close your terminal window instead of calling exit
    
    警告メッセージが出なくなればOKです。

    次に、MinGW-w64(64bit)とその関連パッケージをインストールします。

    pacman -S base-devel
    pacman -S mingw-w64-x86_64-toolchain
    

    一旦、MSYS2のshellを閉じて、MinGW 64-bitのshelを起動します。そこで、以下のコマンドを実行して、下のような表示が出ればOKです。

    gcc --version
    

    2018年5月23日水曜日

    畳み込み積分

    今、時間 \( t \) と共に変化する信号 \( x(t) \) が下の図の太線のようになっていたとして、この信号を、先に示した単位インパルス関数を用いて表すことを考えよう。単位インパルス関数は、面積1の長方形 \( \delta_\varepsilon (t) (1 < t < \varepsilon) \) で \( \varepsilon \to 0 \) の極限をとったものとして定義された。そこで、この \( \delta_\varepsilon (t) \) を少しずつずらして、 \( x(t) \) を構成する。\( x(t) \) を離散時間 \( \varepsilon \) で分割すると、下図の赤い矩形のようになる。

    時間 \( t \) での高さは、 \( x(k\varepsilon) \delta_\varepsilon (t-k \varepsilon) \varepsilon \) で近似できる。 \( k \) は、時間幅 \( \varepsilon \) が何個あるかを表し、\( x(k \varepsilon) \) は \( t=k \varepsilon \) のときの \( x(t) \) の値を表す。\( \delta_\varepsilon (t-k \varepsilon) \) は \( \delta_\varepsilon (t) \) が時間 \( k \varepsilon \) だけずれたことを表し、\( \delta_\varepsilon (t-k \varepsilon) \varepsilon \) は1である。例えば、\( k=1 \) なら、\( x(\varepsilon) \delta_\varepsilon (t-\varepsilon) \varepsilon \) となるし(下図一つ目)、\( k=-2 \) なら、\( x(-2 \varepsilon) \delta_\varepsilon (t+2 \varepsilon) \varepsilon \) となる(下図二つ目)。

    これらを足し合わせると、 \begin{equation} x(t) \approx \sum_{k=-\infty}^\infty x(k\varepsilon) \delta_\varepsilon (t-k \varepsilon) \varepsilon \end{equation} となる。で、近似を等式にするには、\( \varepsilon \to 0 \) の極限をとればよい。そうすると、\( \delta_\varepsilon (t) \) は単位インパルス関数 \( \delta (t) \) となり、次式が得られる。 \begin{equation} x(t) = \int_{-\infty}^\infty x(\tau) \delta(t-\tau) d\tau \end{equation}

    2018年5月9日水曜日

    重ね合わせの理

    重ね合わせの理は、「電気回路で複数の電源があるとき、各電源(起電力)によって流れる電流を求め、それらの足し合わせが回路全体の電流に等しい」という定理です。例えば、以下のような電気回路を考えたとき、この回路に流れる電流はオームの法則から \begin{equation} I = \frac{E_1 + E_2}{R} \end{equation} となります。

    重ね合わせの理を使えば、一方の電源を取り除いて、片方ずつ電流を求め、後でそれらを足し合わることで、全体の電流を求めることができます。で、まず、一方の電源 \( E_1 \) を残して、もう一方の電源 \( E_2 \) を短絡しますと、この回路に流れる電流は、 \begin{equation} I_1 = \frac{E_1}{R} \end{equation} となります。

    次に、反対側の電源を戻して、先ほど残した電源を短絡します。そうしますと、この回路に流れる電流は、 \begin{equation} I_2 = \frac{E_2}{R} \end{equation} となります。

    で、求めた電流をそれぞれ足し合わせると、以下のように回路全体を流れる電流が求められるのです。 \begin{equation} I = \frac{E_1 + E_2}{R} \end{equation} この回路は単純ですので、重ね合わせの理を用いなくても、冒頭のように起電力を足し合わせてオームの法則を適用するやり方で簡単に求めることができます。しかし、回路が複雑になると、重ね合わせの理は重宝することになります。

    ここで、\( E_1, E_2 \) を入力、\( I_1, I_2 \) を出力と考えたとき、以下のことが成り立ちます。

    1. 入力 \( {E_1 + E_2} \) に対する出力は \( I_1 + I_2 \)
    2. 入力 \( \alpha E_1 \) に対する出力は \( \alpha I_1 \) ( \( \alpha \) は定数 )
    このような関係は線形ですし、これを入出力システムとみれば、線形システムということになります(下図)。

    2018年4月26日木曜日

    単位インパルス関数

    \( t < 0 \) のとき 0 で、\( t \ge 0 \) のとき 1 となる関数を考えます。でも、これだと、下の図のように \( t = 0 \) のところで不連続になってしまいます。で、その右の図のように、0 から 1 に変化するところに幅 \( \varepsilon \) を持たせます。これで、一応連続になります。

    \( \varepsilon \to 0 \) のように極限をとりますと、以下のようになります。これが単位ステップ関数というものです。 \begin{equation} u(t) = \lim_{\varepsilon \to 0} u_\varepsilon (t) \end{equation} ここで、上の右図に対して範囲を区切って微分します(\( t=0, t=\varepsilon \)では微分不可能)。 \begin{equation} \delta_\varepsilon (t) = \frac{{\rm d} u_\varepsilon (t)}{{\rm d} t} \end{equation} このとき、\( \delta_\varepsilon (t) \) として面積1の長方形を考えます(下図右)。そうしますと、 \begin{equation} \frac{1}{\varepsilon} ( 0 < t < \varepsilon )\\ 0 \ \ ( それ以外 ) \end{equation} となります。ここで、\( \varepsilon \to 0 \) のように極限をとりますと、以下のようになります。 \begin{equation} \delta (t) = \lim_{\varepsilon \to 0} \delta_\varepsilon (t) \end{equation} これが単位インパルス関数(ディラックのデルタ関数)です。\( \varepsilon \to 0 \)としたとき、\( \delta (t) \) は \( \infty \) になりますので、図では正確に書き表せません。で、便宜的に大きさ1の矢印とします(下図左)。

    つまり、 \begin{equation} \int_{-\infty}^\infty \delta(t) dt = \lim_{\varepsilon \to 0} \int_0^\varepsilon \delta(t) dt = \lim_{\varepsilon \to 0} \int_0^\varepsilon \frac{1}{\varepsilon} dt = 1 \end{equation} ということです。関数 \( f(t) \) を考えた場合、\( f(0) \) は \begin{equation} \int_{-\infty}^\infty f(t) \delta(t) dt = f(0) \end{equation} となります。\( t = \tau \) であれば、単位インパルス関数 \( \delta (t) \) を \( \tau \) だけ平行移動した \( \delta (t - \tau ) \) を考えればよいのです。 \begin{equation} \int_{-\infty}^\infty f(t) \delta(t - \tau) dt = f(\tau) \end{equation}

    で、単位ステップ関数 \( u(t) \) を、単位インパルス関数で表せば、次式となります。 \begin{equation} u (t) = \int_{-\infty}^\infty u(t) \delta( t - \tau ) dt = \int_0^\infty \delta (t-\tau) dt = \int_{-\infty}^t \delta(\tau) d\tau \end{equation}

    2018年4月11日水曜日

    畳み込み

    信号処理の分野では畳み込み積分という重要な処理があります。「畳み込み」と言いますと、ディープラーニングの主要な構造の一つ、畳み込みニューラルネットワーク(CNN)でお馴染みかも知れません。ただ、CNNでは画像とフィルタの間の相関を表すものに近く、実際、画像の画素とフィルタのフィルタ係数との積和演算になっています。

    位置\( (i, j) \)の画素の画素値を \( x_{ij} \) とし、位置\( (p, q) \)のフィルタのフィルタ係数を \( h_{pq} \) とすれば、その積和演算は以下のようになります。

    \begin{equation} u_{ij} = \sum_{p=0}^{H-1} \sum_{q=0}^{H-1} x_{i+p,j+q} h_{pq} \end{equation}

    この積和演算では、画像の中に、フィルタの濃淡パターンと似た濃淡パターンがどこにあるかを検出する働きがあります。したがって、フィルタが持つ濃淡の特徴を、画像から抽出していると考えることができます。で、その先に行く前に、この畳み込み積分をちゃんと理解しておきましょう。

    信号処理では、システムとして線形で時間的に特性が変化しないものを考えることがあります。これを、線形時不変システムと呼びます。知りたいのは、システムに入力があったとき、出力がどうなるかということです。ただ、システムの特性は明示的にはわからないものとします。このとき、インパルス応答と入力の畳み込み積分によって、システムの出力が計算できるのです。インパルス応答というのは、システムに単位インパルス信号を入力したときの出力応答のことです。

    ということで、まず、インパルス応答について知る必要がありますね。その詳細を次に書きます。