OpenCV3 for python3 學習筆記3-----用OpenCV3處理圖像一

  本文的內容都與圖像處理有關,這時須要修改圖像,好比要使用具備藝術性的濾鏡、外插(extrapolate)某些部分、分割、粘貼或其餘須要的操做。算法

  一、不一樣色彩空間的的轉換

    OpenCV有數百種關於在不一樣色彩空間之間轉換的方法。當前,計算機視覺中有三種經常使用的色彩空間:灰度、BGR以及HSV(Hue,Saturation,Value)。數組

    灰度色彩空間是經過去除彩色信息來將其轉換成灰階,灰度色彩空間對中間處理特別有效,好比人臉檢測。app

    BGR,即藍-綠-紅色彩空間,每個像素點都由一個三元數組來表示,分別表明藍、綠、紅三種顏色。函數

    HSV,H(Hue)是色調、S(Saturation)是飽和度、V(Value)表示黑暗的程度(或光譜另外一端的明亮程度)。性能

    BGR的簡短說明spa

    當第一次處理BGR色彩空間的時候,能夠不要其中的一個色彩份量,好比像素值(0,255,255)(沒有藍色,綠色份量取最大值,紅色份量取最大值)表示黃色,綠色和紅色混合產生渾濁的褐色,code

  這是由於計算所使用的顏色模型具備可加性而且處理的是光照,而繪畫不是這樣(它聽從建色模型(subtractive color model))。計算機使用顯示器發光來作顏色的媒介,orm

  所以運行在計算機的軟件所使用的色彩模型是加色模型。視頻

  二、佛裏葉變換

    在OpenCV中,對圖像和視頻的大多數處理都或多或少會涉及佛裏葉變換的概念。Joseph Fourier(約瑟夫.佛裏葉)是一位18實際的法國數學家,他發現並推廣了不少數學概念,對象

  主要研究熱學規律,在數學上,他認爲一切均可以用波形來描述。具體而言,他觀察到的全部波形均可以由一系列簡單其頻率不一樣的正弦曲線疊加獲得。也就是說,

  人們看到的波形都是由其它波形疊加獲得的。這個概念對操做圖像很是有幫助,由於這樣咱們就能夠區分圖像裏哪些區域的信號(好比圖像像素)變化特別強,哪些區域的信號變化不那麼強,

  從而能夠任意地標記噪聲區域、感興趣區域、前景和背景等。原始圖像有許多頻率組成,人們可以分離這些頻率來處理圖像和提取感興趣的數據。

    注意:在OpenCV環境中,有許多實現了算法讓咱們可以處理圖像,理解圖像中所包含的含義。這些算法在 NumPy中也有實現,並且更容易使用。NumPy有快速佛裏葉變換(FFT)的包,

  它包含了fft2()函數,此函數能夠計算一副圖像的離散佛裏葉變換(DFT)。

    下面經過介紹佛裏葉變換來介紹圖像的幅度譜(magnitude spectrum)。圖像的幅度譜是另外一種圖像,幅度譜圖像呈現了原始圖像在變化方面的一種表示:把一副圖像中最明亮的像素放到圖中央,

  而後逐漸變暗,在邊緣的像素最暗。這樣能夠發現圖像中有多少亮的像素和暗的像素,以及他們分佈的百分比。

    佛裏葉變換的概念是許多常見的圖像處理操做的基礎,好比邊緣檢測或線段的和形狀檢測。

    下面先介紹兩個概念:高通濾波器和低通濾波器,上面提到那些操做都是以這兩個概念和佛裏葉變換爲基礎。

  2.一、高通濾波器

    高通濾波器(High-pass filter:HPF)是監測圖像的某個區域,而後根據像素與周圍像素的亮度差值來提示(boost)該像素的亮度的濾波器。

    以以下的核(kernel)爲例:

    注意:核是指一組權重的結合,它會應用在源圖像的一個區域,並由此產生目標圖像的一個像素。好比,大小爲7的核意味着每49(7*7)個源圖像的像素會產生目標圖像的一個像素。

  能夠把核看做一塊覆蓋在源圖像上能夠移動的毛玻璃片,玻璃片覆蓋區域的光線會按某種方式進行擴散混合後投過去。

    在計算完中央像素和周圍臨近像素的亮度差值之和之後,若是亮度變化很大,中央像素的亮度會增長(反之則不會)。換句話說,若是一個像素比它周圍的像素突出,就會提示它的高度。

  這在邊緣檢測上尤爲有效,它會採用一種稱爲高頻提示濾過器(high boost filger)的高通濾波器。

    高通和低通濾波器都有一個稱爲半徑(radius)的屬性,它決定了多大面積的臨近像素參與濾波運算。

    下面是一個高通濾波器的例子:

import cv2
import numpy as np
import os
from scipy import ndimage

kernal_3x3 = np.array([[-1, -1, -1],
                       [-1, 8, -1],
                       [-1, -1, -1]])
