旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【Python】 matplotlibの使用例

matplotlibの使用例をメモしておきます。月別の東京の平均気温を表示する例を示しています。平均気温を棒グラフや散布図を使って表すことは無いと思いますが、あくまでも使い方に焦点を当てているためご容赦下さい。

折れ線グラフ

import numpy as np
from matplotlib import pyplot as plt

if __name__ == '__main__':
    # 月
    months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    # 平均気温
    temperatures = [6.1, 7.2, 10.1, 15.4, 20.2, 22.4,
                    25.4, 27.1, 24.4, 18.7, 11.4, 8.9]
    
    # 折れ線グラフを作成する
    plt.plot(months, temperatures, color='orange', marker='o')
    # グラフのタイトル
    plt.title("Tokyo average temperatures on 2016")
    # 軸ラベル
    plt.xlabel("Month")
    plt.ylabel("Temperature")
    # 罫線を表示する
    plt.grid()
    # グラフを表示する
    plt.show()

f:id:ni4muraano:20170126144909j:plain

棒グラフ

import numpy as np
from matplotlib import pyplot as plt

if __name__ == '__main__':
    # 月
    months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    # 平均気温
    temperatures = [6.1, 7.2, 10.1, 15.4, 20.2, 22.4,
                    25.4, 27.1, 24.4, 18.7, 11.4, 8.9]
    
    # 棒の幅が0.8なので、0.4のオフセットを加えて棒をセンタリングさせる
    xs = [m - 0.4 for _, m in enumerate(months)]
    # 棒グラフを作成する
    plt.bar(xs, temperatures)
    # グラフのタイトル
    plt.title("Tokyo average temperatures on 2016")
    # 軸ラベル
    plt.xlabel("Month")
    plt.ylabel("Temperature")
    # 罫線を表示する
    plt.grid()
    # グラフを表示する
    plt.show()

f:id:ni4muraano:20170126145750j:plain

散布図

import numpy as np
from matplotlib import pyplot as plt

if __name__ == '__main__':
    # 月
    months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    # 平均気温
    temperatures = [6.1, 7.2, 10.1, 15.4, 20.2, 22.4,
                    25.4, 27.1, 24.4, 18.7, 11.4, 8.9]
    
    # 散布図を作成する
    plt.scatter(months, temperatures)
    # グラフのタイトル
    plt.title("Tokyo average temperatures on 2016")
    # 軸ラベル
    plt.xlabel("Month")
    plt.ylabel("Temperature")
    # 罫線を表示する
    plt.grid()
    # 軸の縦横比を等しくする
    plt.axis('equal')
    # グラフを表示する
    plt.show()

f:id:ni4muraano:20170126150348j:plain

固有値、固有ベクトルのイメージ

行列をかけるとは

固有値固有ベクトルの計算方法は確か高校の時に習ったような気がしますし、OpenCV等既存のライブラリを使っても簡単に計算出来ますが、その意味が良く分かっていなかったのでメモします。

まず、点(1,1)に適当に作った行列Aをかけてみます。 $$ A= \left( \begin{array}{ccc} 1 & -4 \\ 3 & 2 \\ \end{array} \right) $$ 計算結果は(-3,5)となります。これは、点(1,1)に行列Aをかけたことで、点(1,1)が点(-3,5)に移動したと考えることが出来ます。
f:id:ni4muraano:20170125110329j:plain
更に、移動の仕方を細かく見ると、"回転"と"長さの変化"に分けることが出来ます。
f:id:ni4muraano:20170125105711j:plain
ここで行列Aは適当に作った行列なので、一般的には点に行列をかけると"回転する"+"長さが変わる"という変化が起こるんだろう、と想像することが出来ます。

次に、点(1,1)に適当ではなく意図的に作成した別の行列Rをかけてみます。 $$ R= \left( \begin{array}{ccc} 0 & -1 \\ 1 & 0 \\ \end{array} \right) $$ 計算結果は(-1,1)となります。先と同様に点が移動していますが、先の例と異なり、回転はしていますが長さは変わっていません。
f:id:ni4muraano:20170125114052j:plain
今の例で利用した行列Rはいわゆる回転行列(回転行列 - Wikipedia)で、回転のみの作用を持ちます。

固有値固有ベクトルについて

ここまでで
1. 点に行列をかけると"回転する"、"長さが変わる"という変化が起こる
2. 特殊なケースでは長さが変わらずに回転のみの変化が起こる
ということが分かりました。そうすると、次は「回転することなく、長さだけが変わるようなケースもあるのでは?」と想像できます。試しに全ての要素が1の行列と点(1,1)をかけると以下のようになります。 $$ \left( \begin{array}{ccc} 1 & 1 \\ 1 & 1 \\ \end{array} \right) \left( \begin{array}{ccc} 1 \\ 1 \\ \end{array} \right) = \left( \begin{array}{ccc} 2 \\ 2 \\ \end{array} \right) = 2 \left( \begin{array}{ccc} 1 \\ 1 \\ \end{array} \right) $$ 結果を図示するまでもなく、回転することなく長さが二倍になっていることが分かります。ようやく固有値固有ベクトルの話となりますが、教科書で見るA{\bf x}=\lambda {\bf x}というのは、「回転することなく、長さだけが変わるA{\bf x}の組み合わせが存在する可能性がありますよ。存在した場合、{\bf x}A固有ベクトルと呼びましょう。どれ位長さが変わるかを表す\lambda固有値と呼びましょう。」と言っている、というイメージを持てば良いかと思います。

