Python で画像ファイルの操作を行う方法を解説!【OpenCV編】

OpenCV 時短入門

よく画像処理で OpenCV というのは聞くけれど、何ができるのか?がわからない方も「今から時短入門」!

こんにちは!新井です。私の 2 本目のブログとなります!
デビュー作は以下です!

突然の宣伝でした、さっそく本題です。

画像データを加工しよう OpenCV 編

今回は、画像データを扱うために多くの方が使用するであろう、「OpenCV」 についてまとめました。

誰のための記事?
  • 画像処理に入門する人
  • OpenCV を使用する人

この記事のゴール

  • OpenCV の使用イメージが分かる
  • 顔検出も実装できるようになる

実行環境

今回は初学者もサクッと入門できるように、Google Colaboratory だけで解説を行います。

ローカルでも同様の処理ができますので、「こんな使い方ができるんだ!」を体感していただけると幸いです。

  • Google Colaborartory
  • cv2==4.7.0(OpenCV のバージョン)

OpenCV とは

OpenCV は、画像処理やコンピュータビジョンの領域で使用されるライブラリです。

コンピュータビジョンとは、カメラやセンサー情報を画像処理として扱う分野です。

画像処理検定というものがあるので、気になる方はチェックしてみてください。ディジタル画像処理という書籍の内容が鍵となると思います。

そもそも、Pillow と OpenCV の違いって何?

Pillow も聞いたことはあるけど、どんなものだっけ?

Pillow については以下で詳しく解説しているので、こちらもご覧ください!

機械学習で「画像処理」を学ぶための手前の段階で出会うと思われる 2 つのライブラリですが、ここでどのような違いがあるのかを把握しておきましょう。

大きな違いとして、画像情報は RGB と記憶する方が多いと思いますが、Pillow は RGBOpenCV は BGR と、チャンネルの順序が異なります。実装時に注意したい点です。

PillowOpenCV
特徴画像の基本的な処理に向いている画像の基本的な処理から複雑な変換などに向いている。
分類も可能。
難易度簡単中から難しいレベルまである
メリット初心者向け、画像のリサイズや保存が簡単画像の変換が Pillow よりも豊富、特徴量抽出可能、機械学習に匹敵する機能あり
デメリット画像に対して高度な変換できない、特徴量を抽出できない難易度が高い画像変換を行う場合、一筋縄ではいかない場面が多い
比較表

OpenCV で画像変換を体験しよう

今回は Google Colaboratory を使用するということで、変換した画像は matplotlib で表示させ、変換後の画像を確認する方針ですすめます。

レベル 0 : png → jpg 変換

Pillow 編では拡張子「png」を使用しました。今回はオリジナル画像を使用するために拡張子「jpg」を使用します。

ここでは「png」から「jpg」の変換コードを紹介します。なお、解説は割愛します。

拡張子 png を jpg に変換するコード
import cv2
import matplotlib.pyplot as plt

# png 画像の読み込み
sample_img_png = cv2.imread('sample.png')

# 画像の拡張子を変更 png → jpg
cv2.imwrite('sample_img_jpg.jpg', sample_img_png)

# 画像の表示→ matplotlib では cv2.COLOR_BGR2RGB の設定が必要
sample_img_jpg = cv2.imread('sample_img_jpg.jpg')
plt.imshow(cv2.cvtColor(sample_img_jpg, cv2.COLOR_BGR2RGB))
plt.show()

レベル 0 : 画像読み込みと表示

画像を読み込み、Google Colaboratory または NotoBook で表示する
import cv2
import matplotlib.pyplot as plt

# 画像の読み込み
img = cv2.imread('chaco.jpg')

# 画像の確認
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

私のオリジナル画像で動作確認をしたキャプチャです。チャコ氏といううさぎなのですが、この画像は表示されているメモリからすると、高さ約 4000 、幅約 3000 の画像です。食べているのは草です。

画像の準備、画像の表示ができれば Google Colaboratory を用意すると、ここから先、 OpenCV の体験ができます!

早速やっていきましょう!


レベル 1 : リサイズ 1「 指定した数値にリサイズ」

すべての画像を同じ大きさにリサイズする場合、簡単にリサイズできる方法です。

1000 × 1000 にリサイズするコード
# モジュールの読み込み
import cv2
import matplotlib.pyplot as plt

# 画像の読み込み
img = cv2.imread('chaco.jpg')

# 画像のサイズを 1000 ✕ 1000 に変換
resize_img_1 = cv2.resize(img, dsize=(1000, 1000))

# 画像の確認
plt.imshow(cv2.cvtColor(resize_img_1, cv2.COLOR_BGR2RGB))
plt.show()