kernal_5x5 = np.array([[-1, -1, -1, -1, -1],
                       [-1, 1, 2, 1, -1],
                       [-1, 2, 4, 2, -1],
                       [-1, 1, 2, 1, -1],
                       [-1, -1, -1, -1,-1]])
# 注意這些濾波器裏面的值加起來等於0,之後會解釋這個緣由
# 使用函數cv2.imread() 讀入圖像。這幅圖像應該在此程序的工做路徑,或者給函數提供完整路徑,第二個參數是要告訴函數應該如何讀取這幅圖片。

#    • cv2.IMREAD_COLOR:讀入一副彩色圖像。圖像的透明度會被忽略,這是默認參數。
  #    • cv2.IMREAD_GRAYSCALE:以灰度模式讀入圖像

img = cv2.imread('flower.jpg',0)       # 注:此處後面要加上0,表示已灰度模式讀入圖像
k3 = ndimage.convolve(img, kernal_3x3)  # 注:使用ndimage.convolve()時,濾波核的維度應與原始圖像的維度相同,故此採用灰度圖
k5 = ndimage.convolve(img, kernal_5x5)

blurred = cv2.GaussianBlur(img, (11, 11), 0)
g_hpf = img - blurred
cv2.imshow("flower", img)
cv2.imshow("flower:3x3", k3)
cv2.imshow("flower:5x5", k5)
cv2.imshow("flower:g_hpf", g_hpf)
cv2.waitKey()
cv2.destroyAllWindows()

 

  運行效果圖以下:

  

    導入模塊之後,咱們定義了一個3*3和一個5*5的核,而後將讀入的圖像轉換爲灰度格式。一般大多數的圖像處理會用NumPy來完成,可是這裏的狀況比較特殊,

  由於須要一個給定的核與圖像進行「卷積」(convolve),可是NumPy碰巧只接受一維數組。可是並非說不能用NumPy完成多維數組的卷積運算,只是有些複雜。而ndimage(它是SciPy的

  一部分)的convolve()函數能夠解決這個問題,該函數支持經典的NumPy數組,cv2模塊用這種數組來存儲圖像。

  上面的代碼用了兩個自定義的卷積核來實現兩個高通濾波器。最後會用一週不一樣的方法來實現一個高通濾波器:經過對象圖像應用低通濾波器以後,與原始圖像計算差值。第三種方法獲得的效果最好。

 

  2.二、低通濾波器

     高通濾波器是根據像素與周圍像素的亮度差值來提示該像素的亮度,低通濾波器(Low Pass Filter,LPF)則是在像素與周圍像素的亮度差值小於一個特定值時,平滑該像素的亮度。

  它主要用於去噪和模糊化,好比數,高斯模糊是最經常使用的模糊濾波器(平滑濾波器)之一,它是一個削弱高頻信號強度的低通濾波器。

  三、邊緣檢測

    邊緣在人類視覺和計算機視覺中均起着重要的的做用。人類可以憑藉一張背景剪影或一個草圖就識別出物體的類型和姿態。事實上,藝術強調邊緣和姿態,

  它們一般傳達了原型(archetype)的思想,好比Rodin的《思考者》和Joe Shuster的《超人》。軟件也同樣,它能夠推理出邊緣、姿態以及原型。

    OpenCV提供了許多邊緣檢測濾波函數,包括Laplacian()、Sobel()以及Scharr()。這些濾波函數都會將邊緣區域轉爲黑色,將邊緣區域轉爲白色或其它飽和的顏色。可是,

  這些函數很容易將噪聲錯誤地識別爲邊緣。緩解這個問題的辦法是在找到邊緣以前對圖像進行模糊處理。OpenCV也提供了許多模糊濾波函數,包括blur()(簡單是算術平均),medianBlur()以及

  GaussianBlur()。邊緣檢測函數和模糊濾波函數有不少參數,但總會有一個ksize參數,它是一個奇數,表示高濾波的寬和高(以像素爲單位)。

    這裏使用medianBlur()做爲模糊函數,它對去除數字化的視頻噪聲很是有效,特別是去除彩色圖像的噪聲;使用Laplacian()做爲邊緣檢測函數,它會產生明顯的邊緣線條,

  灰度圖像更是如此。在使用medianBlur()函數以後,將要使用Laplacian()函數以前,須要將圖像從BGR色彩空間灰度色彩空間。

    在獲得Laplacian()函數的結果以後,須要將其轉換成黑色邊緣和黑色背景的圖像。而後將其歸一化(使它的像素值在0到1之間),並乘以源圖像以便將邊緣變黑。

    代碼實現以下:

