[OpenCV-Python] OpenCV 中的圖像處理 部分 IV (三)

部分 IV
OpenCV 中的圖像處理

OpenCV-Python 中文教程(搬運)目錄html

 

19 Canny 邊緣檢測

 

目標
  • 瞭解 Canny 邊緣檢測的概念
  • 學習函數 cv2.Canny()算法


19.1 原理
  Canny 邊緣檢測是一種很是流行的邊緣檢測算法,是 John F.Canny 在1986 年提出的。它是一個有不少步構成的算法,咱們接下來會逐步介紹。小程序


19.1.1 噪聲去除
  因爲邊緣檢測很容易受到噪聲影響,因此第一步是使用 5x5 的高斯濾波器去除噪聲,這個前面咱們已經學過了。app


19.1.2 計算圖像梯度
  對平滑後的圖像使用 Sobel 算子計算水平方向和豎直方向的一階導數(圖像梯度)(Gx 和 Gy)。根據獲得的這兩幅梯度圖(Gx 和 Gy)找到邊界的梯度和方向,公式以下:
      Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2}

Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y}{G_x}\bigg)
梯度的方向通常老是與邊界垂直。梯度方向被歸爲四類:垂直,水平,和兩個對角線。函數


19.1.3 非極大值抑制
  在得到梯度的方向和大小以後,應該對整幅圖像作一個掃描,去除那些非邊界上的點。對每個像素進行檢查,看這個點的梯度是否是周圍具備相同梯度方向的點中最大的。以下圖所示:

post

      Non-Maximum Suppression

如今你獲得的是一個包含「窄邊界」的二值圖像。學習


19.1.4 滯後閾值
  如今要肯定那些邊界纔是真正的邊界。這時咱們須要設置兩個閾值:minVal 和 maxVal。當圖像的灰度梯度高於 maxVal 時被認爲是真的邊界,那些低於 minVal 的邊界會被拋棄。若是介於二者之間的話,就要看這個點是否與某個被肯定爲真正的邊界點相連,若是是就認爲它也是邊界點,若是不是就拋棄。以下圖:優化

      Hysteresis Thresholding
A 高於閾值 maxVal 因此是真正的邊界點,C 雖然低於 maxVal 但高於minVal 而且與 A 相連,因此也被認爲是真正的邊界點。而 B 就會被拋棄,由於他不只低於 maxVal 並且不與真正的邊界點相連。因此選擇合適的 maxVal和 minVal 對於可否獲得好的結果很是重要。
在這一步一些小的噪聲點也會被除去,由於咱們假設邊界都是一些長的線段。spa


19.2 OpenCV 中的 Canny 邊界檢測
  在 OpenCV 中只須要一個函數:cv2.Canny(),就能夠完成以上幾步。
讓咱們看如何使用這個函數。這個函數的第一個參數是輸入圖像。第二和第三個分別是 minVal 和 maxVal。第三個參數設置用來計算圖像梯度的 Sobel卷積核的大小,默認值爲 3。最後一個參數是 L2gradient,它能夠用來設定求梯度大小的方程。若是設爲 True,就會使用咱們上面提到過的方程,不然使用方程:Edge\_Gradient \; (G) = |G_x| + |G_y|. 代替,默認值爲 False。code

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('messi5.jpg',0)
edges = cv2.Canny(img,100,200)

plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])

plt.show()

結果:

個人結果

    

官方結果

     Canny Edge Detection


更多資源
1. Canny edge detector at Wikipedia
2. Canny Edge Detection Tutorial by Bill Green, 2002.
練習
1. 寫一個小程序,能夠經過調節滑動條來設置閾值 minVal 和 maxVal 進而來進行 Canny 邊界檢測。這樣你就會理解閾值的重要性了。


20 圖像金字塔


目標
  • 學習圖像金字塔
  • 使用圖像建立一個新水果:「橘子蘋果」
  • 將要學習的函數有:cv2.pyrUp(),cv2.pyrDown()。


20.1 原理
  通常狀況下,咱們要處理是一副具備固定分辨率的圖像。可是有些狀況下,咱們須要對同一圖像的不一樣分辨率的子圖像進行處理。好比,咱們要在一幅圖像中查找某個目標,好比臉,咱們不知道目標在圖像中的尺寸大小。這種狀況下,咱們須要建立建立一組圖像,這些圖像是具備不一樣分辨率的原始圖像。咱們把這組圖像叫作圖像金字塔(簡單來講就是同一圖像的不一樣分辨率的子圖集合)。若是咱們把最大的圖像放在底部,最小的放在頂部,看起來像一座金字塔,故而得名圖像金字塔。
  有兩類圖像金字塔:高斯金字塔和拉普拉斯金字塔。
  高斯金字塔的頂部是經過將底部圖像中的連續的行和列去除獲得的。頂部圖像中的每一個像素值等於下一層圖像中 5 個像素的高斯加權平均值。這樣操做一次一個 MxN 的圖像就變成了一個 M/2xN/2 的圖像。因此這幅圖像的面積就變爲原來圖像面積的四分之一。這被稱爲 Octave。連續進行這樣的操做咱們就會獲得一個分辨率不斷降低的圖像金字塔。咱們可使用函數cv2.pyrDown() 和 cv2.pyrUp() 構建圖像金字塔。
  函數 cv2.pyrDown() 從一個高分辨率大尺寸的圖像向上構建一個金子塔(尺寸變小,分辨率下降)。