少々顔がつぶれたチャコ氏になりました。

画像によっては、このように少々変な画像変換となることもあります。


レベル 1 : リサイズ 2 「倍率でリサイズ」

画像の読み込みを省略したコードです。

倍率でリサイズするコード
# 画像のサイズを縦横 1/10 のサイズに変換
img = cv2.resize(img, dsize=None, fx=0.1 , fy=0.1)

# 画像の確認
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

こちらはリサイズ 1 とは異なり、高さと幅の比率を維持しているので、違和感のないリサイズができます。


レベル 1 : 回転

今回は 90 度、時計回りへ画像を回転させます。

回転 時計回りに 90 度回転させるコード
# 回転
img_rotate = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)

# 画像の確認
plt.imshow(cv2.cvtColor(img_rotate, cv2.COLOR_BGR2RGB))
plt.show()

回転しても可愛いチャコ氏です。

単に 1 枚の画像を回転させるだけならば、PC の画像ソフトなどで変換したほうが早いかもしれません。

しかし、大量の画像を回転させたい場合はコード実行すると作業が楽になります。

また、機械学習で画像データを学習させる際、回転させた画像を学習に使用することもできます(例外あり)。工場部品のネジを思い浮かべていただくと、見る方向によってネジの向きが変わると思います。かといって、色んな角度の画像を生成して学習させるか?というとそうではないので、データが少なくて学習データが不足している際に使える一つの策だと思ってください。


レベル 1 : グレースケール変換 *

機械学習での画像変換で使用率が高いと思われる処理です。

変換は簡潔なコードですが、matplotlib の仕様上、表示するコードは独特です。

グレースケール変換
# グレースケール変換
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 画像の確認
plt.imshow(img_gray, cmap='gray')
plt.show()

しっかりモノクロ画像になりました。


レベル 1 : 2 枚の画像を合成

Pillow 編で紹介しているので、OpenCV でも紹介です。

2 枚の画像を合成するコード
# キカガクのロゴの読み込み(png でも合成できます)
kikagaku_icon = cv2.imread('icon.png')

# キカガクのロゴをチャコ氏の画像サイズに合わせる
# kikagaku_icon.shape でサイズを取得できる→(302, 403)でした
kihkagaku_icon =cv2.resize(kikagaku_icon, (302, 403))

# 画像を合成(引数の詳細は公式ドキュメントを参考に変更してください)
blended = cv2.addWeighted(src1=img, alpha=0.7, src2=kihkagaku_icon, beta=0.3, gamma=0)

# 画像の確認
plt.imshow(cv2.cvtColor(blended, cv2.COLOR_BGR2RGB))
plt.show()

画像を合成することができました。


レベル 2 : 上下反転

画像の上下を反転させたい場面で使用できます。機械学習で画像処理を行う際にデータ水増しの一つの手段として使用できるケースがあります。

画像を上下逆転させるコード
img_flip_ud = cv2.flip(img, 0)

# 画像の確認
plt.imshow(cv2.cvtColor(img_flip_ud, cv2.COLOR_BGR2RGB))
plt.show()

チャコ氏の場合、上下反転させても「うさぎ」であることには変わりません。たまたま仰向けに寝ていて、上下反転していることもあるかもしれません。

「信号機」の場合はどうでしょうか?北海道など積雪対策された縦信号は例外として、信号機が上下反転することはないかと思います。この変換を使用する場合、逆転させて問題ないか?は確認しておきたいポイントです。


レベル 2 : 左右を入れ替える

こちらも上下反転と同様に、左右逆転したい画像への変換、データの水増しに使用できる変換です。

画像を左右反転させるコード
# 左右反転
sample_img_jpg = cv2.imread('sample_img_jpg.jpg')
img_flip_lr = cv2.flip(sample_img_jpg, 1)

# 画像の確認
plt.imshow(cv2.cvtColor(img_flip_lr, cv2.COLOR_BGR2RGB))
plt.show()

まるで鏡のように左右が反転しているのが、「草の向き」からわかります。

このケース、データの水増しをする際に注意が必要です。

私はチャコ氏に詳しく、みなさんよりチャコ氏のドメイン知識があります。左右逆転すると、正確にはチャコ氏ではなくなってしまいます。しかし、「うさぎ」であることには変わらないと思います。この様に左右逆転するとよくないケースもあるので抑えておくポイントではあります。

余談ですが、最近では「あえて」左右逆転して撮影するカメラアプリもあるようです。芸能人の Instagram などで、もしかしたらみなさん、左右逆転した画像を見ているかもしれません。


レベル 2 : 画像の二値化

