年末にOpenCVを色々いじってみた
1.OpenCVってなに?
OpenCVとは、画像の読み込みから表示、加工までを行えるオープンソースライブラリとなっています。
・画像のエッジ抽出
・画像のグレースケール
・二値化
・物体検出
そのほか様々なことができます
今回は、画像の読み込みから様々なものをこの年末にやったので紹介していきます。
2.画像の表示
まぁこれをやらないとこれから何するんだって話ですからね笑
ここからやっていきましょう!
2.1事前準備(インストール)
!pip install opencv-python
import cv2
まずは実行環境上でOpencvを使えるようにしていきましょう。
importする際はcv2なのでご注意を
2.2画像の書き込み
# 画像を保存する場所をカレントディレクトリに作る
os.mkdir("./output")
# 画像の書き込み
cv2.imwrite("/●●/●●/Desktop/画像認識/output/test.jpg" , cacao)
ここでは、保存先のディレクトリを作成し、
カカオという犬の画像を使っていきたいと思います
3.映像の読み込み表示・書き込み
cap = cv2.VideoCapture("/Users/uchiiyusaku/Desktop/画像認識/cacao_sleep.mp4")
# cap.isOpened() 正常に読み込むことができればTrueが帰ってくる
if cap.isOpened() == False:
sys.exit()
ret , frame = cap.read() # 1フレームだけ読み込む
# ret:審議値 flame:1フレーム読み込んだ画像が存在する
h , w = frame.shape[:2]
# 書き込みの設定 引数:コーデックを指定
fourcc = cv2.VideoWriter_fourcc(*"XVID")
# 30.0 : fpsの指定 # 動画の解像度の指定 (w,h)
dst = cv2.VideoWriter("output/test.avi" , fourcc , 30.0 , (w,h))
while True:
ret , frame = cap.read()
if ret == False:
break
cv2.imshow("img" , frame)
dst.write(frame)
if cv2.waitKey(30) == 27 :
break
cv2.destroyAllWindows()
cap.releace()
映像の読み込みには
cv2.Videocaptureが必要になってきます
読み込むことができたかは
.isOpened()メソッドで確認することができます。
もし、Falseであれば
sys.exitでシステムを終了させることができます。
cap.read()で二つの変数が返ってきていますが
retは読み込めたかどうかのbool値
frameは映像データそのものです
frame.shapeで映像の解像度とチャネル数を表すことができます
fourccはコーデックを指定しています
dstという変数では動画を書き込む際のプログラムを記載しています。
cv2.VideoWriter("output/test.avi" , fourcc , 30.0 , (w,h))
# 第一引数:保存先
# 第二引数:コーデックの指定
# 第三引数:Fpsの指定
# 第四引数:解像度の指定
# 映像の表示
cv2.imshow("img" , frame)
# 映像の書き込み
dst.write(frame)
# 27:escボタンを表している。30second以内にescを押すと、映像の表示が終了する
if cv2.waitKey(30) == 27 :
break
# 開いているすべてのウィンドウを閉じたい場合は、destroyAllWindows()を使用します。
cv2.destroyAllWindows()
# 読み込んだ動画ファイルやカメラデバイスを閉じるにはrelease()メソッドを実行する。
cap.releace()
cap.releaceは明示的にしなくてもVideoCaptureオブジェクトのデストラクタが正常に実行されれば自動的にrelease()が実行されるそうです。一応この処理がされていることは覚えておきましょう。
以上が動画の読み込みから表示、書き込みでした。
一応自分の実行環境では、このような動画が表示されます笑
4.フィルタを作成して、画像に畳み込んでみる
CNNでは、畳み込み層というフィルタを介して特徴量抽出しています。
ここまではよく聞いたことあると思いますが、具体的にどのようなフィルタがされているのでしょうか。
それを学習するのが深層学習であるCNNなのですが、
今回は平均値フィルタを紹介します。
上の平均値フィルタは、3*3の畳み込みされた部分の平均の輝度値を算出します。
人物と背景のようなエッジがくっきりしている部分は、輝度値が急激に変化します。ちょうどそのエッジのど真ん中を畳み込みすると、平均の輝度値を出すので、ぼかす効果が発揮されるのです。
具体的なコードはこの通りです。
ndimage.convolveを用いることにより、畳み込み演算をすることができます。
具体的な説明は割愛しますが、@interactを用いると自由自在に畳み込みフィルタを変化させることができます。とても面白いです。
平均値フィルタがぼかす効果があることがHans On で学べます。
# 平均値フィルタ
vals= (val_start , val_end , val_step) = 1,30,2
val_default = 3
@interact(N=vals)
def g(N=val_default):
fig = plt.figure(figsize=(15,10))
ax1 = fig.add_subplot(1,2,1)
ax1.imshow(rgb2gray(dorocy) , cmap="gray")
ax2 = fig.add_subplot(1,2,2)
w = np.ones((N,N)) / (N**2) #N*N 平坦化フィルタ
ax2.imshow(ndimage.convolve(rgb2gray(dorocy),w) ,cmap="gray")
fig = plt.figure(figsize=(15,15))
5.画像の二値化
続いては画像の二値化について説明します
画像が表示されるか確認
img = cv2.imread("./output/test.jpg" , 0) # 0はグレースケールを示す
cv2.imshow("img" , img)
cv2.waitKey(0)
cv2.destroyAllWindows()
この画像を8bitでの輝度値0か255にしていきたいと思います
ではコードを書いていきます。
# 閾値の設定
threshold = 150
# 150超えた場合は255を返す
ret , img_th = cv2.threshold(img , threshold , 255 , cv2.THRESH_BINARY)
ret
# 150.0
cv2.thresholdを用いることにより、画像の二値化が可能となっております。
thresholdを指定することにより(今回は155)、その値以上であれば255を返すという記述になっております。
cv2.THRESH_BINARY_INVと最後の引数を指定することにより、逆に指定した閾値超えた場合は0と返すというような性質を持っています。
# 二値化画像の表示
cv2.imshow("img_th" , img_th)
cv2.waitKey(0)
cv2.destroyAllWindows()
また、閾値を自ら決めてしまうとうまく二値化ができないですよね。
そんな時に用いられるのが、大津の二値化というものです
大津の二値化
まずは二値化をするので、0か1かのグループに分けます
そのグループの分け方が肝になっており、
クラス間の分散・クラス内の分散を算出し、分離度を算出します。
maxell様の資料をお借りしています。
クラス内分散については、その二つの分散の平均を用います。
機械学習などではよく登場する分散共分散行列を算出すれば良いのですね。わからない方は調べてみてください。
# 大津の二値化を実行
ret2 , img_o = cv2.threshold(img , 0 , 255 , cv2.THRESH_OTSU)
ret2 # 139.0
#ヒストグラムを書いてみる。
hist = cv2.calcHist([img] , [0] , None , [256] , [0,256])
plt.plot(hist)