Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

歪み補正・台形補正

まっすぐ撮影できずに台形になっているような状態はperspective歪みなどと呼ばれる

射影変換 (projective transformation)によって台形補正を行う

非線形な歪みの補正はできない。例えば書類をスキャンするときに隙間が空いていて、たわんでいる状態でスキャンした文書の補正はできない。

方法

例えば画像内に長方形になるべきものが写っている場合、4点を検出して、まっすぐな歪みのない長方形にアフィン変換してやればよい。

点はkeypoints検出や直線検出の結果をもとに推定する

  • 局所対応点(keypoints)を検出して、4点にまとめる

    • 例えばコーナー検出(corner detection)によって「角っぽい点」を全部検知してから適切にフィルタリング・統合する

  • 線が直交する点が角なので、線を検出して直交点を推測する

    • 例えばハフ変換。ただし、太い線だと並行に検出するとは限らない。候補の線を全部出してしまう。Cannyなどで細線化する方法があるが、細線化が失敗すると後の工程が全部失敗するリスクもある

    • 例えばLSD(Line Segment Detection)

画像の例

Source
<Figure size 640x480 with 1 Axes>

前処理:台形の輪郭線を取り出す

<Figure size 640x480 with 1 Axes>
<Figure size 640x480 with 1 Axes>

前処理:平滑化

<Figure size 640x480 with 1 Axes>

局所対応点を検出する方法

Harrisによるコーナー検出

<Figure size 640x480 with 1 Axes>

FASTによるコーナー検出

FASTは16x16ピクセルの範囲ごとにグレースケール画像の濃淡の領域を分割するように決定木を構築してコーナーを検出する手法

OpenCV: FAST Algorithm for Corner Detection

<Figure size 640x480 with 1 Axes>

検出したコーナーたちを4点にまとめる

array([[101., 99.], [403., 99.], [104., 102.], [400., 102.], [ 55., 498.], [449., 498.], [ 51., 501.], [453., 501.]], dtype=float32)
array([[401.5, 100.5], [451. , 499.5], [ 53. , 499.5], [102.5, 100.5]], dtype=float32)
array([[102.5, 100.5], [401.5, 100.5], [ 53. , 499.5], [451. , 499.5]], dtype=float32)
<Figure size 640x480 with 1 Axes>

Perspective Transform

array([[ 50., 50.], [429., 50.], [ 50., 589.], [429., 589.]], dtype=float32)
<Figure size 640x480 with 1 Axes>

直線を検出する場合

LSDはライセンスの問題でOpenCVからは削除されたようで、使いたい場合は pylsd をインストールする必要がある。

代わりに FastLineDetector というものがある。(Python使用例:Python+OpenCVによる線分検出 - 社会人研究者が色々頑張るブログ)

直線を検出する

ぴったり4つではなく8つくらい出てる

8 lines are detected
<Figure size 640x480 with 1 Axes>

交点を出すために直線を延長する

diff=P1−P2=(x1y1)−(x2y2)direction=diff∥diff∥P1new=(x1y1)−direction×lengthP2new=(x2y2)+direction×length\begin{align} \text{diff} &= P_1 - P_2 \\ &= \begin{pmatrix} x_1 \\ y_1 \end{pmatrix} - \begin{pmatrix} x_2 \\ y_2 \end{pmatrix} \\ \text{direction} &= \frac{\text{diff}}{ \|\text{diff}\| }\\ P_1^{new} &= \begin{pmatrix} x_1 \\ y_1 \end{pmatrix} - \text{direction} \times \text{length}\\ P_2^{new} &= \begin{pmatrix} x_2 \\ y_2 \end{pmatrix} + \text{direction} \times \text{length}\\ \end{align}
<Figure size 640x480 with 1 Axes>

2つの線の交点を出す

4つの点

P1=(x1,y1),P2=(x2,y2),P3=(x3,y3),P4=(x4,y4)P_1 = (x_1, y_1), \quad P_2 = (x_2, y_2), \quad P_3 = (x_3, y_3), \quad P_4 = (x_4, y_4)

からなる2つの線分

L1(t)=(1−t)P1+tP2,(0≤t≤1)L2(u)=(1−u)P3+uP4,(0≤u≤1)L_1(t) = (1 - t) P_1 + t P_2, \quad (0\leq t \leq 1) L_2(u) = (1 - u) P_3 + u P_4, \quad (0\leq u \leq 1)

を用いて、交点(x,y)(x,y)を求める連立方程式を組む

{x=(1−t)x1+tx2x=(1−u)x3+ux4y=(1−t)y1+ty2y=(1−u)y3+uy4\left\{\begin{array}{l} x=(1-t) x_1+t x_2 \\ x=(1-u) x_3+u x_4 \\ y=(1-t) y_1+t y_2 \\ y=(1-u) y_3+u y_4 \end{array}\right.

整理すると

{(x2−x1)t−(x4−x3)u=x3−x1(y2−y1)t−(y4−y3)u=y3−y1\left\{\begin{array}{l} \left(x_2-x_1\right) t-\left(x_4-x_3\right) u=x_3-x_1 \\ \left(y_2-y_1\right) t-\left(y_4-y_3\right) u=y_3-y_1 \end{array}\right.

となる。行列表記にすると以下のようになる。

(x2−x1−(x4−x3)y2−y1−(y4−y3))(tu)=(x3−x1y3−y1)\left(\begin{array}{ll} x_2-x_1 & -\left(x_4-x_3\right) \\ y_2-y_1 & -\left(y_4-y_3\right) \end{array}\right)\binom{t}{u}=\binom{x_3-x_1}{y_3-y_1}

行列式

det⁡=∣x2−x1−(x4−x3)y2−y1−(y4−y3)∣=(x2−x1)(y4−y3)−(y2−y1)(x4−x3)\operatorname{det} =\left|\begin{array}{ll} x_2-x_1 & -\left(x_4-x_3\right) \\ y_2-y_1 & -\left(y_4-y_3\right) \end{array}\right| =\left(x_2-x_1\right)\left(y_4-y_3\right)-\left(y_2-y_1\right)\left(x_4-x_3\right)

がゼロでなければ、クラメルの公式により

t=(x3−x1)(y4−y3)−(y3−y1)(x4−x3)det⁡u=(x2−x1)(y3−y1)−(y2−y1)(x3−x1)det⁡\begin{aligned} & t=\frac{\left(x_3-x_1\right)\left(y_4-y_3\right)-\left(y_3-y_1\right)\left(x_4-x_3\right)}{\operatorname{det}} \\ & u=\frac{\left(x_2-x_1\right)\left(y_3-y_1\right)-\left(y_2-y_1\right)\left(x_3-x_1\right)}{\operatorname{det}} \end{aligned}

として解が求まる

<Figure size 1000x1000 with 1 Axes>

交点の中心点をとる

array([[402.25, 99. ], [ 50.5 , 500.5 ], [452.5 , 500.5 ], [100.75, 98. ]])
array([[100.75, 98. ], [402.25, 99. ], [ 50.5 , 500.5 ], [452.5 , 500.5 ]])
<Figure size 640x480 with 1 Axes>

Perspective Transform

array([[ 50., 50.], [429., 50.], [ 50., 589.], [429., 589.]], dtype=float32)
<Figure size 640x480 with 1 Axes>