openCV
前回、画像処理ライブラリopenCVで画像の読み込みとグレースケール化、画像の保存を試してみました。
今回はグレースケール化した画像を使ってエッジ検出をしていきます。
openCVのエッジ検出にはその検出法としてSobel法、Laplacian法、Canny法という3つの方法があるようです。
手法としてどういう違いがあるのかは他のサイトを参考にしてもらうとして、とりあえず3つとも試してみましょう。
画像は前回用いた「lotus.jpg」を使い、読み込む際にグレースケールに変換します。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
cv2.imwrite("lotus2-0.jpg", img)
実行結果
それでは始めていきましょう。
Sobel法によるエッジ検出
まずはSobel法によるエッジ検出の方法です。
「cv2.Sobel(入力画像, ビット深度, x方向の微分数, y方向の微分数, カーネルサイズ)」とします。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_sobel = cv2.Sobel(img, cv2.CV_32F, 1, 1, ksize=3)
cv2.imwrite("lotus2-sobel-1.jpg", img_sobel)
実行結果
オリジナル画像
エッジ検出後画像
ここで変えられる引数は「ビット深度」、「x方向の微分数」、「y方向の微分数」、「カーネルサイズ」がありますので、それぞれ変えてみましょう。
ビット深度は一画素について何ビット、つまりは何バイトを使用して表現するかというものです。
Sobel法で使えるビット深度は決まっていて、こちらの記事が参考になります。
Sobel法で使えるのは、「cv2.CV_8U」、「cv2.CV_16U」、「cv2.CV_16S」、「cv2.CV_32F」、「cv2.CV_64F」ですのでそれぞれ試してみましょう。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_sobel_8u = cv2.Sobel(img, cv2.CV_8U, 1, 1, ksize=3)
img_sobel_16u = cv2.Sobel(img, cv2.CV_16U, 1, 1, ksize=3)
img_sobel_16s = cv2.Sobel(img, cv2.CV_16S, 1, 1, ksize=3)
img_sobel_32f = cv2.Sobel(img, cv2.CV_32F, 1, 1, ksize=3)
img_sobel_64f = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
cv2.imwrite("lotus2-sobel-8u.jpg", img_sobel_8u)
cv2.imwrite("lotus2-sobel-16u.jpg", img_sobel_16u)
cv2.imwrite("lotus2-sobel-16s.jpg", img_sobel_16s)
cv2.imwrite("lotus2-sobel-32f.jpg", img_sobel_32f)
cv2.imwrite("lotus2-sobel-64f.jpg", img_sobel_64f)
cv2.CV_8U
cv2.CV_16U
cv2.CV_16S
cv2.CV_32F
cv2.CV_64F
正直今回の画像からではその違いはよく分かりませんが、色調を何段階に分けるかという話だと思われますので、ビット深度をあげると、よりエッジの色の細かな違いを表現できるようになると思います。
次に「x方向の微分数」です。
これは単純に何回微分するという意味だと思いますので、とりあえず2に増やしてみます。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_sobel = cv2.Sobel(img, cv2.CV_32F, 2, 1, ksize=3)
cv2.imwrite("lotus2-sobel-2.jpg", img_sobel)
実行結果
x方向の微分数:1
x方向の微分数:2
ついでに「y方向の微分数」も変えてみましょう。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_sobel = cv2.Sobel(img, cv2.CV_32F, 1, 2, ksize=3)
cv2.imwrite("lotus2-sobel-3.jpg", img_sobel)
実行結果
y方向の微分数:1
y方向の微分数:2
なかなか分かりづらいですが、それぞれの方向の微分数が1の時よりも2の時の方が全体的に薄くなっています。
次に「カーネルサイズ」を変化させてみましょう。
カーネルサイズは微分するために用いるピクセルサイズのようです。
とりあえず小さくしたり、大きくしたりしてみます。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_sobel = cv2.Sobel(img, cv2.CV_32F, 1, 1, ksize=1)
cv2.imwrite("lotus2-sobel-4.jpg", img_sobel)
実行結果
ksize=3
ksize=1
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_sobel = cv2.Sobel(img, cv2.CV_32F, 1, 1, ksize=9)
cv2.imwrite("lotus2-sobel-5.jpg", img_sobel)
実行結果
ksize=3
ksize=9
ksizeを大きくするとより変化の少ないところまで検出されるようです。
Laplacian法によるエッジ検出
次にLaplacian法によるエッジ検出を見ていきましょう。
Laplacian法でエッジ検出するには「cv2.Laplacian(画像データ, ビット深度)」とします。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_laplacian = cv2.Laplacian(img, cv2.CV_32F)
cv2.imwrite("lotus2-laplacian-1.jpg", img_laplacian)
実行結果
オリジナル画像
エッジ検出後画像
Sobel法と比べてより細かな部分までエッジが検出されているように感じます。
Canny法によるエッジ検出
最後にCanny法によるエッジ検出を試してみましょう。
Canny法によるエッジ検出をするには「cv2.Canny(画像データ, 最小の閾値, 最大の閾値)」を用います。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_canny = cv2.Canny(img, 100, 100)
cv2.imwrite("lotus2-canny-1.jpg", img_canny)
実行結果
オリジナル画像
エッジ検出後画像
最小の閾値を下げてみるとこうなります。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_canny = cv2.Canny(img, 1, 100)
cv2.imwrite("lotus2-canny-2.jpg", img_canny)
実行結果
オリジナル画像
エッジ検出後画像
最大の閾値を上げてみるとこうなります。
import cv2
img = cv2.imread("lotus.jpg", cv2.IMREAD_GRAYSCALE)
img_canny = cv2.Canny(img, 100, 200)
cv2.imwrite("lotus2-canny-3.jpg", img_canny)
実行結果
オリジナル画像
エッジ検出後画像
こんな感じでそれぞれの閾値を変えることで、検出されるエッジを変えることができます。
とりあえずざっと触ってみた感じではCanny法が明確にエッジが検出できて、エッジの検出の仕方を変えるのも分かりやすいのではないかなと思います。
次回はopenCVを使って画像のリサイズ、トリミング、回転、反転を勉強してみます。
ではでは今回はこんな感じで。
コメント