以前の記事でOpenCVで顔検出するプログラムを解説しました。
今回は、顔検出の応用として、目と笑顔を検出する方法について解説します。
こちらが目、笑顔を検出するサンプルプログラムです。
import cv2 as cv
# カスケード分類器読み込み
frontalface_cascade = cv.CascadeClassifier("haarcascade_frontalface_default.xml")
eye_cascade = cv.CascadeClassifier("haarcascade_eye.xml")
smile_cascade = cv.CascadeClassifier("haarcascade_smile.xml")
# 画像の読み込み
img = cv.imread("sample.jpg")
# 顔検出
face = frontalface_cascade.detectMultiScale(img)
# 顔部を赤枠で囲む
for x, y, w, h in face:
cv.rectangle(img,(x,y), (x+w, y+h), (0,0,255), 1)
# 顔部の上3/4を切り取り
upper_face_img = img[y:int(y+3*h/4), x:x+w]
# 目検出
eye = eye_cascade.detectMultiScale(upper_face_img)
# 目部を青枠で囲む
for x1, y1, w1, h1 in eye:
cv.rectangle(img, (x+x1,y+y1), (x+x1+w1, y+y1+h1), (255,0,0), 1)
# 顔部の下半分を切り取り
under_face_img = img[int(y+h/2):(y+h), x:x+w]
# 笑顔検出
smile = smile_cascade.detectMultiScale(under_face_img)
# 笑顔を緑枠で囲む
for x2, y2, w2, h2 in smile:
cv.rectangle(img,(x+x2, int(y+h/2)+y2), (x+x2+w2, int(y+h/2)+y2+h2), (0,255,0), 1)
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()
実行結果がこちら
3人とも目が検出できました。右2人は笑っているので、口元が笑顔と判定されています。
次章ではサンプルプログラムを解説していきます。
サンプルプログラムの流れは以下のとおりとなっています。
STEP1~3は、別記事で解説していますので、本記事では割愛しました。
ここでは、STEP4: 目の検出、STEP5: 笑顔の検出について解説していきます。
目を検出するには「haarcascade_eye.xml」を使用します。
# カスケード分類器読み込み
eye_cascade = cv.CascadeClassifier("haarcascade_eye.xml")
# 顔部の上3/4を切り取り
upper_face_img = img[y:int(y+3*h/4), x:x+w]
# 目検出
eye = eye_cascade.detectMultiScale(upper_face_img)
# 目部を青枠で囲む
for x1, y1, w1, h1 in eye:
cv.rectangle(img, (x+x1,y+y1), (x+x1+w1, y+y1+h1), (255,0,0), 1)mg)
顔部の上3/4を切り取り、この中から目を検出して青枠で囲むということをしています。
もし、顔部を切り取らなかったらどうなるのでしょうか?
下図は顔部を切り取らなかった場合の実行結果です。
左の女性のおでこと口が目と誤判定されてしまいました。目は顔の上部に位置していますので、誤判定しないためにも検出範囲を顔の上部のみに絞る方が良いことが分かります。
笑顔を検出するのは「haarcascade_smile.xml」です。
# カスケード分類器読み込み
smile_cascade = cv.CascadeClassifier("haarcascade_smile.xml")
# 顔部の下半分を切り取り
under_face_img = img[int(y+h/2):(y+h), x:x+w]
# 笑顔検出
smile = smile_cascade.detectMultiScale(under_face_img)
# 笑顔を緑枠で囲む
for x2, y2, w2, h2 in smile:
cv.rectangle(img,(x+x2, int(y+h/2)+y2), (x+x2+w2, int(y+h/2)+y2+h2), (0,255,0), 1)
目の検出同様、顔部の下半分を切り取り、この中から笑顔を検出して緑枠で囲みます。
こちらも顔部を切り取らなかった場合を確認してみましょう。
様々なところで笑顔が検出されてしまいました。検出範囲を絞ることが重要です!
こちらの画像では右側女性の右目が検出出来ませんでした。
こちらの写真は3人とも笑顔ではないですが、口元が笑顔と検出されています。
笑顔検出というよりは、口検出に近いですね。
顔検出の応用として、OpenCVの「カスケード分類器」を使用した目・笑顔検出プログラムについて解説しました。
手順のおさらい
今回の記事が皆さんのPython学習に役立つなら幸いです。
Python独学が大変な方は、書籍やスクールを活用するのも手です。
物体のエッジを抽出するプログラムを作りたいが、どのように作ればよいか分からない。エッジ抽出の原理について詳しく知りたい。
そんな悩みを解消すべく本記事では、ノイズを除去しつつエッジ抽出するソーベルフィルタについて解説します。
まず、エッジ抽出原理を解説します。次にソーベルフィルタとは何かについての解説です。最後にPython,OpenCVを用いて画像のエッジを抽出するプログラムを解説します。
それでは始めましょう!
この記事はこんな人におすすめ!
ソーベルフィルタについて理解するためには、エッジ抽出の原理を学ぶ必要があります。ここでは、微分によってエッジ検出する原理を解説します。
画像処理でのエッジとは、画像の明るい部分と暗い部分が急激に変化する勾配部です。この勾配部を抽出するには、画像の輝度値を微分し数値化します。関数\(f(x)\)の微分\(f'(x)\)は以下式で表されますよね。
$$f'(x)=\lim_{h \to 0}\frac{f(x+h)-f(x)}{h}$$
下図のように、輝度値を微分した値が大きい部分がエッジに相当することが分かります。
デジタル画像は2変数かつ1画素ごとの値のため、どうやったら勾配を求められる?
2変数関数の場合は、微分する変数を1つ指定し、それ以外の変数を定数として微分します。このような微分を偏微分といいます。2変数関数を\(f(x,y)\)とし、xで微分した導関数を\(f_x(x,y)\)、yで微分した導関数を\(f_y(x,y)\)とすると
$$f_x(x,y) = \lim_{h \to 0}\frac{f(x+h, y)-f(x, y)}{h}$$
$$f_y(x,y) = \lim_{h \to 0}\frac{f(x, y+h)-f(x, y)}{h}$$
\(f_x(x,y)\)はx方向、\(f_y(x,y)\)はy方向の勾配を表します。
デジタル画像は、1画素ごとの離散値です。そのため、\(h\)は有限です。そこで、\(h=1\)として微分値を求めます。
$$f_x(x,y) = f(x+1, y)-f(x, y)$$
$$f_y(x,y) = f(x, y+1)-f(x, y)$$
これは座標\((x,y)\)とその前方座標\((x+1,y)、(x,y+1)\)の差分をとっていますので前方差分と呼ばれます。
一方、\(h=-1\)の場合は
$$f_x(x,y) = f(x, y)-f(x-1, y)$$
$$f_y(x,y) = f(x, y)-f(x, y-1)$$
となり、座標\((x,y)\)とその前方座標\((x-1,y)\)、\((x,y-1)\)の差分をとることから後方差分と呼ばれます。
また、前方差分と後方差分の平均を求めると
$$f_x(x,y) = \frac{f(x+1, y)-f(x-1, y)}{2}$$
$$f_y(x,y) = \frac{f(x, y+1)-f(x, y-1)}{2}$$
となります。座標\((x+1,y)\)と座標\((x-1,y)\)の差分、座標\((x,y+1)\)と座標\((x,y-1)\)の差分をとることから中心差分と呼ばれます。
空間フィルタリングにおいて線形フィルタは、入力画像を\(f(i,j)\)、出力画像を\(g(i,j)\)、フィルタ係数を\(h(m,n)\)とするとき、以下式で表されました。
$$g(i,j)=\sum_{n=-W}^{W}\sum_{m=-W}^{W}f(i+m,j+n)h(m,n)$$
https://craft-gogo.com/gauss-fillter/
微分をこの形式で表すと微分フィルタは以下表となります。
x方向 | y方向 | |
前方差分 | \begin{pmatrix} 0 & 0 & 0 \\ 0 & -1 & 1 \\ 0 & 0 & 0 \end{pmatrix} | \begin{pmatrix} 0 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 1 & 0 \end{pmatrix} |
後方差分 | \begin{pmatrix} 0 & 0 & 0 \\ -1 & 1 & 0 \\ 0 & 0 & 0 \end{pmatrix} | \begin{pmatrix} 0 & -1 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 0 \end{pmatrix} |
中心差分 | \begin{pmatrix} 0 & 0 & 0 \\ -1/2 & 0 & 1/2 \\ 0 & 0 & 0 \end{pmatrix} | \begin{pmatrix} 0 & -1/2 & 0 \\ 0 & 0 & 0 \\ 0 & 1/2 & 0 \end{pmatrix} |
画像にノイズが混入していると、ノイズをエッジと誤検出してしまいます。
そこで登場するのが、ノイズを除去しつつエッジ抽出するソーベルフィルタです。
そこで登場するのがソーベルフィルタです。ソーベルフィルタとは、前節で説明した中央差分したのち、中央に重み付けした平滑化を行ったフィルタです。
下記表は、x方向、y方向の中央差分、重み付き平滑化、ソーベルフィルタのフィルタ係数です。
中央差分: \(h_1(x,y)\) | 重み付き平滑化: \(h_2(x,y)\) | ソーベルフィルタ: \(h_2(x,y)\cdot h_1(x,y)\) | |
x方向 | \(\begin{pmatrix} 0 & 0 & 0 \\ -\frac{1}{2} & 0 & \frac{1}{2} \\ 0 & 0 & 0 \end{pmatrix}\) | \(\begin{pmatrix} 0 & \frac{1}{4} & 0 \\ 0 & \frac{2}{4} & 0 \\ 0 & \frac{1}{4} & 0 \end{pmatrix}\) | \(\frac{1}{8}\begin{pmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{pmatrix}\) |
y方向 | \(\begin{pmatrix} 0 & -\frac{1}{2} & 0 \\ 0 & 0 & 0 \\ 0 & \frac{1}{2} & 0 \end{pmatrix}\) | \(\begin{pmatrix} 0 & 0 & 0 \\ \frac{1}{4} & \frac{2}{4} & \frac{1}{4} \\ 0 & 0 & 0 \end{pmatrix}\) | \(\frac{1}{8}\begin{pmatrix} -1 & -2 & 1 \\ 0 & 0 & 0 \\ -1 & 2 & 1 \end{pmatrix}\) |
中央差分を\(h_1(x,y)\)、重み付き平滑化を\(h_2(x,y)\)とするとき、中央差分と重み付き平滑化を順に施したソーベルフィルタは、\(h_2(x,y)\cdot h_1(x,y)\)で計算できます。
ソーベルフィルタと同様なフィルタにブリューウィットフィルタ(Prewitt filter)というものがありますので紹介しておきます。
中央差分: \(h_1(x,y)\) | 平滑化: \(h_2(x,y)\) | ブリューウィットフィルタ: \(h_2(x,y)\cdot h_1(x,y)\) | |
x方向 | \(\begin{pmatrix} 0 & 0 & 0 \\ -\frac{1}{2} & 0 & \frac{1}{2} \\ 0 & 0 & 0 \end{pmatrix}\) | \(\begin{pmatrix} 0 & \frac{1}{3} & 0 \\ 0 & \frac{1}{3} & 0 \\ 0 & \frac{1}{3} & 0 \end{pmatrix}\) | \(\frac{1}{6}\begin{pmatrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{pmatrix}\) |
y方向 | \(\begin{pmatrix} 0 & -\frac{1}{2} & 0 \\ 0 & 0 & 0 \\ 0 & \frac{1}{2} & 0 \end{pmatrix}\) | \(\begin{pmatrix} 0 & 0 & 0 \\ \frac{1}{3} & \frac{1}{3} & \frac{1}{3} \\ 0 & 0 & 0 \end{pmatrix}\) | \(\frac{1}{6}\begin{pmatrix} -1 & -1 & 1 \\ 0 & 0 & 0 \\ -1 & 1 & 1 \end{pmatrix}\) |
こちらは中央差分と重みなしの平滑化を順に施したフィルタです。
では、PythonとOpenCVを用いて画像のエッジを抽出してみましょう。
下記は、画像を読み込み、ソーベルフィルタを適用したサンプルプログラムです。
import cv2 as cv
def concat_tile(im_list_2d):
return cv.vconcat([cv.hconcat(im_list_h) for im_list_h in im_list_2d])
img = cv.imread("sample.jpg") # 写真の読み込み
img_sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3) # 3x3の横方向ソーベルフィルタ
img_sobelx = cv.convertScaleAbs(img_sobelx)
img_sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=3) # 3x3の縦方向ソーベルフィルタ
img_sobely = cv.convertScaleAbs(img_sobely)
img_gradient = cv.addWeighted(img_sobelx, 0.5, img_sobely, 0.5, 0) # 勾配の大きさ
imgs = concat_tile([[img, img_sobelx], [img_sobely, img_gradient]])
cv.imshow("img", imgs)
cv.imwrite("output.jpg", imgs)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
左上が元画像、右上が横方向エッジ抽出、左下が縦方向のエッジ抽出、右下が勾配の大きさになります。
花の輪郭が抽出されているのが分かりますよね。
サンプルプログラムを解説していきましょう。
ソーベルフィルタ適用は、cv.Sobel()関数を呼び出します。
cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3)
第1引数に読み込む画像、第2引数は、出力画像のビット深度を指定します。第3,4引数はx,yに関する微分の次数です。(1, 0)でx方向、(0, 1)でy方向のエッジが抽出できます。ksizeはフィルタ係数の大きさです。今回は3×3のフィルタサイズを指定しました。
ソーベルフィルタは微分値をとるため、負の値にもなります。しかし、負の値が含まれていると画像表示することが出来ません。そこで、cv.convertScaleAbs()関数を呼び出して絶対値にします。
cv.convertScaleAbs(img)
縦方向と横方向のエッジを合成するには、cv.addWeighted()関数を呼び出します。
cv.addWeighted(src1, alpha1, src2, beta, gamma)
出力結果は、\(\alpha\cdot src1 + \beta \cdot src2 + \gamma\)となります。
今回は、ノイズを除去しつつエッジ抽出するソーベルフィルタについて解説しました。
これで、物体のエッジを抽出するプログラムが簡単に作れますね。
この記事が画像処理を学ぶ皆さんのためになれば幸いです。
独学が大変な方は、書籍やスクールを活用するのも手です。私も活用しているものを載せておきますので参考にして下さい。
最後までお読み頂きありがとうございました!
画像のごま塩ノイズを除去したい。そんなときはメディアンフィルタの登場です。本記事では、ノイズを除去するメディアンフィルタについて解説します。
この記事はこんな人におすすめ!
メディアンフィルタとは空間フィルタリングの一種です。
メディアン(median)とは中央値のこと。メディアンフィルタは、周辺画素の画素値を降(昇)順に並べ、中央値を出力します。周辺画素に対して著しく外れた値(ノイズ)は、メディアンフィルタを通過すると除去されます。
下図は3×3メディアンフィルタでノイズが除去されるイメージ図です。
座標(i,j)の画素は上下左右斜めに対して著しく外れた「10」ですが、メディアンフィルタを通過すると、上下左右斜めの画素中央値である「2」に変換され、ノイズが除去されることが分かります。
PythonとOpenCVライブラリを用いれば、簡単にメディアンフィルタで画像のノイズを除去することが出来ます。
こちらがメディアンフィルタのPythonサンプルプログラムです。
import cv2 as cv
def concat_tile(im_list_2d):
return cv.vconcat([cv.hconcat(im_list_h) for im_list_h in im_list_2d])
img = cv.imread("sample.jpg") # 写真の読み込み
img_median3 = cv.medianBlur(img, 3) # 3x3のメディアンフィルタ
img_median5 = cv.medianBlur(img, 5) # 5x5のメディアンフィルタ
img_median7 = cv.medianBlur(img, 7) # 7x7のメディアンフィルタ
imgs = concat_tile([[img, img_median3], [img_median5, img_median7]])
cv.imshow("img", imgs)
cv.imwrite("output.jpg", imgs)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
左上が元画像。右上、左下、右下がそれぞれ3×3、5×5、7×7のメディアンフィルタを適用した画像となります。
3×3フィルタサイズで、ごま塩ノイズが十分除去されていることが分かりますね。フィルタサイズを大きくしすぎると、画像全体がぼやけてしまうので注意が必要です。
cv.medianBlur(img, N) # NxNのメディアンフィルタ
第1引数に入力画像、第2引数にフィルタサイズを設定します。
ぼかしの基本であるガウシアンフィルタでも、ごま塩ノイズが除去できるか試してみましょう。
ガウシアンフィルタについては、下記リンク先参照して下さい。
https://craft-gogo.com/gauss-fillter/
左上が元画像。右上、左下、右下がそれぞれσ=1、σ=2、σ=3のガウシアンフィルタを適用した画像です。
σを大きくしないと、ごま塩ノイズは除去できないことが分かります。ごま塩ノイズ除去できたとしても、画像はかなりぼやけてしまいますね。
本記事では、ノイズを除去するメディアンフィルタについて解説しました。
画像のごま塩ノイズを除去したければ、メディアンフィルタを適用すればよいことが分かりましたね。
この記事が画像処理を学ぶ皆さんのためになれば幸いです。
独学が大変な方は、書籍やスクールを活用するのも手です。私も活用しているものを載せておきますので参考にして下さい。
最後までお読み頂きありがとうございました!
]]>写真の主役を引き立たせるために背景をぼかしたり、プライバシー保護のため写真の顔をぼかしたりしますよね?
Photoshopなどの画像処理ソフトで画像をぼかそうとすると、種類がたくさんあって何を使ったらいいものか…。
本記事では、ぼかしの基本であるボックスフィルタ、ガウシアンフィルタについて解説します。また、PythonとOpenCVで実際に写真をぼかす実践をします。この記事を読めば、画像をぼかしたかったら、悩まずガウシアンフィルタを選択すれば無難なことが分かります。
もう、ぼかしで悩むことから脱却しましょう!
この記事はこんな人におすすめ!
ぼかしを学ぶ前に空間フィルタリングを理解する必要があります。フィルタを直訳すると、ろ過器やこし器などです。画像処理では、画像をこの空間フィルタで処理することで様々な画像に変換します。空間フィルタには、ぼかすためのフィルタ、エッジを抽出するためのフィルタ、ノイズを除去するためのフィルタなどがあります。
空間フィルタの概念は、コーヒーの抽出で例えることができます。コーヒーは、コーヒーの粉をコーヒーフィルタに入れ、お湯を注ぐと作ることができますよね。澄んだお湯がコーヒーフィルタを介すと黒く濁ったコーヒーになる。空間フィルタリングも同様なイメージです。入力画像は、お湯です。空間フィルタリングは、コーヒーフィルタです。コーヒーの粉は、ぼかしフィルタなどフィルタの種類です。出力画像は、コーヒーです。入力画像をぼかしフィルタで処理すると、出力画像は入力画像がぼけた結果になります。
空間フィルタは、線形フィルタと非線形フィルタに分けられます。ボックスフィルタ、ガウシアンフィルタは線形フィルタのため、ここでは線形フィルタについて解説していきます。
線形フィルタは、入力画像を\(f(i,j)\)、出力画像を\(g(i,j)\)とするとき、以下の式により計算されます。
$$g(i,j)=\sum_{n=-W}^{W}\sum_{m=-W}^{W}f(i+m,j+n)h(m,n)$$
\(h(m,n)\)はフィルタの係数を表す配列であり、フィルタの種類によってこの配列は変わってきます。ボックスフィルタ、ガウシアンフィルタの係数は次章で詳しく説明します。
フィルタの大きさは、\((2W+1)\times (2W+1)\)です。\(W=1\)の場合、\(3\times 3\)行列、\(W=2\)の場合、\(5\times 5\)行列となります。
数式見てもイメージしづらいですよね…。
そこで数式に具体的な数値を代入し、イメージしやすくしましょう。下図は3×3フィルタの計算例です。
入力画像の(i,j)座標の画素は「10」でしたが、フィルタを通すと(i,j)座標の画素は「7」となります。
3×3フィルタの場合は、入力画像のある画素に対して、上下左右斜めの8画素が影響し出力画素となっていますよね。これが空間フィルタリングと呼ばれる理由です。
空間フィルタリングの知識は身に付いたと思いますので、画像をぼかすためのボックスフィルタ、ガウシアンフィルタについて解説していきます。
ボックスフィルタは、非常に簡単なぼかし(平滑化)フィルタです。フィルタの範囲を平均化するのみです。3×3フィルタの場合は、3×3=9画素をフィルタしますので、各フィルタ係数は9分の1となります。同様に5×5フィルタの場合は、各フィルタ係数は25分の1となります。
ガウシアンフィルタは、フィルタ係数がガウス分布となっているものです。
ガウス分布?
1次元のガウス分布は以下式で表されます。σは標準偏差です。
$$h_g (x) = \frac{1}{\sqrt{2\pi}\sigma}\exp(-\frac{x^2}{2\sigma^2})$$
グラフにすると下図です。
σが小さいと尖った山となり、周辺画素の影響は小さくなります。σが大きくなるにつれて、山は平坦となり、周辺画素の影響が大きくなります。ぼかしを強くしたいならσを大きくすればよいことが分かります。
画像は2次元ですので、2次元ガウス分布が必要です。2次元ガウス分布は以下式で表されます。
$$h_g(x) = \frac{1}{2\pi\sigma^2}\exp(-\frac{x^2+y^2}{2\sigma^2})$$
σ=2を代入してグラフにするとこんな感じ。
この式からNxNフィルタ係数行列を作れば、ガウシアンフィルタの出来上がりです。
PythonとOpenCVライブラリを用いれば、簡単にボックスフィルタとガウシアンフィルタで画像をぼかすことが出来ます。
まず、ボックスフィルタのサンプルプログラムです。
import cv2 as cv
def concat_tile(im_list_2d):
return cv.vconcat([cv.hconcat(im_list_h) for im_list_h in im_list_2d])
img = cv.imread("sample.jpg") # 写真の読み込み
img = cv.resize(img, None, fx=0.5, fy=0.5) # 写真サイズを2分の1
img_blur3 = cv.blur(img, (3, 3)) # 3×3のボックスフィルタ
img_blur5 = cv.blur(img, (5, 5)) # 5×5のボックスフィルタ
img_blur7 = cv.blur(img, (7, 7)) # 7x7のボックスフィルタ
imgs = concat_tile([[img, img_blur3], [img_blur5, img_blur7]])
cv.imshow("img", imgs)
cv.imwrite("output.jpg", imgs)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
左上が元画像。右上、左下、右下がそれぞれ3×3、5×5、7×7のボックスフィルタを適用した画像です。
フィルタサイズが大きくなるほど、ぼかしが強くなっていくことが分かります。
cv.blur(img, (N, N)) # N×Nのボックスフィルタ
第1引数に入力画像、第2引数にフィルタサイズを設定します。
次にガウシアンフィルタのサンプルプログラムです。
import cv2 as cv
def concat_tile(im_list_2d):
return cv.vconcat([cv.hconcat(im_list_h) for im_list_h in im_list_2d])
img = cv.imread("sample.jpg") # 写真の読み込み
img = cv.resize(img, None, fx=0.5, fy=0.5) # 写真サイズを2分の1
img_gauss1 = cv.GaussianBlur(img, (0, 0), 1) # σ=1のガウシアンフィルタ
img_gauss2 = cv.GaussianBlur(img, (0, 0), 2) # σ=2のガウシアンフィルタ
img_gauss3 = cv.GaussianBlur(img, (0, 0), 3) # σ=3のガウシアンフィルタ
imgs = concat_tile([[img, img_gauss1], [img_gauss2, img_gauss3]])
cv.imshow("img", imgs)
cv.imwrite("output.jpg", imgs)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
左上が元画像。右上、左下、右下がそれぞれσ=1、σ=2、σ=3のガウシアンフィルタを適用した画像です。
σを大きくすると、ぼかしが強くなります。
cv.GaussianBlur(img, (0, 0), N) # σ=Nのガウシアンフィルタ
第1引数に入力画像、第2引数にフィルタサイズ、第3引数にσを設定します。
ここでフィルタサイズが0?と思われるでしょう。
openCVでは、フィルタサイズを0に設定するとσに応じてフィルタサイズが自動設定されます。
フィルタサイズ設定に困ったら、σだけ設定すればよいです。
ボックスフィルタとガウシアンフィルタを比べてみると、ガウシアンフィルタの方がぼかしが自然かと思われます。画像をぼかしたかったら、ガウシアンフィルタを選んでおけば間違いないです。
本記事では、ぼかしの基本であるボックスフィルタ、ガウシアンフィルタについて解説しました。また、PythonとOpenCVで実際に写真をぼかす実践をしました。
画像をぼかしたかったら、悩まずガウシアンフィルタを選択すれば無難なことが分かりましたね。
これで、写真の主役を引き立たせるために背景をぼかしたり、プライバシー保護のため写真の顔をぼかしたりが出来ると思います。
この記事が画像処理を学ぶ皆さんのためになれば幸いです。最後まで読んで頂きありがとうございました!
独学が大変な方は、書籍やスクールを活用するのも手です。私も活用しているものを載せておきますので参考にして下さい。
]]>今回は、OpenCVを使って画像を平行移動・回転する方法について解説していきます。
画像の平行移動・回転は、アフィン変換という知識が必要です。
まず、アフィン変換について解説します。そして、OpenCVを使った画像処理プログラムを解説していきます。
平面座標系のアフィン変換は3×3の行列を使って次式で表現できます。
$$ \left( \begin{array}{ccc} x’ \\ y’ \\ 1 \end{array} \right) = \left( \begin{array}{ccc} a & b & t_{x} \\ c & d & t_{y} \\ 0 & 0 & 1 \end{array} \right) \left( \begin{array}{ccc} x \\ y \\ 1 \end{array} \right) $$3行目って左辺が「1」、右辺も「1」でムダじゃない?
3行目は、実際の座標に加えて、仮想的な座標を付け加えています。このような座標系を同次座標系と呼びます。同次座標系による表現は、平行移動を行う上で重要となります。
まず、アフィン変換を使って平行移動する方法を説明します。
座標\((x,y)\)から\(x\)方向に\(t_{x}\)、\(y\)方向に\(t_{y}\)平行移動するとします。移動先の座標を\((x’,y’)\)としました。座標\((x’,y’)\)は、次の式で表せますね。
$$ \left( \begin{array}{ccc} x’ \\ y’ \end{array} \right) = \left( \begin{array}{ccc} x \\ y \end{array} \right) + \left( \begin{array}{ccc} t_{x} \\ t_{y} \end{array} \right) $$これを同次座標系で表現すると次の式で整理できます。
$$ \left( \begin{array}{ccc} x’ \\ y’ \\ 1 \end{array} \right) = \left( \begin{array}{ccc} 1 & 0 & t_{x} \\ 0 & 1 & t_{y} \\ 0 & 0 & 1 \end{array} \right) \left( \begin{array}{ccc} x \\ y \\ 1 \end{array} \right) $$このように平行移動は、アフィン変換の3×3行列 \(a,b,c,d\)に\(a=d=1\)、\(b=c=0\)を代入すればよいことが分かります。
次にアフィン変換を使って回転する方法の説明です。
座標\((x,y)\)を原点中心に\(\theta\)角回転するとします。ここでも移動先の座標を\((x’,y’)\)としました。座標\((x’,y’)\)は次の式で表すことが出来ます。
$$ \left( \begin{array}{ccc} x’ \\ y’ \end{array} \right) = \left( \begin{array}{ccc} r \cos (\theta + \alpha) \\ r \sin (\theta + \alpha) \end{array} \right) $$ここで三角関数の加法定理を使うと
$$ \left( \begin{array}{ccc} x’ \\ y’ \end{array} \right) = \left( \begin{array}{ccc} r \cos \theta \cos \alpha – r \sin \theta \sin \alpha \\ r \sin \theta \sin \alpha + r \cos \theta \cos \alpha \end{array} \right) $$座標\((x,y)\)は
$$ \left( \begin{array}{ccc} x \\ y \end{array} \right) = \left( \begin{array}{ccc} r \cos \alpha \\ r \sin \alpha \end{array} \right) $$ですので、座標\((x’,y’)\)は次式となります。
$$ \left( \begin{array}{ccc} x’ \\ y’ \end{array} \right) = \left( \begin{array}{ccc} x \cos \theta – y \sin \theta \\ x \sin \theta + y \cos \theta \end{array} \right) $$式を整理すると
$$ \left( \begin{array}{ccc} x’ \\ y’ \end{array} \right) = \left( \begin{array}{ccc} \cos \theta & – \sin \theta \\ \sin \theta & \cos \theta \end{array} \right) \left( \begin{array}{ccc} x \\ y \end{array} \right) $$最後に、同次座標系で表現すると次式になります。
$$ \left( \begin{array}{ccc} x’ \\ y’ \\ 1 \end{array} \right) = \left( \begin{array}{ccc} \cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{array} \right) \left( \begin{array}{ccc} x \\ y \\ 1 \end{array} \right) $$回転は、アフィン変換の3×3行列\(a,b,c,d\)に\(a=\cos\theta\)、\(b=-\sin\theta\)、\(c=\sin\theta\)、\(d=\cos\theta\)、\(t_{x}=0\)、\(t_{y}=0\)を代入すればよいことが分かります。
このように平行移動、回転はアフィン変換で実現できます。
アフィン変換について理解できたと思いますので、本題のOpenCVで画像を平行移動・回転していきましょう!
今回は▼の画像を平行移動・回転します。画像は何でもよいですよ。
画像を平行移動するサンプルプログラムです。
import cv2 as cv
import numpy as np
#画像データの読み込み
img = cv.imread("sample.jpg")
height, width = img.shape[:2]
tx, ty = 100, 100
#アフィン変換: 平行移動
mv_mat = np.float32([[1, 0, tx],[0, 1, ty]])
afin_img = cv.warpAffine(img, mv_mat, (width, height))
cv.imshow("img", afin_img)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
水平方向に100、垂直方向に100移動させるため、\(t_{x}=100\)、\(t_{y}=100\)としました。アフィン変換の3×3行列 「mv_mat」を作成します。OpenCVでは、画像をアフィン変換するためのwarpAffine関数が用意されていますので、第1引数に移動対象の画像、第2引数にアフィン変換の行列、第3引数に出力画像サイズを指定しましょう。
出力結果が▼となります。
画像を回転するサンプルプログラムです。
import cv2 as cv
import numpy as np
#画像データの読み込み
img = cv.imread("sample.jpg")
height, width = img.shape[:2]
#アフィン変換: 回転
rot_mat = cv.getRotationMatrix2D((width/2, height/2), 45, 1)
afin_img = cv.warpAffine(img, rot_mat, (width, height))
cv.imshow("img", afin_img)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
前章で解説した回転のアフィン変換は、原点を中心に行われます。任意の点を中心に回転したい場合は、平行移動→回転→平行移動を繰り返す必要があります。OpenCVは、この処理を一括で行ってくれる関数があります。それがgetRotationMatrix2D関数です。第1引数に回転中心の座標、第2引数に角度、第3引数に拡大率を指定します。getRotationMatrix2D関数を実行するとアフィン変換の行列が戻り値として返ってきます。
出力結果が▼となります。
画像の中央点を中心に画像が回転されました。
今回は、OpenCVを使って画像を平行移動・回転する方法について解説しました。
画像処理ソフトがどうやって画像を平行移動・回転しているか理解出来たのではないかと思います。
今回の記事が皆さんのPython学習に役立つなら幸いです。
Python独学が大変な方は、書籍やスクールを活用するのも手です。
今回はOpenCVを使って文字を描画する方法について解説します。
文字の大きさ、フォント、太さを様々に変えて表示の違いを見ることで、画像処理などで文字描画を使うときに本記事が役立てればよいと思っています。
まず、文字描画の土台となるプログラムを下記に示します。文字描画にはcv.putText()を使います。この引数を変えることで、文字の大きさ、フォント、色を変えることが出来ます。
import cv2 as cv
import numpy as np
# 黒い背景を作成
img = np.zeros((240,640,3), np.uint8)
# ここにプログラムを追加して、文字を描画します
font = cv.FONT_HERSHEY_SIMPLEX
cv.putText(img,'CRAFT',(20,150),font,4,(0,255,0),2,cv.LINE_AA)
cv.imshow("Draw Text", img)
k = cv.waitKey(0)
if k == ord("q"):
cv.destroyAllWindows()
文字描画はcv.putText()を用いることで実現出来ます。
cv.putText(img, text, (X0,Y0), fontType, scale, (Blue, Green, Red), thickness, lineType)
文字の大きさを1~4まで変化させてみました。
cv.putText(img,'CRAFT',(10,30),font,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,80),font,2,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,150),font,3,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,240),font,4,(0,255,0),2,cv.LINE_AA)
表示はこんな感じになります。
OpenCVにはフォントが9種類用意されています。全部表示してみます。
font1 = cv.FONT_HERSHEY_SIMPLEX
font2 = cv.FONT_HERSHEY_PLAIN
font3 = cv.FONT_HERSHEY_DUPLEX
font4 = cv.FONT_HERSHEY_COMPLEX
font5 = cv.FONT_HERSHEY_TRIPLEX
font6 = cv.FONT_HERSHEY_COMPLEX_SMALL
font7 = cv.FONT_HERSHEY_SCRIPT_SIMPLEX
font8 = cv.FONT_HERSHEY_SCRIPT_COMPLEX
font9 = cv.FONT_ITALIC
cv.putText(img,'CRAFT',(10,30),font1,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,60),font2,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,90),font3,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,120),font4,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,150),font5,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,180),font6,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,210),font7,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,240),font8,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,270),font9,1,(0,255,0),2,cv.LINE_AA)
表示結果です。
個人的には上から5番目の「FONT_HERSHEY_TRIPLEX」が好みですね。
今度は、文字太さを1~9まで変えてみます。
font = cv.FONT_HERSHEY_SIMPLEX
cv.putText(img,'CRAFT',(10,30),font,1,(0,255,0),1,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,60),font,1,(0,255,0),2,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,90),font,1,(0,255,0),3,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,120),font,1,(0,255,0),4,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,150),font,1,(0,255,0),5,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,180),font,1,(0,255,0),6,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,210),font,1,(0,255,0),7,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,240),font,1,(0,255,0),8,cv.LINE_AA)
cv.putText(img,'CRAFT',(10,270),font,1,(0,255,0),9,cv.LINE_AA)
表示はこんな感じです。
OpenCVを使って文字を描画する方法について解説しました。
文字を描画する関数は、cv.putText()です。
今回、文字の大きさ、フォント、太さを変え、表示の違いを確認しました。
画像処理などで文字描画を実際に使う時のイメージがつかめたのではないでしょうか。
これからもOpenCVの使い方を解説していく予定です。
]]>OpenCVを使うと画像や動画から特定の物体を検出することなどが簡単に出来ます。こんなプログラムを作成したときに、検出した物体を枠で囲んでマークしたいですよね。
▼こんなふうに
今回は、OpenCVを使って線、円、楕円、矩形を描画する方法について解説します。
まず、図形を描画するのに土台となるプログラムを下記に記しました。cv.line()で赤線を描いています。この部分に図形描画のプログラムを記述することで様々な図形を描くことが出来ます。
次章では、線、円、矩形の描き方について解説します。
import cv2 as cv
import numpy as np
# 黒い背景を作成
img = np.zeros((480,640,3), np.uint8)
# ここにプログラムを追加して、色々な図形を描きます
cv.line(img,(0,0),(640,480),(0,0,255),10)
cv.imshow("Drawing Figure", img)
k = cv.waitKey(0)
if k == ord("q"):
cv.destroyAllWindows()
最初に線の描画方法を解説します。線を引くにはcv.line()を使用します。
cv.line(Img,(X0,Y0),(X1,Y1),(Blue,Green,Red),Thickness)
座標の原点は、画像の左上です。水平右方向がX座標、鉛直下方向がY座標となります。
cv.line(img,(20, 20),(600,400),(255,0,0),1)
cv.line(img,(20, 40),(600,420),(0,255,0),3)
cv.line(img,(20, 60),(600,440),(0,0,255),5)
第5引数で線のタイプを指定できます。線のタイプは省略可能です。
cv.line(img,(20, 20),(600,400),(255,0,0),3, cv.FILLED)
cv.line(img,(20, 40),(600,420),(255,0,0),3, cv.LINE_4) # 4-connected line
cv.line(img,(20, 60),(600,440),(255,0,0),3, cv.LINE_8) # 8-connected line (デフォルト)
cv.line(img,(20, 80),(600,460),(255,0,0),3, cv.LINE_AA) # antialiased line
うーん・・・。私にはアンチエイリアスの線以外、違いが分かりませんでした。アンチエイリアスにするかしないかの選択で良い気がします。
続いて円の描画方法です。円を描くにはcv.circle()を使用します。
cv.circle(Img, (X0, Y0), Radius, (Blue, Green, Red), Thickness)
cv.circle(img, (100, 100), 50, (255,0,0), 1)
cv.circle(img, (150, 150), 80, (0,255,0), 3)
cv.circle(img, (200, 200), 100, (0,0,255), 5)
cv.circle(img, (250, 250), 130, (255,0,0), -1)
線の太さを-1に設定すると円が塗りつぶされます。
楕円の描画方法はややこしいです。
cv.ellipse(Img, (X0,Y0),(Width,Height),Angle,StartAngle,EndAngle,(Blue,Green,Red),Thickness)
cv.ellipse(img, (100,50),(20,50),0,0,360,(255,0,0),1)
cv.ellipse(img, (150,100),(100,50),0,45,270,(0,255,0),3)
cv.ellipse(img, (200,150),(100,50),45,0,360,(0,0,255),5)
cv.ellipse(img, (400,300),(200,50),0,45,180,(255,0,0),-1)
cv.rectangle(img, (X0,Y0), (X1,Y1), (Blue, Green, Red), Thickness)
cv.rectangle(img, (50,50),(150,100), (255,0,0),1)
cv.rectangle(img, (100,150),(350,250), (0,255,0),3)
cv.rectangle(img, (150,200),(400,300), (0,0,255),5)
cv.rectangle(img, (350,350),(600,450), (255,0,0),-1)
OpenCVを使って線、円、楕円、矩形を描画する方法について解説しました。
図形描画の関数をまとめると
です。
]]>デジカメのように顔検出するプログラムを作ってみたいと思いませんか?
OpenCVを使えば、たった十数行で顔検出できるプログラムが出来上がります。
OpenCVには大量の画像から人の顔の特徴を学習し、人の顔を判別する「カスケード分類器」が用意されています。これを使用した顔検出プログラムを解説していきます。
顔検出の流れは以下のとおりとなります。
import cv2 as cv
#カスケード分類器読み込み
HAAR_FILE = "haarcascade_frontalface_default.xml"
cascade = cv.CascadeClassifier(HAAR_FILE)
#画像の読み込み
img = cv.imread("sample.jpg")
#顔検出
face = cascade.detectMultiScale(img)
#顔部を枠で囲む
for x, y, w, h in face:
cv.rectangle(img,(x,y),(x+w,y+h),(0,0,255),1)
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()
HAAR_FILE = "haarcascade_frontalface_default.xml"
cascade = cv.CascadeClassifier(HAAR_FILE)
cv.CascadeClassifier()メソッドを呼び出し、人の顔を検出するための「カスケード分類器」を読み込みます。
カスケード分類器?
OpenCVは、人の顔を集めた大量画像から人の顔の特徴量をまとめたHaar-Cascadeが用意されています。これはGitHubからダウンロードすることが出来ます。
今回は、正面の顔を検出する「haarcascade_frontalface_default.xml」を使用しました。他にも色々な分類器があります。
img = cv.imread("sample.jpg")
cv.imread()メソッドを呼び出し、人の顔を検出したい画像を読み込みます。
face = cascade.detectMultiScale(img)
cascade.detectMultiScale()メソッドを呼び出し、画像内の顔を検出します。戻り値として、検出した顔の位置が配列に格納され返ってきます。戻り値は順に、顔の位置のx座標、y座標、横幅、縦幅になります。
for x, y, w, h in face:
cv.rectangle(img,(x,y),(x+w,y+h),(0,0,255),1)
faceに顔の位置座標が格納されています。顔が正常に検出されたかどうか確認するため、元画像に赤枠を加えます。
違う画像でも試してみました。この画像は5人中、4人検出出来ました。検出出来ない人もいます。
横顔は検出するのが難しそうです。誤検出も発生しています。
ここまでの解説は静止画像でした。プログラムを少し改変することで、PCカメラのリアルタイム画像から顔検出することが出来ます。
自分の顔が検出されるか試してみて下さい。
import cv2 as cv
HAAR_FILE = "haarcascade_frontalface_default.xml"
cascade = cv.CascadeClassifier(HAAR_FILE)
cap = cv.VideoCapture(0)
while(True):
ret, frame = cap.read()
face = cascade.detectMultiScale(frame)
for x, y, w, h in face:
cv.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),1)
cv.imshow('frame',frame)
if cv.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv.destroyAllWindows()
OpenCVの「カスケード分類器」を使用した顔検出プログラムについて解説しました。
手順をおさらいすると以下になります。
今回の記事が皆さんのPython学習に役立つなら幸いです。
Python独学が大変な方は、書籍やスクールを活用するのも手です。
Python 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイルを学ぶオンライン講座 ]]>今回の記事では、OpenCVライブラリを用いて、1枚の画像から特定の色を検出する方法について解説します。
この記事はこんな人におすすめ!
それでは始めましょう!
サンプルプログラムは以下のようになります。
import cv2 as cv
import numpy as np
#画像データの読み込み
img = cv.imread("sample.jpg")
#BGR色空間からHSV色空間への変換
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
#色検出しきい値の設定
lower = np.array([90,64,0])
upper = np.array([150,255,255])
#色検出しきい値範囲内の色を抽出するマスクを作成
frame_mask = cv.inRange(hsv, lower, upper)
#論理演算で色検出
dst = cv.bitwise_and(img, img, mask=frame_mask)
cv.imshow("img", dst)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
青色のみを検出するよう色しきい値を設定し、1枚の画像の青色部のみを表示しました。色しきい値を変更することによって、赤色や緑色など様々な色を検出することが出来ます。
元画像です。
プログラム実行結果です。青色の花と空が検出され、他は黒くなって表示されました。
【Pythonで学ぶ】OpenCVでの画像処理入門サンプルプログラムの解説をしていきます。openCVのcv.imread()は、BGR色空間で読み込まれます。色検出する場合、HSV空間の方が色検出しきい値を指定しやすいので、BGR色空間からHSV色空間に変換します。
#画像データの読み込み
img = cv.imread("sample.jpg")
#BGR色空間からHSV色空間への変換
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
HSV色空間とは、「色相(Hue)」「彩度(Saturation)」「明度(Value・Brightness)」の3要素から成る色空間です。各要素の説明は以下表のとおりとなります。HSV色空間の値とOpenCVの値は異なるので、注意が必要です。
説明 | 値範囲 | OpenCVの値範囲 | |
---|---|---|---|
色相(H) | 色合い。赤が0度で360度まで虹色の順で変化する。 | 0-360度 | 0-180 |
彩度(S) | 色の鮮やかさ。100%が純色で低くなるにつれて白くなる。 | 0-100% | 0-255 |
明度(V) | 色の明るさ。100%が純色で低くなるにつれて暗くなる。 | 0-100% | 0-255 |
青系の色を検出をするために、色相が90-150(180-300度)、彩度が64-255(25-100%)、明度が0-255(0-100%)の範囲を抽出するマスクを作成します。作成するにはcv.inRange()を使用します。cv.inRange()は、指定したい範囲の色を255、それ以外の範囲の色を0として2値化します。第1引数に2値化したい画像を指定します。第2引数、第3引数で色範囲を指定します。
#色検出しきい値の設定
lower = np.array([90,64,0])
upper = np.array([150,255,255])
#色検出しきい値範囲内の色を抽出するマスクを作成
frame_mask = cv.inRange(hsv, lower, upper)
下図は2値化した画像です。青色部が白、それ以外は黒になりました。
#論理演算で色検出
dst = cv.bitwise_and(img, img, mask=frame_mask)
cv.bitwise_and()でマスクをかけ、cv.imshow()で表示させると実行結果の通り青色のみ検出されます。
色検出しきい値を変えて、違う色も検出してみましょう。まずは緑です。色相範囲は30-90(60-180度)としました。
#色検出しきい値の設定
lower = np.array([30,64,0])
upper = np.array([90,255,255])
#色検出しきい値範囲内の色を抽出するマスクを作成
frame_mask = cv.inRange(hsv, lower, upper)
花の葉と木の葉が検出されました。
次は赤です。赤は0-30(0-60度)、150-180(300-360度)です。色範囲が2条件となりますので、プログラムに工夫が必要です。
#色検出しきい値の設定
lower = np.array([0,64,0])
upper = np.array([30,255,255])
#色検出しきい値範囲内の色を抽出するマスクを作成
frame_mask1 = cv.inRange(hsv, lower, upper)
#色検出しきい値の設定
lower = np.array([150,64,0])
upper = np.array([180,255,255])
#色検出しきい値範囲内の色を抽出するマスクを作成
frame_mask2 = cv.inRange(hsv, lower, upper)
frame_mask = frame_mask1 + frame_mask2
2つのマスクを足し合わせています。赤、オレンジ、黄色の花が検出されました。
以上、OpenCVを使って特定の色を検出するプログラムの解説でした。
おさらいしますと
Python、OpenCV学習のお役に立てたなら幸いです。
独りでPython学習するのは大変だなと思う方は、書籍やスクールを活用するのも手です。
最後までお読み頂きありがとうございました!
]]>OpenCVライブラリを使用して、PCカメラから動画を表示してみました。その方法について解説します。
サンプルプログラムは以下のようになります。実行することでウィンドウが立ち上がり、PCカメラで映し出された動画で表示されます。「q」キーを押すことでウィンドウが閉じ、プログラムが終了します。
import cv2 as cv
WIDTH: int = 320
HEIGHT: int = 240
cap = cv.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
cap.set(cv.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, HEIGHT)
print("Width = ", cap.get(cv.CAP_PROP_FRAME_WIDTH))
print("Height = ", cap.get(cv.CAP_PROP_FRAME_HEIGHT))
while True:
# 1フレームずつ読み込む
ret, frame = cap.read()
# フレームが正しく読み込まれない場合
if not ret:
print("Can't receive frame")
break
# 読み込んだフレームを表示
cv.imshow("frame", frame)
#「q」キーが押されたらウィンドウを閉じる
if cv.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv.destroyAllWindows()
次項では、プログラム詳細を解説していきます。
まず、VideoCapture()でVideoCaptureオブジェクトを取得します。
cap = cv.VideoCapture(0)
引数は、PCに接続されているカメラ番号になります。PCにカメラが1台しか接続されていない場合は「0」を指定します。
if not cap.isOpened():
exit()
もし、cap.isOpened()がFalseを返したならば、プログラムを終了するようにしています。
openCVのデフォルトでは、解像度は640×480のようです。今回は320×240の解像度に変更しました。カメラの仕様により解像度は自由に変更することは出来ないので、自身がお持ちのカメラ仕様を調べて下さい。
解像度の幅と高さを指定して、指定した値が反映されているか確認しています。
WIDTH: int = 320
HEIGHT: int = 240
cap.set(cv.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, HEIGHT)
print("Width = ", cap.get(cv.CAP_PROP_FRAME_WIDTH))
print("Height = ", cap.get(cv.CAP_PROP_FRAME_HEIGHT))
cap.set()は動画のプロパティを設定するメソッドです。第1引数にプロパティ、第2引数に値を指定します。
cap.get()はプロパティを取得するメソッドです。第1引数にプロパティを指定します。
プロパティ | 説明 |
---|---|
CAP_PROP_FRAME_WIDTH | フレーム幅 |
CAP_PROP_FRAME_HEIGHT | フレーム高さ |
カメラから連続した画像を表示するため、cap.read()で読み込んだ画像をcv.imshow()で表示し、while文で繰り返します。
while True:
# 1フレームずつ読み込む
ret, frame = cap.read()
# フレームが正しく読み込まれない場合
if not ret:
print("Can't receive frame")
break
# 読み込んだフレームを表示
cv.imshow("frame", frame)
cv.waitKey(1)
cap.read()は2つの戻り値を返します。1つ目は、正しくフレームが読み込まれたかどうかTrue/FalseのBoolean値。2つ目は読み込んだ画像データです。
正しくフレームが読み込まれない場合は、while文を抜け、プログラムを終了させます。
フレームを読み込むだけでは、スクリーンに表示されないので、cv.imshow()で表示させます。cv.imshow()の第1引数は、表示させるフレームの名称を文字列で指定します。第2引数は、読み込む画像データを指定します。
cv.imshow()だけでは、処理が高速すぎてウィンドウは真っ黒のまま表示され、固まってしまいます。cv.waitkey(1)で1ms処理を待機させることで動画として表示出来るようにしています。
「q」キーが押されたらウィンドウを閉じるようにします。
while True:
#「q」キーが押されたらウィンドウを閉じる
if cv.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv.destroyAllWindows()
cv.waitKey()はキーが何も押されていないとき-1を返します。ここでは、「q」が押されたらwhile文を抜けるようにしています。
cap.release()でVideoCaptureオブジェクトを解放し、cv.destroyAllWindows()でウィンドウを閉じてプログラム終了です。
今回は、PCカメラを使って動画を表示する方法を解説しました。
要点をまとめると