固有値固有ベクトルの応用例

Aを対称行列とし、A固有値固有ベクトル(a_1,\lambda_1),(a_2,\lambda_2),…(a_n,\lambda_n)とした場合、Aを下記のように表現することができます。

A={\lambda_1}{a_1}{a_1^T}+{\lambda_2}{a_2}{a_2^T}+{...}+{\lambda_n}{a_n}{a_n^T}

仮に\lambda_1, \lambda_2と比較して{\lambda_3}{...}{\lambda_n}の値が小さければ、Aを以下のように近似することができます。

A{\fallingdotseq}{\lambda_1}{a_1}{a_1^T}+{\lambda_2}{a_2}{a_2^T}

例として、具体的に以下の対称行列Aで試します。 $$ A = \left( \begin{array}{ccc} 1 & 0.967 & 0.376 & 0.311 \\ 0.967 & 1 & 0.415 & 0.398 \\ 0.376 & 0.415 & 1 & 0.972 \\ 0.311 & 0.398 & 0.972 & 1 \end{array} \right) $$ A固有値固有ベクトルは以下の通りです。

\lambda_1=2.721, a_1=(0.487, 0.511, 0.508, 0.493)^T
\lambda_2=1.222, a_2=(0.527, 0.474, -0.481, -0.516)^T
\lambda_3=0.052, a_3=(-0.499, 0.539, -0.504, 0.455)^T
\lambda_4=0.005, a_4=(0.485, -0.474, -0.506, 0.533)^T

\lambda_1, \lambda_2と比較して\lambda_3, \lambda_4の値は小さいので、

A{\fallingdotseq}{\lambda_1}{a_1}{a_1^T}+{\lambda_2}{a_2}{a_2^T}

と近似できそうです。実際にこれを計算してみると、 $$ A = \left( \begin{array}{ccc} 0.985 & 0.982 & 0.363 & 0.321 \\ 0.982 & 0.985 & 0.428 & 0.387 \\ 0.363 & 0.428 & 0.985 & 0.985 \\ 0.321 & 0.387 & 0.985 & 0.987 \end{array} \right) $$ となります。元の行列と比較して各要素に似たような値が入っており、上手く近似できていることが分かります。

多変量解析法入門 (ライブラリ新数学大系)

多変量解析法入門 (ライブラリ新数学大系)

【OpenCV】 特徴点検出を行う

OpenCVの復習が必要になったのでメモしておきます。

// グレースケールで画像を読み込む
UMat source1;
imread("lenna.jpg", CV_LOAD_IMAGE_GRAYSCALE).copyTo(source1);
if (source1.empty())
{
    throw runtime_error("Failed to open image");
}
    
// 画像をコピーして回転させる
UMat source2 = source1.clone();
// 回転の中心
Point2f center = Point2f(static_cast<float>(source2.cols / 2),
                         static_cast<float>(source2.rows / 2));
// 回転角度
double angle_deg = 30.0;
// スケーリング係数
double scale = 1.0;
// アフィン変換行列の取得
UMat affine;
getRotationMatrix2D(center, angle_deg, scale).copyTo(affine);
// 内挿方法
int interpolationFlag = INTER_CUBIC;
// 画像の回転を行う
UMat rotated;
warpAffine(source2, rotated, affine, source2.size(), interpolationFlag);
source2 = rotated.clone();

// 特徴点検出器の作成
vector<KeyPoint> keyPoint1, keyPoint2;
Ptr<FeatureDetector> keyPointDetector = AKAZE::create();

// 特徴点を検出する
keyPointDetector->detect(source1, keyPoint1);
keyPointDetector->detect(source2, keyPoint2);

// 特徴量記述子の作成
Mat descriptor1, descriptor2; // ここをUMATで宣言するとextractor->computeでエラーとなった
Ptr<DescriptorExtractor> extractor = AKAZE::create();

// 特徴量の記述
extractor->compute(source1, keyPoint1, descriptor1);
extractor->compute(source2, keyPoint2, descriptor2);

// 特徴量のマッチングを行う(クロスチェック)
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce");
vector<DMatch> forwardMatches;
matcher->match(descriptor1, descriptor2, forwardMatches);
vector<DMatch> backwardMatches;
matcher->match(descriptor2, descriptor1, backwardMatches);
vector<DMatch> matches;
for (int i = 0; i < forwardMatches.size(); ++i)
{
    DMatch forward = forwardMatches[i];
    DMatch backward = backwardMatches[forward.trainIdx];
    if (backward.trainIdx == forward.queryIdx)
    {
        matches.push_back(forward);
    }
}

