【Python・OpenCV】写真の傷を修復しよう!(cv2.inpaint)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】写真の傷を修復しよう!(cv2.inpaint)

はじめに

写真を撮る際、望んでいないものが写り込んでしまうことがよくあります。カメラのレンズに付いた塵埃、画像のデジタルノイズ、写真に写り込んだ人物の顔を隠したい、などなど。

そんな時、「この写真を修復できたらいいのに」と誰もが思うことでしょう。
写真を修正するのは難しそうで、面倒だしスキルも必要と思ってしまいますよね。

OpenCVのcv2.inpaint関数を使えば、「写真の修復」を簡単に実現できます。
今回は、OpenCVのinpaint関数を使って、写真の傷を魔法のように修復する方法をご紹介します。

cv2.inpaint関数

inpaint関数は、OpenCV 3.0以降のバージョンで利用可能な画像修復機能です。この関数は、指定した領域の欠損部分を、周辺の画像情報から推測して自然に補完(インペインティング)することができます。

例えば、写真に写り込んだ人物の顔を消す場合、単に画像をぼかしたり塗り潰したりすると、その部分が不自然に見えてしまいます。しかしinpaint関数を使えば、自然な補完ができるのです。

cv2.inpaint(入力画像, inpaintMask, inpaintRadius, flags)

(広告) OpenCV関連書籍をAmazonで探す

引数

名称説明
入力画像(必須)入力画像
inpaintMask(必須)マスク画像。欠損部分を白(255)、それ以外を黒(0)で指定します。
inpaintRadius(必須)周辺の画素を参照する半径(int)
flags(必須)補完手法を指定するフラグ
cv2.INPAINT_NS: Navier-Stokesに基づく手法
cv2.INPAINT_TELEA: Alexandru Teleaにより提案されたアルゴリズム

注意

入力画像に関して、いくつか注意すべきことがあります。

  • データ型は uint8 または float32 でなければなりません
    OpenCVの関数では、画像データは通常uint8型の符号なし8ビット整数で表現されます。float32型の32ビット浮動小数点数も受け付けますが、それ以外の型(int, float64など)は使えません。
  • チャンネル数は1または3でなければなりません
    修復可能な画像は、グレースケール(1チャンネル)かカラー(3チャンネル:BGR)のいずれかでなければなりません。アルファチャンネルを含む4チャンネル画像は扱えません。
  • 入力画像とマスク画像のサイズは同じである必要があります

ポイント

補完手法を指定するフラグについて、それそれの手法の特徴は次の通りです。

  • Navier-Stokes方に基づく手法(cv2.INPAINT_NS)は、主に小さな領域の欠損に適しています。
    欠損部分の端から内側に向かって補完が行われるため、大きな領域だと不自然な結果になる可能性があります。
  • Alexandru Teleaにより提案されたアルゴリズム(cv2.INPAINT_TELEA)は、大きな欠損領域でも比較的自然な補完ができます。

戻り値

修復された画像が戻り値となります。

使い方

実際にコードを見ながら、使い方を確認していきましょう。
ここでは、2つの写真について修復をします。

具体例1
下の様な、雲と飛行機の写真を入力画像とします。
飛行機の部分を消してみようとと思います。

マスク画像は下になります。
入力画像から飛行機の部分を白(255)、それ以外の部分を黒(0)とした入力画像と同じサイズの画像です。

後に示すサンプルコードでは、補完手法を指定するフラグとinpaintRadiusの違いについて確認します。
フラグは、cv2.INPAINT_NScv2.INPAINT_TELEAinpaintRadiusは0, 5, 10の3通りに変化させています。

結果は下の様になりました。
どの条件も、概ね飛行機の姿を消すことができていますが、よく見ると飛行機の形が残っている様です。
右下のcv2.INPAINT_NSinpaintRadius=10の場合のみ、飛行機の形が残らず、きれいに消せています。

具体例2
入力画像を次の画像に変えてみました。
雲の様なぼんやりした背景ではなく、木の幹が放射状に配置されているシャープな線が多い背景に太さと色が異なる3つのジグザグの線が描かれているものです。
これらのジグザグの線を消せるか試してみましょう。

背景の画像は下のリンクになります。

引用元
https://pixabay.com/photos/trees-canopy-crown-shyness-forest-4450514/

マスク画像は次になります。

結果は下の様になりました。
一番細い真ん中のジグザグの線は概ね自然に消すことができましたが、線が太くなるにつれて残った痕跡が目立つ様になりました。
また、背景にシャープな線が多い場合、痕跡が残りやすい様です。
inpaintRadiusは修復対象の太さ(大きさ)と背景画像にシャープな線が多い・少ないなどの状態によって決めると良さそうです。
最後に、修復対象の色の影響はないことがわかりました。