二値化とは、画像を白と黒に変換することなのですが、次のコードでは、閾値を設定して白と黒に変換しています。今回は125 を閾値としていますが、お手元でこの閾値を調節してみてください。ただのグレースケール変換では見つからないような特徴が見つかるかもしれません。

画像を二値化させるコード
# 閾値の設定
threshold = 125

# 二値化(閾値 125 を超えた画素を255にする。)
ret, img_thresh = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)

# 画像の確認
plt.imshow(cv2.cvtColor(img_thresh, cv2.COLOR_BGR2GRAY), cmap='gray')
plt.show()

レベル 2 : エッジ検出

次は使用する画像をテレビのリモコンにしてみました。画像のエッジ検出では、画像の中の物体のエッジ(特徴)を検出することを言います。これは実際に画像を見てどのような検出がされるのか?を見るとわかりやすいと思います。

輪郭やエッジを検出して物体検出や物体認識の前処理で利用されます。

このリモコンの画像からエッジを検出します。どこが特徴として検出されるのでしょうか?

エッジ検出するコード
import numpy as np

# 画像の読み込み
img_f = cv2.imread('remote_control.jpg')

# グレースケール画像を用意
img_gray = cv2.cvtColor(img_f, cv2.COLOR_BGR2GRAY)

# Sobel フィルタを適用してエッジを検出
sobel_x = cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize=3)

# sobel_x と sobel_y を絶対値に変換してから合成する
sobel_combined = cv2.addWeighted(np.absolute(sobel_x), 0.5, np.absolute(sobel_y), 0.5, 0)

# 画像の確認
plt.imshow(sobel_combined, cmap='gray')
plt.show()

検出結果です。リモコンの「ボタン」の部分が白く見える箇所があるかと思います。このリモコンの画像では、「ボタン」の部分に特徴があると認識されているようです。機械学習を使用しなくても OpenCV だけでここまでエッジを検出することもできます。


レベル 2 : ハリスのコーナー検出(おまけ)

エッジ検出で、画像内の物体から特徴を検出できることがわかりました。OpenCV には他にも、特徴を検出する手法があります。その一部を紹介します。なお、おまけコードなので、解説は割愛します。

今回もエッジ検出と同じ、リモコンの画像から特徴を検出します。

ハリスのコーナー検出を実装したコード
import numpy as np

# 画像の読み込み
img_f = cv2.imread('remote_control.jpg')

# グレースケール画像を用意
img_gray = cv2.cvtColor(img_f, cv2.COLOR_BGR2GRAY)

gray = np.float32(img_gray)
img_dst = np.copy(gray)


dst = cv2.cornerHarris(gray, 2, 3, 0.05, img_dst)
dst = cv2.dilate(dst,None,iterations = 3) 

img_f[dst>0.05*dst.max()]=[0, 0, 255]

# 画像の確認
plt.imshow(cv2.cvtColor(img_f, cv2.COLOR_BGR2RGB))
plt.show()

ハリスのコーナー検出で得られた特徴です。画像内の赤く示されている部分が今回特徴として検出された部分です。

こちらも「ボタン」を特徴として捉えていることがわかります。


レベル 2 : 特徴量抽出

2 枚の画像間で特徴量が一致している部分を確認することができます。

特徴量検出するコード
# 2つの画像を読み込む
img1 = cv2.imread('an_old.jpg')
img2 = cv2.imread('an.jpg')

# 特徴点検出
akaze = cv2.AKAZE_create()
kp1, des1 = akaze.detectAndCompute(img1, None)
kp2, des2 = akaze.detectAndCompute(img2, None)

# マッチング
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)

# 特徴点間のハミング距離でソート
matches = sorted(matches, key=lambda x: x.distance)

# 2 つの画像のマッチング結果を作成、最も似ている 5 箇所を表示する
img1_2 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:5], None)


# 画像を確認
plt.imshow(cv2.cvtColor(img1_2, cv2.COLOR_BGR2RGB))
plt.title('Feature Matching Result')
plt.axis('off')
plt.show()

同じ人物で特徴量を検出した結果です。画像に表示されている直線は特徴が一致していることを表しています。5 本あるのですが、コード上で最もよく似た部分を 5 箇所だけ検出するようにしています。

どうやって似た特徴を抽出しているのかというと、2 つの画像の人物の「距離」で似ているかどうか(マッチングしているか)を検出しています。よく「〇〇に似ている人」のような会話を聞いたりする方もいると思いますが、これを使えば、どのくらい似ているか?の数値化まで行うことも可能です。


レベル 2 : 輪郭抽出

文字通り、画像内の物体の輪郭を抽出します。今回はわかりやすい例として、キカガクのロゴを使用します。

こちらがキカガクのロゴです。人間の目から見ても、なんとなく輪郭を見つけられそうですね。