// 結果を描画する
UMat destination;
drawMatches(source1, keyPoint1, source2, keyPoint2, matches, destination);

imshow("source1", source1);
imshow("source2", source2);
imshow("destination", destination);

f:id:ni4muraano:20170115145041j:plain:w150 f:id:ni4muraano:20170117180130j:plain:w150 f:id:ni4muraano:20170117180136j:plain:w300

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

【OpenCV】 画像から顔を検出する

OpenCVの復習が必要になったのでメモしておきます。

// グレースケールで画像を読み込む
UMat source;
imread("lenna.jpg", CV_LOAD_IMAGE_GRAYSCALE).copyTo(source);
if (source.empty())
{
    throw runtime_error("Failed to open image");
}

// 輝度平滑化を行う
equalizeHist(source, source);

// 顔検出器の作成(xmlはOpenCVをインストールしたフォルダのsources/data/haarcascadesにある)
CascadeClassifier faceDetector("haarcascade_frontalface_alt.xml");
if (faceDetector.empty())
{
    throw runtime_error("Failed to open xml");
}

// 顔を検出する
double scaleFactor = 1.1; // 各スケール毎に画像が縮小される割合
int minNeighbors = 3; // ?
int flag = 0; // 今は使用されていないのでどんな値でも良い
Size minSize = Size(source.cols/4, source.rows/4); // オブジェクトの最小サイズ
Size maxSize = Size(source.cols/1.5, source.rows/1.5); // オブジェクトの最大サイズ
vector<Rect> faces;
faceDetector.detectMultiScale(source, faces, scaleFactor, minNeighbors, flag, minSize, maxSize);

// 検出した顔に目印を付ける
UMat destination = source.clone();
int radius = 8;
Scalar color = Scalar(255, 255, 0);
int thickness = 2;
for (int i = 0; i < faces.size(); ++i)
{
    Point p1 = Point(faces[i].x, faces[i].y);
    Point p2 = Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height);
    rectangle(destination, p1, p2, color, thickness);
}

imshow("source", source);
imshow("destination", destination);

f:id:ni4muraano:20170115145041j:plain f:id:ni4muraano:20170117110946j:plain

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

【OpenCV】 画像のコーナーを検出する

OpenCVの復習が必要になったのでメモしておきます。

// グレースケールで画像を読み込む
UMat source;
imread("lenna.jpg", CV_LOAD_IMAGE_GRAYSCALE).copyTo(source);
if (source.empty())
{
    throw runtime_error("Failed to open image");
}

// 画像のコーナーを検出する
int maxCorners = 30; // 検出するコーナーの最大数
double qualityLevel = 0.01; // コーナーの最低品質
double minDistance = 20.0; // 出力されるコーナー間の最小ユークリッド距離
vector<Point2f> corners;
goodFeaturesToTrack(source, corners, maxCorners, qualityLevel, minDistance);

// 検出したコーナーを書き込む
UMat destination = source.clone();
int radius = 8;
Scalar color = Scalar(255, 255, 0);
int thickness = 2;
for (int i = 0; i < corners.size(); ++i)
{
    circle(destination, corners[i], radius, color, thickness);
}

imshow("source", source);
imshow("destination", destination);

f:id:ni4muraano:20170115145041j:plain f:id:ni4muraano:20170117104535j:plain

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

【OpenCV】 画像を合成する(論理積)

OpenCVの復習が必要になったのでメモしておきます。

// グレースケールで画像を読み込む
UMat source1;
imread("lenna.jpg", CV_LOAD_IMAGE_GRAYSCALE).copyTo(source1);
if (source1.empty())
{
    throw runtime_error("Failed to open image");
}

// 画像を二値化する
UMat source2;
threshold(source1, source2, 0.0, 255.0, THRESH_BINARY | THRESH_OTSU);

// 画像の論理積
UMat destination;
bitwise_and(source1, source2, destination);

imshow("source1", source1);
imshow("source2", source2);
imshow("destination", destination);

f:id:ni4muraano:20170115145041j:plain:w200 & f:id:ni4muraano:20170116091948j:plain:w200 = f:id:ni4muraano:20170116155520j:plain:w200

【OpenCV】 画像を合成する(減算)

OpenCVの復習が必要になったのでメモしておきます。

// グレースケールで画像を読み込む
UMat source1;
imread("something1.jpg", CV_LOAD_IMAGE_GRAYSCALE).copyTo(source1);
if (source1.empty())
{
    throw runtime_error("Failed to open image");
}
UMat source2;
imread("something2.jpg", CV_LOAD_IMAGE_GRAYSCALE).copyTo(source2);
if (source2.empty())
{
    throw runtime_error("Failed to open image");
}

// 画像を減算する
UMat destination;
absdiff(source1, source2, destination);

imshow("source1", source1);
imshow("source2", source2);
imshow("destination", destination);

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング

さらに進化した画像処理ライブラリの定番 OpenCV 3基本プログラミング