def strokeEdges(src,dst,blurKsize = 7,edgeKsize = 5):
    if blurKsize >= 3:
        blurredSrc = cv2.medianBlur(src,blurKsize)
        graySrc =  cv2.cvtColor(blurredSrc, cv2.COLOR_BGR2GRAY)
    else:
        graySrc = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    cv2.Laplacian(graySrc,cv2.CV_8U,graySrc,ksize=edgeKsize)
    normalizedInverseAlpha = (1.0/255)*(255-graySrc)
    channels =  cv2.split(src)
    for channel in channels:
        channel[:] = channel * normalizedInverseAlpha
    cv2.merge(channels,dst)

 

    注意,核的大小可由strokeEdges()函數的參數來指定。 blurKsize參數會做爲medianBlur()函數的ksize參數,edgeKsize參數會做爲Laplacian()函數的ksize參數。這裏將blurKsize值設爲7,

  將edgeKsize值設爲5會獲得更好的效果,不幸的是,對於較大的ksize(好比7),使用medianBlur()的代價很高。

  提示,若是你在使用strokeEdges()時遇到性能問題,能夠試着減小blurKsize的值,要關閉模糊效果,能夠將blurKsize的值設爲3如下。

  四、用定製內核作卷積

    OpenCV預約義的許多濾波器(濾波函數)都使用核。其實核是一組權重,它決定如何經過臨近像素點來計算新的像素點。核也稱爲卷積矩陣,它對一個區域的像素作調和

  (mix up)或卷積運算,一般基於核的濾波器被稱爲卷積濾波器。

    OpenCV提供了一個很是通用的filter2D(),它運用由用戶指定的任意核或卷積矩陣。爲了理解這個函數的使用方法,首先來了解卷積矩陣的的格式。卷積矩陣是一個二維數組,

  有奇數行、奇數列,中心的元素對應於感興趣的像素,其它的元素對應於這個像素周圍的臨近像素,每一個元素都有一個整數或浮點數的值,這些值就是應用在像素上的權重。

kernel = numpy.array([[-1, -1 , -1],
                      [-1,  9,  -1],
                      [-1, -1, -1]])

  好比:上面實例在感興趣的像素權重是9,其臨近像素權重爲-1。對感興趣的像素來講,新像素值是用當前像素值乘以9,而後減去8個臨近像素值。若是感興趣的像素已經與臨近

  像素有一點差異,那麼這個差異會增長,這些會讓圖像銳化,由於該像素的值與臨近像素的之間的差距拉大了。注意權重加起來爲1,若是不想改變圖像的亮度就應該這樣。

  若是稍微修改一下銳化核使它的權重加起來爲0,就會獲得一個邊緣檢測核,把邊緣轉爲白色,把非邊緣區域轉爲黑色。

    

    在源圖像和目標圖像上分別使用卷積矩陣:cv2.filter2D(src, -1, kernel, dst). 第二個參數指定了目標圖像每一個通道的位深度(好比,位深度cv2.CV_8U表示每一個通道爲8位),

  若是爲負值,則表示目標圖像和源圖像有一樣的位深度。

 

    注:對彩色圖像來講,filter2D()會對每一個通道都用一樣的核。若是要對每一個通道使用不一樣的核,就必須用split()函數和merge()函數。

 

    對於模糊濾波器,爲了達到模糊效果,一般權重和應該爲1,並且鄰近像素的權重全爲正。

# VConvolutionFilter 表示通常的濾波器
class VConvolutionFilter(object):
    """A filter that applies a convolution to V(or all of BGR)."""

    def __init__(self, kernel):
        self._kernel = kernel

    def apply(self, src, dst):
        """Apply the filter with a BGR or gray source/destination."""
        cv2.filter2D(src, -1, self._kernel, dst)

# SharpenFilter 表示特定的銳化濾波器
class SharpenFilter(VConvolutionFilter):
    """A sharpen filter with a 1-pixel radius."""
    def __init__(self):
        kernel = numpy.array([[-1, -1, -1],
                              [-1, 9, -1],
                              [-1, -1, -1]])
        VConvolutionFilter.__init__(self, kernel)

# 邊緣檢測濾波器
class FindEdgesFilter(VConvolutionFilter):
    """A edge-finding filter with a 1-pixel radius."""
    def __init__(self):
        kernel = numpy.array([[-1, -1, -1],
                              [-1, 8, -1],
                              [-1, -1, -1]])
        VConvolutionFilter.__init__(self, kernel)

# 鄰近平均濾波器
class BlurFilter(VConvolutionFilter):
    """A edge-finding filter with a 1-pixel radius."""
    def __init__(self):
        kernel = numpy.array([[0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04]])
        VConvolutionFilter.__init__(self, kernel)

 

 

  銳化、邊緣檢測以及模糊等濾波器都是用了高度對稱的核。可是有時不對稱的核也會獲得一些有趣的效果。

class EmbossFilter(VConvolutionFilter):
    """A edge-finding filter with a 1-pixel radius."""

    def __init__(self):
        kernel = numpy.array([[-2, -1, 0],
                              [-1, 1, 1],
                              [0, 1, 2]])
        VConvolutionFilter.__init__(self, kernel)
相關文章
相關標籤/搜索