13
Help us understand the problem. What are the problem?

posted at

updated at

画像処理初心者がOpenCVを使ってみた(1)

この記事は、画像処理エンジニア検定を受験するにあたり、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をインストールしました。image.png

画像の表示・出力

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」となっている。image.png

ウィンドウの調整

前項の画像の表示で表示させたウィンドウは固定サイズでした。
以下のようにコードを書くと、表示されるウィンドウのサイズを調整することができます。

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()

表示される画像は以下のような感じです。image.png

ウィンドウを縮小させるとこんな感じになります。
image.png

また、表示させる画像のリサイズを行う方法もあります。

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()

表示される画像は、以下のようになりました。
image.png

上記とは別の方法で画像をリサイズする方法もあります。

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()

出力結果は以下のようになります。
image.png

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()

上記コードを実行した結果、以下のように画像が表示されました。
image.png

また、以下の方法で画像を白黒にすることもできます。

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()

実行結果は以下のようになりました。画像が明るくなっているのが分かると思います。
image.png

また、変数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()

上記のコードを実行した結果がこちらです。元画像よりも暗い画像に変換されていることが分かります。
image.png

表示した画像に図形を描写する

読み込んだ画像に対して、図形などを表示させることができます。
以下にサンプルを作成しました。座標軸などは任意の値を設定しています。

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()

上記コードを実行した結果がこちらです。白い画像にそれぞれ図形が描写されていることが分かります。
image.png

画像の二値化

画像の二値化とは、画像を白と黒の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()

上記のコードを実行した結果が以下です。
image.png

また、以下のように適応に閾値を設定して、二値化することも可能です。

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()

上記のコードを実行した結果が以下です。
image.png

また、トラックバーを使用して、閾値を変化させることも可能です。

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に変化させたときの画像です。

image.pngimage.png
image.png

透視変換

透視変換とは、任意の視点から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()

実行結果は以下のような画像です。

image.png

畳み込み

以下のページに畳み込みについてまとまっているので、こちらで勉強させていただきました。

フィルターを用いて、画像の平滑化(画素値の変化を滑らかにすること)を行いました。
以下がそのコードです。

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」が元画像です。
楽譜の周りや音符などがぼやけて見えると思います。
image.png

また、画像の平滑化には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()

上記コードの実行結果は以下です。
こちらの画像も平滑化されています。

image.png

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()

上記の実行結果は以下のようになります。他の方法よりもぼやけた結果となりました。
image.png

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()

実行結果は以下のようになりました。
image.png

最後に

まだまだ画像処理を理解するには道のりが長そうです。
引き続き勉強します。
間違いなどありましたら、ご指摘いただけると幸いです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
13
Help us understand the problem. What are the problem?