勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

OpenCVやってみる - 32. 輪郭の変形(考察のみ)

あけましておめでとうございます。
1月も半分以上過ぎてしまいましたが、今年初めての記事です。

今年も引き続き春のパン祭りシール点数集計をやっていきます。
去年の3月から始めて、色々寄り道しながらゆっくり進めてきましたが、 今年の春のパン祭りに間に合うように完成させたいな。

今回の内容

前回テンプレート画像を用意したので、今回は基本的にテンプレートマッチングを実施するだけです。
ただし、台紙を斜めから撮影することによる変形も考慮したいと思います。
以下進めていきます。

輪郭の変形

シール台紙をカメラで撮影するとき、人間がやるのであれば多少なりともカメラの角度がシール台紙の垂直軸からずれます。そうすると撮影された画像では点数文字がいずれかの方向につぶれて写ります。これを補正したいなと。

今回のように平面をカメラで撮影した場合、平面上の点から画像上の点への変換は、厳密には射影変換で表すことができます。ただし、射影変換のパラメータ推定をするのは計算量が多くなるし、そのために準備が必要なマッチング点ペアの数も多くなります。
カメラの撮像面が被写体平面に対してそれほど平行から外れていなければ、アフィン変換での近似で十分なので、これを試してみたいと思います。

一応この方向で考えますが、本当にアフィン変換での変形の補正が必要か、というのも確認しておきます。
そんなに斜めから撮影しないという前提にすれば、必要なさそうな気も…。
ただ、勉強ということで試しにやってみたいなと、そういうモチベーションでやっています。

アフィン変換参考
https://note.nkmk.me/python-opencv-warp-affine-perspective/

今回の変換適用について

テンプレート画像と検出した点数文字輪郭(の周辺画像)を比較する、というのが今回やろうと思っていることです。
テンプレート画像を基準として、これとは違う角度から撮影されて輪郭周辺画像が得られる、という見方になります。

  • この変換のパラメータを求める
  • 輪郭周辺画像に逆変換を施して、本来のカメラ角度からの画像を得る

ということを目指します。

アフィン変換の解釈?

アフィン変換は、2つの平面座標間を、2x2の行列と並進を作用させて変換します。

 \begin{bmatrix} x' \\ y' \\ \end{bmatrix}
= 
\begin{bmatrix} a & b \\ c & d \\ \end{bmatrix}
\begin{bmatrix} x \\ y \\ \end{bmatrix}
+
\begin{bmatrix} \tau_x \\ \tau_y \\ \end{bmatrix}
=
\boldsymbol{M}
\begin{bmatrix} x \\ y \\ \end{bmatrix}
+
\boldsymbol{\tau}

以降、並進\boldsymbol{\tau}は置いておいて、 行列\boldsymbol{M}のほうを考えます。

アフィン変換では、\boldsymbol{M}の要素に特に制約はありません(2次元座標変換には他にユークリッド変換、相似変換がありますが、変換行列に制約がある)が、この要素の意味合いを少し考えてみました。

一応考えましたが、もしかしたら間違いがあるかもなので、そこはご容赦です。あまり信用しないでください。

f:id:nokixa:20220117230423p:plain

まず、この図では、被写体平面に対して斜めの方向からカメラで撮影を行っている様子を表しています。
被写体平面に平行にx軸とy軸、垂直にz軸を定義しています。
ここで、カメラ軸を被写体平面上に射影した軸をx'軸、これに垂直な被写体平面内の軸をy'軸と定義します。
以下の図はz軸に垂直な視点から見た図です。

f:id:nokixa:20220117230426p:plain

x'軸とy'軸を使って被写体の座標を表すことを考えます。
上の図で、x'軸はx軸に対して\varphiの角度(角度をどう取るかはもう少し考えたほうがよさそう…)となっているので、x, yでの座標からx', y'での座標への変換は、

 \begin{bmatrix} x' \\ y' \end{bmatrix} 
=
\begin{bmatrix} \cos(-\varphi) & -\sin(-\varphi) \\ \sin(-\varphi) & \cos(-\varphi) \end{bmatrix} 
\begin{bmatrix} x \\ y \end{bmatrix}
=
\begin{bmatrix} \cos\varphi & \sin\varphi \\ -\sin\varphi & \cos\varphi \end{bmatrix} 
\begin{bmatrix} x \\ y \end{bmatrix} 
=
\boldsymbol{R_\varphi} \begin{bmatrix} x \\ y \end{bmatrix}

となります。

この被写体平面上の点が、カメラの撮像面上でどのように写るか考えてみます。
まずカメラ軸とx'軸を含む面で見てみます。

f:id:nokixa:20220117230429p:plain

カメラの撮像面に対して被写体平面が傾いていて、被写体平面上の点のx'軸座標と撮像面上の座標tex:uはシンプルな関係にはなりませんが、カメラの光学中心と被写体平面間の距離がカメラの奥行方向に大きく変動しなければ、図の緑の面(撮像面に平行)に射影した点で近似することができ、そうすると2つの座標間の関係は

 u = k_x x' \cos \theta = k'_x x'
 (k'_x = k_x \cos \theta)

という形になります。
また、カメラ軸とy'軸を含む面で見ると、こちらはy'軸とカメラ軸が直交するのでシンプルになります。

f:id:nokixa:20220117230431p:plain

被写体平面上のy'座標と撮像面上の座標vの関係は

 v = k_y y'

となります。

これらの関係は、x'軸、y'軸上にない点についても同様で、まとめると、

 \begin{bmatrix} u \\ v \end{bmatrix}
= 
\begin{bmatrix} k'_x & 0 \\ 0 & k_y \end{bmatrix}
\begin{bmatrix} x' \\ y' \end{bmatrix}
=
\boldsymbol{K} \begin{bmatrix} x' \\ y' \end{bmatrix}

となります。

また、カメラのほうも、カメラ軸を中心として回転する自由度があります。
この回転角を\omegaとすると、回転後のu'軸、v'軸座標について、

 \begin{bmatrix} u' \\ v' \end{bmatrix}
=
\begin{bmatrix} \cos\omega & \sin\omega \\ -\sin\omega & \cos\omega \end{bmatrix}
\begin{bmatrix} u \\ v \end{bmatrix}
=
\boldsymbol{R_\omega} \begin{bmatrix} u \\ v \end{bmatrix}

となります。

f:id:nokixa:20220117230434p:plain

今までに出た変換をまとめると、被写体平面上の点(x,y)と撮像面上の点(u',v')の間に以下の関係が成り立ちます。

 \begin{bmatrix} u' \\ v' \end{bmatrix}
=
\boldsymbol{R_\omega} \boldsymbol{K} \boldsymbol{R_\varphi} \begin{bmatrix} x \\ y \end{bmatrix} 
=
\boldsymbol{M}_{\omega,k'_x,k_y,\varphi} \begin{bmatrix} x \\ y \end{bmatrix}

ここで現れた行列 \boldsymbol{M}_{\omega,k'_x,k_y,\varphi} は2x2行列で、4つの任意なパラメータを持ちますが、 これがアフィン変換の行列\boldsymbol{M}に当たるということになります。
これをちゃんと計算すれば、行列\boldsymbol{M}の要素と4つのパラメータ(\omega,k'_x,k_y,\varphi)の関係が出て、かつこれらのパラメータにより行列\boldsymbol{M}の要素を任意に設定できることが示せるかと思います。

ここで一区切り

記事が長くなったので、一旦ここで切ります。
次回からは実際にコードをいじっていこうと思います。