この記事は、画像処理エンジニア検定を受験するにあたり、OpenCVを実際に利用して画像処理について学ぶことを目的としています。勉強した内容をメモします。
※画像処理でできることをメインに書いていくので、Pythonや使用する各関数の説明などは省略しています。
使用環境
- windows10
- Anaconda: 1.7.2
- python: 3.7.9
- Jupyter Notebook
前提
画像処理とは
-
画像はパターンを表す数値(画素値)の集まり
-
0から255で表現する
RGBの3色から成る。
例
(10, 20, 30)
※順番がR(Red)G(Green)B(Blue)ではないことに注意
B | G | R |
---|---|---|
10 | 20 | 30 |
- 画像を解析しやすくする、情報を抽出するための処理
OpenCVとは
-
オープンソースの画像処理ライブラリ
-
マルチプラットフォーム:windows, mac, linux
-
多言語対応:C++, Pyhton, Java...
-
画像処理以外にも機械学習などのコンテンツも含む
目次
-
環境構築
-
画像の表示・出力
-
ウィンドウの調整
-
色変換
-
γ(ガンマ)変換で画像を明るくする、暗くする
-
表示した画像に図形を描写する
-
画像の二値化
-
透視変換
-
畳み込み
環境構築
AnacondaのインストールとOpenCVのインストール
Anacondaはすでにインストール済みだったので、上記のブログを参考にOpenCVをインストールしました。
画像の表示・出力
import cv2
#画像を保存するためにimport
import os
img = cv2.imread("data/src/score.jpg")
#画像の表示
cv2.imshow("img",img)
#何かしらのキーが入力されるとウィンドウが閉じられる
cv2.waitKey(0)
cv2.destroyAllWindows()
#outputディレクトリを作成
os.mkdir("./output")
#読み込んだimgをtest.jpgとして保存
cv2.imwrite("output/test.jpg", img)
出力された画像は以下。左上のtitleが「img」となっている。
ウィンドウの調整
前項の画像の表示で表示させたウィンドウは固定サイズでした。
以下のようにコードを書くと、表示されるウィンドウのサイズを調整することができます。
import cv2
img = cv2.imread("data/src/favorite.jpg")
#第二引数にcv2.WINDOW_NORMALを指定するとウィンドウの調整ができるようになる
cv2.namedWindow("window", cv2.WINDOW_NORMAL)
cv2.imshow("window", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
また、表示させる画像のリサイズを行う方法もあります。
import cv2
img = cv2.imread("data/src/score.jpg")
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img.shape #出力(3672, 4896, 3)
#高さ、幅
size = (300, 200)
img_resize = cv2.resize(img,size)
img_resize.shape #出力(200, 300, 3)
cv2.imshow("resize",img_resize)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記とは別の方法で画像をリサイズする方法もあります。
img_area = cv2.resize(img, size, interpolation = cv2.INTER_AREA)
img_linear = cv2.resize(img, size, interpolation = cv2.INTER_LINEAR)
cv2.imshow("area", img_area)
cv2.imshow("linear", img_linear)
cv2.waitKey(0)
cv2.destroyAllWindows()
2つの画像を比べると、titleが「linear」となっている画像の方がギザギザ?になっていることが分かります。
色変換
カラー画像を白黒画像にする方法を以下のコードで書きます。
import cv2
img = cv2.imread("data/src/favorite.jpg")
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray.shape #出力は解像度の情報だけ残る -> (200, 300)
cv2.imshow("img", img)
cv2.imshow("gray", img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記コードを実行した結果、以下のように画像が表示されました。
また、以下の方法で画像を白黒にすることもできます。
import cv2
# imread()の第二引数に0を設定することで白黒画像として読み込まれる。
img_gray2 = cv2.imread("data/src/favorite.jpg", 0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img_gray2 = cv2.resize(img_gray2,size)
#白黒画像になっているか確認するコード
cv2.imshow("gray", img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
γ(ガンマ)変換で画像を明るくする、暗くする
こちらの記事にγ変換についてまとまっているので、勉強させていただきました。
実際にγ変換を用いて画像の明るさを変換するコードを実行しました。
import cv2
import numpy as np
#元画像より明るい画像を表示させます
gamma = 2.0
img = cv2.imread("data/src/favorite.jpg")
#元画像が大きいためリサイズしています
size = (300, 200)
img = cv2.resize(img,size)
gamma_cvt = np.zeros((256,1), dtype = np.uint8)
for i in range(256):
gamma_cvt[i][0] = 255 * (float(i) / 255) ** (1.0 / gamma)
img_gamma = cv2.LUT(img, gamma_cvt)
cv2.imshow("img",img)
cv2.imshow("gamma",img_gamma)
cv2.waitKey(0)
cv2.destroyAllWindows()
実行結果は以下のようになりました。画像が明るくなっているのが分かると思います。
また、変数gammaを1より小さくすることで、元画像より暗い画像へ変換することもできます。
import cv2
import numpy as np
#元画像より暗い画像を表示させます
gamma = 0.5
#元画像が大きいためリサイズしています
img = cv2.imread("data/src/favorite.jpg")
size = (300, 200)
img = cv2.resize(img,size)
gamma_cvt = np.zeros((256,1), dtype = np.uint8)
for i in range(256):
gamma_cvt[i][0] = 255 * (float(i) / 255) ** (1.0 / gamma)
img_gamma = cv2.LUT(img, gamma_cvt)
cv2.imshow("img",img)
cv2.imshow("gamma",img_gamma)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコードを実行した結果がこちらです。元画像よりも暗い画像に変換されていることが分かります。
表示した画像に図形を描写する
読み込んだ画像に対して、図形などを表示させることができます。
以下にサンプルを作成しました。座標軸などは任意の値を設定しています。
import cv2
import numpy as np
#白い画像を生成
img = np.ones((1000, 1000, 3)) * 255
#青い直線を描写
cv2.line(img, (0, 0), (150,300),(255, 0, 0,), 2)
#緑四角形を描写
cv2.rectangle(img, (100, 100), (200, 200),(0, 255, 0), 4)
#赤い円を描写
cv2.circle(img, (250,100), 50, (0, 0, 255), -1)
#青い楕円を描写
cv2.ellipse(img,(250, 250), (100, 50), 20, 0, 360, (255, 0, 0), 1)
#水色の多角形(今回は三角形)を描写
pts = np.array([[100, 30], [200, 30], [200, 80], [100, 30]])
cv2.polylines(img, [pts], False,(100, 255, 0), 3)
#テキストを描写
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, "text!!!", (400, 400), font, 1, (0, 255, 0),3, cv2.LINE_AA)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記コードを実行した結果がこちらです。白い画像にそれぞれ図形が描写されていることが分かります。
画像の二値化
画像の二値化とは、画像を白と黒の2色に変換する処理のことで、ある値(閾値)以上の画素値を白、ある値未満の画素値を黒に変換することを指します。
以下のコードで画像の二値化をしてみます。
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
img = cv2.imread("data/src/favorite.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (300, 200)
img = cv2.resize(img,size)
#閾値を50に設定
threshold = 50
#retは閾値が返ってくる
ret, img_th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)
cv2.imshow("img_th", img_th)
cv2.waitKey(0)
cv2.destroyAllWindows()
また、以下のように適応に閾値を設定して、二値化することも可能です。
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
img = cv2.imread("data/src/favorite.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (300, 200)
img = cv2.resize(img,size)
img_ada = cv2.adaptiveThreshold(img, 255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, 1)
cv2.imshow("img_ada",img_ada)
cv2.waitKey(0)
cv2.destroyAllWindows()
また、トラックバーを使用して、閾値を変化させることも可能です。
import cv2
img = cv2.imread("data/src/favorite.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (300, 200)
img = cv2.resize(img,size)
#トラックバーを用いて、二値化の閾値を変化させる
def onTrackbar(position):
global threshold
threshold = position
cv2.namedWindow("img")
threshold = 100
cv2.createTrackbar("track","img",threshold, 255, onTrackbar)
while True:
ret, img_th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)
cv2.imshow("img",img_th)
#Escapeキーでウィンドウを閉じます
if cv2.waitKey(10) == 27:
break
cv2.destroyAllWindows()
上記コードでは、閾値を0~255まで変化させることができます。
以下の画像は閾値を0, 77, 255に変化させたときの画像です。
透視変換
透視変換とは、任意の視点から3次元空間を眺めて遠近感を出す変換です。
以下のサンプルで透視変換がどういう変換か確認します。
import cv2
import numpy as np
img = cv2.imread("data/src/score.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (500, 500)
img = cv2.resize(img,size)
h, w = img.shape[:2]
p1 = np.float32([[100, 500], [300, 500], [300, 100],[100,100]])
p2 = np.float32([[100, 500], [300, 500], [200, 200],[100,200]])
p_matrix = cv2.getPerspectiveTransform(p1, p2)
img_p = cv2.warpPerspective(img, p_matrix, (w, h))
cv2.imshow("img",img_p)
cv2.waitKey(0)
cv2.destroyAllWindows()
実行結果は以下のような画像です。
畳み込み
以下のページに畳み込みについてまとまっているので、こちらで勉強させていただきました。
フィルターを用いて、画像の平滑化(画素値の変化を滑らかにすること)を行いました。
以下がそのコードです。
import cv2
import numpy as np
kernel = np.ones((5,5)) / 25.0
img = cv2.imread("data/src/score.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (500, 500)
img = cv2.resize(img,size)
img_2 = cv2.filter2D(img, -1, kernel)
cv2.imshow("img_2",img_2)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記を実行すると次のような画像が表示されます。「img」が元画像です。
楽譜の周りや音符などがぼやけて見えると思います。
また、画像の平滑化にはGaussianBlur
を用いた方法もあります。
import cv2
img = cv2.imread("data/src/score.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (500, 500)
img = cv2.resize(img,size)
img_gaussian = cv2.GaussianBlur(img,(9,9),2)
cv2.imshow("img_gaussian",img_gaussian)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記コードの実行結果は以下です。
こちらの画像も平滑化されています。
medianBlur
を用いた場合のコードと実行結果も以下に書きます。
import cv2
img = cv2.imread("data/src/score.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (500, 500)
img = cv2.resize(img,size)
#第二引数の値が大きいほど画像がぼやけます
img_median = cv2.medianBlur(img, 5)
cv2.imshow("img_median",img_median)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記の実行結果は以下のようになります。他の方法よりもぼやけた結果となりました。
bilateralFilter
を用いた方法も書きます。
import cv2
img = cv2.imread("data/src/score.jpg", 0)
#画像サイズが大きいためリサイズしています
size = (500, 500)
img = cv2.resize(img,size)
img_bi = cv2.bilateralFilter(img, 20, 30, 30)
cv2.imshow("img_bi",img_bi)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最後に
まだまだ画像処理を理解するには道のりが長そうです。
引き続き勉強します。
間違いなどありましたら、ご指摘いただけると幸いです。