LoginSignup
3
9

More than 5 years have passed since last update.

python で画像処理と行列超入門

Last updated at Posted at 2017-11-29

???「Structure from Motion するために2つの画像の特徴点を取って対応点を見つけて〜」

特徴点? 対応点? 何を仰るのだこの御仁は。

同僚に突然こんなふうに話しかけられたときに戸惑わないために、画像処理の超基本を書いておきます。 OpenCV python を使います。

1. 画像データ is 何

画像データは行列(多次元配列)です。

以下の画像(320x180ピクセル)を読み込んで、サイズを表示してみます。OpenCV で読み込んが画像データは numpy array になります。

import cv2

# 画像を読み込んで形を表示
image = cv2.imread("image.png")
print(image.shape)
# -> (180, 320, 3)
# (縦, 横, 色)という3次元配列

# グレースケールに変換
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print(gray_image.shape)
# -> (180, 320)
# グレースケールは1色なので2次元配列になる

2. 行列 is 何

行列はある演算規則の定まった2次元配列です(一般的に多次元になるとテンソルといいます)。数学のさまざまな分野で出てきます。

行列で遊ぼう1: 基本演算

numpy で行列を作って遊んでみます。

import numpy as np

a1 = np.array([
    [1, 2],
    [3, 4]
])

a2 = np.array([
    [5, 6],
    [7, 8]
])

# 行列の足し算は、要素同士の足し算
a1 + a2
# [[ 6  8]
#  [10 12]]

# 行列の引き算は、要素同士の引き算
a1 - a2
# [[-4 -4]
#  [-4 -4]]

# 行列のかけ算は、ちょっと複雑(演算規則は省略)
np.dot(a1, a2)
# [[19 22]
#  [43 50]]

# "1" っぽい役割のもの
# 「単位行列」といいます
e = np.array([
    [1, 0],
    [0, 1]
])
# 任意の行列とかけ算すると…
np.array_equal(
    np.dot(e, a1),
    a1
)
# -> True
np.array_equal(
    np.dot(a1, e),
    a1
)
# -> True
# "1 * x = x * 1 = x" みたいなのが成り立つ!

# わり算は?
# 逆数が定義できればいいよね
# 逆数とは: "x * x^(-1) = x^(-1) * x = 1" が成り立つとき x^(-1) が x の逆数
a1_inverse = np.linalg.inv(a1)
# 行列 a1 とその逆行列の積が単位行列になる!
np.dot(a1_inverse, a1).astype(np.int32) # 見やすさのため整数型に変換
# [[1 0]
#  [0 1]]
np.dot(a1, a1_inverse).astype(np.int32)
# [[1 0]
#  [0 1]]

# わり算(の代わりに逆数をかける)
np.dot(a2, a1_inverse)

行列で遊ぼう2: フィルタによる畳み込み

# 5x5 のグレースケール画像の行列
image = np.array([
    [  0,  0,  0,  0,  0],
    [  0,  0,  0,  0,  0],
    [  0,  0,255,255,  0],
    [  0,  0,255,255,  0],
    [  0,  0,  0,  0,  0],
]).astype(np.float32)

# フィルタによる畳込み

# 何もしないカーネル
id_kernel = np.array([
    [0, 0, 0],
    [0, 1, 0],
    [0, 0, 0],
]).astype(np.float32)
# 元の image と変わらない
cv2.filter2D(image, -1, id_kernel).astype(np.int32)
# [[  0   0   0   0   0]
#  [  0   0   0   0   0]
#  [  0   0 255 255   0]
#  [  0   0 255 255   0]
#  [  0   0   0   0   0]]

# 1ピクセル右にずらすカーネル
right_kernel = np.array([
    [0, 0, 0],
    [1, 0, 0],
    [0, 0, 0],
]).astype(np.float32)
cv2.filter2D(image, -1 ,right_kernel).astype(np.int32)
# [[  0   0   0   0   0]
#  [  0   0   0   0   0]
#  [  0   0   0 255 255]
#  [  0   0   0 255 255]
#  [  0   0   0   0   0]]

# 画像の微分は実はフィルタによる畳み込み
d_x = np.array([
    [-1, 0, 1],
    [-1, 0, 1],
    [-1, 0, 1],
]).astype(np.float32)
d_y = np.array([
    [-1,-1,-1],
    [ 0, 0, 0],
    [ 1, 1, 1]
]).astype(np.float32)
cv2.filter2D(image, -1, d_x).astype(np.int32)
# [[   0    0    0    0    0]
#  [   0  255  255 -255    0]
#  [   0  510  510 -510    0]
#  [   0  510  510 -510    0]
#  [   0  510  510 -510    0]]
cv2.filter2D(image, -1, d_y).astype(np.int32)
# [[   0    0    0    0    0]
#  [   0  255  510  510  510]
#  [   0  255  510  510  510]
#  [   0 -255 -510 -510 -510]
#  [   0    0    0    0    0]]

行列で遊ぼう3: ガウシアンフィルタ

ガウシアンフィルタで画像の「ぼかし」ができます。

## 5x5のグレースケール画像
image = np.array([
    [  0,  0,  0,  0,  0],
    [  0,  0,  0,  0,  0],
    [  0,  0,100,100,  0],
    [  0,  0,100,100,  0],
    [  0,  0,  0,  0,  0],
]).astype(np.float32)

# ガウシアンフィルタ
gaussian_kernel = np.array([
  [1,  2, 1],
  [2,  4, 2],
  [1,  2, 1]
]).astype(np.float32) / 16
gauss_image = cv2.filter2D(image, -1 , gaussian_kernel).astype(np.int32)
print(gauss_image)
# [[ 0  0  0  0  0]
#  [ 0  6 18 18 12]
#  [ 0 18 56 56 37]
#  [ 0 18 56 56 37]
#  [ 0 12 37 37 25]]

特徴点 is 何

  • コーナーなど画像内の特徴的な点
  • 物体追跡や対応点検出で使う

Harris コーナー検出器

特徴点検出のやり方としては、画像の各点ごとに Harris 行列を計算してそれを判別関数に入れてコーナーがあるかどうか判別します。内部のアルゴリズムは省略。

import numpy as np
import cv2

# 5x5 のグレースケール画像
image = np.array([
    [  0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
    [  0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
    [  0,  0,  0,  0,  0,255,255,255,255,255],
    [  0,  0,  0,  0,  0,255,255,255,255,255],
    [  0,  0,  0,  0,  0,255,255,255,255,255],
]).astype(np.float32)

# コーナー検出
dst = cv2.cornerHarris(image,2,3,0.04)
# 二値化処理
threshold = (dst > 0.01 * dst.max()).astype(np.int32)
print(threshold)
# [[0 0 0 0 0 0 0 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]
#  [0 0 0 0 0 1 1 0 0 0]
#  [0 0 0 0 0 1 1 0 0 0]
#  [0 0 0 0 0 0 0 0 0 0]]

特徴量記述子

特徴点だけでは座標しかわからないので、対応点検出のためにはピクセルの周辺情報を含んだ「特徴記述子」が必要となる。

  • ある点の周辺の画像情報を次元を落としたベクトルにする
    • SIFTなら128次元のベクトル
  • 記述子はスケール、回転、明度などに対してロバスト(変更に強い性質)でなければならない
  • ある記述子と別の記述子が「近い」といえるために距離を定義する
  • 特徴量記述子のマッチングを効率よく行うアルゴリズムを作る
    • 近似最近傍探索(FLANN based matcher)など

くたびれたからこれでおしまい。

3
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
9