輪郭抽出するコード
# キカガクのロゴを読み込み
kikagaku = cv2.imread('logo.jpg')

# グレースケール変換
img_gray = cv2.cvtColor(kikagaku, cv2.COLOR_BGR2GRAY)

# 輪郭を抽出するための設定
ret,thresh = cv2.threshold(img_gray,150,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt_img = np.zeros_like(thresh, dtype=np.uint8)
cnt_img = cv2.drawContours(cnt_img, contours, -1, 255, 2)

# 画像の確認
plt.imshow(cnt_img,cmap='gray')
plt.show()

コード内に「輪郭を抽出するための設定」という部分がありますが、ここで画像のどの部分を基準に輪郭を抽出するかを指定しています。ここは適宜値を変更してみてください。

今回は以下のように輪郭抽出されました。ロゴの一部が抽出されていない結果となりましたが、「文字」の部分まで輪郭が抽出されていることがわかります。


レベル 3

* 顔検出 *

実は OpenCV で画像から顔を検出することができます。これを使用することで、機械学習を使用しなくても OpenCV とルールベースで画像分類などを実現することもできます。実は OpenCV は他にも色々な機能を持ち合わせているので、画像処理を今後行う方は OpenCV を使用する機会が増えると思います。困ったときに OpenCV で解決できないかな?を検討するのはかなり有効的だと個人的に思います。

どのように顔の検出ができるかを見ていきましょう。

今回、Google Colaboratory で顔検出を実現するには少々手順が増えます。

顔検出する際に OpenCV 提供しているファイルをダウンロードして、Google Colaboratory で読み込む必要があります。

まず、OpenCV の GitHub ページより data > haarcascades haarcascade_frontalface_default.xml をダウンロードします。これは顔を検出するための「分類情報」を持っているファイルです。こちらをダウンロードし、Google Colaboratory へアップロードしてから以下のコードを実行すると、顔検出することができます。

顔検出するコード
# 顔検出したい画像の読み込み
kikagaku_member = cv2.imread('/content/drive/MyDrive/キカガクブログ/2022_kikagaku.jpg')

# RGB 変換
img = cv2.cvtColor(kikagaku_member, cv2.COLOR_BGR2RGB)

# 事前にアップロードしたファイル読み込み
cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')

# 顔を検出
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=3, minSize=(30, 30))

# 検出した領域を矩形で囲む
for (x, y, w, h) in face:
  cv2.rectangle(img, (x, y), (x + w, y + h), (200,0,0), 3)

# 画像の確認
plt.imshow(img)

今回は、キカガクメンバーの写真を使用しました。

こちらを見ていただくとかなりの精度で顔の検出ができていることがわかるかと思います。

素敵な笑顔が検出されました (*^^*)


まとめ

OpenCV でできること、イメージが湧きましたでしょうか?また、Pillow と異なる点について、この記事で少しでも理解が深まったら嬉しい限りです。

今回紹介したコードは OpenCV の一部の機能のみとなります。この記事で紹介した以外にも便利な機能がありますので、ぜひ調べて使ってみてください。

詳細は、公式ドキュメントや参考書などで学ぶのがおすすめです。

公式ドキュメントはやや癖がある(個人的感想)ので、一緒にトライしていきましょう!!

さいごに

OpenCV は「画像変換」だけではなく、機械学習の前処理にも使用できます。

そして、ルールベースと合わせることで機械学習と同じような処理を実現することが可能です。

今後、画像処理分野に入門する方はぜひ、 OpenCV をチェックしていきましょう。

こちらの記事もオススメ

まずは無料で学びたい方・最速で学びたい方へ

まずは無料で学びたい方: Python&機械学習入門コースがおすすめ

Python&機械学習入門コース

AI・機械学習を学び始めるならまずはここから!経産省の Web サイトでも紹介されているわかりやすいと評判の Python&機械学習入門コースが無料で受けられます!
さらにステップアップした脱ブラックボックスコースや、IT パスポートをはじめとした資格取得を目指すコースもなんと無料です!

無料で学ぶ

最速で学びたい方:キカガクの長期コースがおすすめ

一生学び放題

続々と転職・キャリアアップに成功中!受講生ファーストのサポートが人気のポイントです!

AI・機械学習・データサイエンスといえばキカガク!
非常に需要が高まっている最先端スキルを「今のうちに」習得しませんか?

無料説明会を週 2 開催しています。毎月受講生の定員がございますので確認はお早めに!

説明会ではこんなことをお話します!
  • 国も企業も育成に力を入れている先端 IT 人材とは
  • キカガクの研修実績
  • 長期コースでの学び方、できるようになること
  • 料金・給付金について
  • 質疑応答