img = cv2.imread('messi5.jpg')
lower_reso = cv2.pyrDown(higher_reso)

下圖是一個四層的圖像金字塔。

    Gaussian Pyramid

函數 cv2.pyrUp() 從一個低分辨率小尺寸的圖像向下構建一個金子塔(尺寸變大,但分辨率不會增長)。

higher_reso2 = cv2.pyrUp(lower_reso)

    Gaussian Pyramid

  你要記住的是是 higher_reso2 和 higher_reso 是不一樣的。由於一旦使用 cv2.pyrDown(),圖像的分辨率就會下降,信息就會被丟失。下圖就是從 cv2.pyrDown() 產生的圖像金字塔的(由下到上)第三層圖像使用函數cv2.pyrUp() 獲得的圖像,與原圖像相比分辨率差了不少。
  拉普拉斯金字塔能夠有高斯金字塔計算得來,公式以下:
    

拉普拉金字塔的圖像看起來就像邊界圖,其中不少像素都是 0。他們常常被用在圖像壓縮中。下圖就是一個三層的拉普拉斯金字塔:

    Laplacian Pyramid


20.2 使用金字塔進行圖像融合
  圖像金字塔的一個應用是圖像融合。例如,在圖像縫合中,你須要將兩幅圖疊在一塊兒,可是因爲鏈接區域圖像像素的不連續性,整幅圖的效果看起來會不好。這時圖像金字塔就能夠排上用場了,他能夠幫你實現無縫鏈接。這裏的一個經典案例就是將兩個水果融合成一個,看看下圖也許你就明白我在講什麼了。
    Pyramid Blending
你能夠經過閱讀後邊的更多資源來了解更多關於圖像融合,拉普拉斯金字塔的細節。
實現上述效果的步驟以下:
  1. 讀入兩幅圖像,蘋果和句子
  2. 構建蘋果和橘子的高斯金字塔(6 層)
  3. 根據高斯金字塔計算拉普拉斯金字塔
  4. 在拉普拉斯的每一層進行圖像融合(蘋果的左邊與橘子的右邊融合)
  5. 根據融合後的圖像金字塔重建原始圖像。
下圖是摘自《學習 OpenCV》展現了金子塔的構建,以及如何從金字塔重建原始圖像的過程。
整個過程的代碼以下。(爲了簡單,每一步都是獨立完成的,這回消耗更多、的內存,若是你願意的話能夠對他進行優化)

import cv2
import numpy as np,sys

A = cv2.imread('apple.jpg')
B = cv2.imread('orange.jpg')

# generate Gaussian pyramid for A
G = A.copy()
gpA = [G]
for i in xrange(6):
    G = cv2.pyrDown(G)
    gpA.append(G)

# generate Gaussian pyramid for B
G = B.copy()
gpB = [G]
for i in xrange(6):
    G = cv2.pyrDown(G)
    gpB.append(G)

# generate Laplacian Pyramid for A
lpA = [gpA[5]]
for i in xrange(5,0,-1):
    GE = cv2.pyrUp(gpA[i])
    L = cv2.subtract(gpA[i-1],GE)
    lpA.append(L)

# generate Laplacian Pyramid for B
lpB = [gpB[5]]
for i in xrange(5,0,-1):
    GE = cv2.pyrUp(gpB[i])
    L = cv2.subtract(gpB[i-1],GE)
    lpB.append(L)

# Now add left and right halves of images in each level
LS = []
for la,lb in zip(lpA,lpB):
    rows,cols,dpt = la.shape
    ls = np.hstack((la[:,0:cols/2], lb[:,cols/2:]))
    LS.append(ls)

# now reconstruct
ls_ = LS[0]
for i in xrange(1,6):
    ls_ = cv2.pyrUp(ls_)
    ls_ = cv2.add(ls_, LS[i])

# image with direct connecting each half
real = np.hstack((A[:,:cols/2],B[:,cols/2:]))

cv2.imwrite('Pyramid_blending2.jpg',ls_)
cv2.imwrite('Direct_blending.jpg',real)
相關文章
相關標籤/搜索