サンプルコードを示します。
具体例1と2は5, 6行目で読み込む画像が変えただけです。

import cv2
from matplotlib import pyplot as plt

# 画像の読み込み
image = cv2.imread('image.jpg')
mask = cv2.imread('mask.jpg', cv2.IMREAD_GRAYSCALE)

# 修復を実行
result1 = cv2.inpaint(image, mask, 0, cv2.INPAINT_TELEA)
result2 = cv2.inpaint(image, mask, 5, cv2.INPAINT_TELEA)
result3 = cv2.inpaint(image, mask, 15, cv2.INPAINT_TELEA)
result4 = cv2.inpaint(image, mask, 0, cv2.INPAINT_NS)
result5 = cv2.inpaint(image, mask, 5, cv2.INPAINT_NS)
result6 = cv2.inpaint(image, mask, 15, cv2.INPAINT_NS)

# BGRのチャンネル並びをRGBの並びに変更(matplotlibで結果を表示するため)
rgb_result1 = cv2.cvtColor(result1, cv2.COLOR_BGR2RGB) 
rgb_result2 = cv2.cvtColor(result2, cv2.COLOR_BGR2RGB) 
rgb_result3 = cv2.cvtColor(result3, cv2.COLOR_BGR2RGB) 

rgb_result4 = cv2.cvtColor(result4, cv2.COLOR_BGR2RGB) 
rgb_result5 = cv2.cvtColor(result5, cv2.COLOR_BGR2RGB) 
rgb_result6 = cv2.cvtColor(result6, cv2.COLOR_BGR2RGB) 

# 結果の可視化
plt.rcParams["figure.figsize"] = [16,8]                             # 表示領域のアスペクト比を設定
title = "cv2.inpaint: codevace.com"
plt.figure(title)                                                   # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95)   # 余白を設定

plt.subplot(231)                                                    # 2行3列の1番目の領域にプロットを設定
plt.imshow(rgb_result1)                                             # 修復後の画像を表示
plt.title("cv2.INPAINT_TELEA, radius=0")                            # 画像タイトル設定
plt.axis("off")                                                     # 軸目盛、軸ラベルを消す

plt.subplot(232)                                                    # 2行3列の2番目の領域にプロットを設定
plt.imshow(rgb_result2)                                             # 修復後の画像を表示
plt.title("cv2.INPAINT_TELEA, radius=5")                            # 画像タイトル設定
plt.axis("off")                                                     # 軸目盛、軸ラベルを消す

plt.subplot(233)                                                    # 2行3列の3番目の領域にプロットを設定
plt.imshow(rgb_result3)                                             # 修復後の画像を表示
plt.title("cv2.INPAINT_TELEA, radius=15")                           # 画像タイトル設定
plt.axis("off")                                                     # 軸目盛、軸ラベルを消す

plt.subplot(234)                                                    # 2行3列の4番目の領域にプロットを設定
plt.imshow(rgb_result4)                                             # 修復後の画像を表示
plt.title("cv2.INPAINT_NS, radius=0")                               # 画像タイトル設定
plt.axis("off")                                                     # 軸目盛、軸ラベルを消す

plt.subplot(235)                                                    # 2行3列の5番目の領域にプロットを設定
plt.imshow(rgb_result5)                                             # 修復後の画像を表示
plt.title("cv2.INPAINT_NS, radius=5")                               # 画像タイトル設定
plt.axis("off")                                                     # 軸目盛、軸ラベルを消す

plt.subplot(236)                                                    # 2行3列の6番目の領域にプロットを設定
plt.imshow(rgb_result6)                                             # 修復後の画像を表示
plt.title("cv2.INPAINT_NS, radius=15")                              # 画像タイトル設定
plt.axis("off")                                                     # 軸目盛、軸ラベルを消す
plt.show()                                                          # ウィンドウを表示する
(広告) OpenCV関連書籍をAmazonで探す

おわりに

OpenCVのcv2.inpaint関数を使って写真の傷を修復する方法を解説しました。
数行のコードで、高度な画像修復処理を実現できることが確認できました。

こちらの記事もオススメです。

ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。

参考リンク

(広告)あなたに合ったプログラミング スクールが見つかるかも

プログラミングの学習が、新しい世界を開きます。副業からキャリアの転換まで、目標に向かって一歩を踏み出しましょう。

これらのプログラミング スクールは、あなたの目的に合わせて選択できるさまざまなプログラムを提供しています。どのスクールを選ぶにせよ、プログラミングは未来への扉を開き、新しい機会を切り開く手段として素晴らしい選択です。あなたの夢や目標を実現する第一歩を踏み出す準備はできていますか?

-Python, プログラミング, 画像処理
-