OpenCV_007-OpenCV 中的圖像基本操作

本文主要內容來自於 OpenCV-Python 教程核心操作 部分,這個部分的主要內容如下:

目標

學習:

  • 訪問像素值並修改它們
  • 訪問圖像屬性
  • 設置感興趣區域 (ROI)
  • 分割和合並圖像

本節中幾乎所有的操作都主要與 Numpy 有關,而不是 OpenCV。使用 OpenCV 編寫更好的優化的代碼需要良好的 Numpy 知識。

訪問及修改像素值

讓我們先加載一幅彩色圖像:

#!/use/bin/env python

import numpy as np
import cv2 as cv

if __name__ == "__main__":
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    filepath = cv.samples.findFile("messi5.jpg")
    img = cv.imread(filepath)

我們可以根據像素點的行和列座標訪問像素的值。對於 BGR 圖像,它返回一個藍色、綠色和紅色值的數組。對於灰度圖像,只返回相應的亮度。

    px = img[100, 100]
    print(px)
    print(px.__class__.__name__)

    blue = img[100, 100, 0]
    print(blue)

這幾行代碼對應的輸出如下:

[157 166 200]
ndarray
157

彩色圖像像素點的值是由一個 Numpy 的 ndarray 表示的。我們還可以以相同的方式修改像素值。

    img[100, 100] = [255, 255, 255]
    print(img[100, 100])

這幾行代碼對應的輸出如下:

[255 255 255]

警告 Numpy 是一個爲快速數組計算高度優化的庫。因此,簡單地訪問每個像素值並對其進行修改將非常緩慢,並且不鼓勵這樣做。

注意 上面的方法通常用於選擇數組的一個區域,比如開始的 5 行和最後的 3 列。對於單獨的像素訪問,Numpy 數組方法,array.item()array.itemset() 被認爲是更好的選擇。它們總是返回一個標量,然而,如果想要訪問所有的 B,G,R 值,則將需要爲每個值分別調用 array.item()

更好的像素訪問和編輯方法:

    # accessing RED value
    red_value = img.item(10, 10, 2)
    print(red_value)

    # modifying RED value
    img.itemset((10, 10, 2), 100)

    red_value = img.item(10, 10, 2)
    print(red_value)

這幾行代碼對應的輸出如下:

59
100

訪問圖像的屬性

圖像屬性包括行數、列數和通道數;圖像數據的類型;像素的個數等等。

一幅圖像的形狀可以通過 img.shape 訪問。它返回行數、列數和通道數的元組(如果圖像是彩色的):

    print(img.shape)

這行代碼對應的輸出如下:

(342, 548, 3)

注意 如果圖像是灰度圖,則返回的元組只包含行數和列數,因而這是一種檢查加載的圖像是灰度圖還是彩色圖的好方法。

像素值的總個數通過 img.size 訪問,它是行數、列數和通道數三者的乘積,而不是行數和列數兩者的乘積:

    print(img.size)
    totoal_pixels = img.shape[0] * img.shape[1] * img.shape[2]
    print(totoal_pixels)

這幾行代碼對應的輸出如下:

562248
562248

圖像的數據類型通過 img.dtype 獲取:

    print(img.dtype)

這行代碼對應的輸出如下:

uint8

注意 img.dtype 在調試時非常重要,因爲 OpenCV-Python 代碼中的大量錯誤都是由無效的數據類型引起的。

圖像 ROI

有時,我們將不得不使用某些圖像區域。對於圖像中的眼睛檢測,首先對整個圖像進行人臉檢測。當獲得人臉時,我們只選擇人臉區域並在其中搜索眼睛,而不是搜索整個圖像。它提高了精度(因爲眼睛總是在臉上:D)和性能(因爲我們在一個更小的區域內搜索)。

使用 Numpy 索引再次獲得 ROI。這裏我們選擇足球,並把它拷貝到圖像的另一個區域:

    ball = img[280:340, 330:390]
    img[273:333, 100:160] = ball

上面方括號中逗號前面的數字表示選取的區域的行的範圍,即區域的垂直方向的範圍,後面的數字表示選取的區域的列的範圍,即區域的水平方向的範圍。檢查結果如下:

分割和合並圖像通道

有時我們需要分別處理一幅圖像的 B,G,R 通道。在這種情況下,我們需要把 BGR 圖像分割爲單獨的通道。在其它情況下,我們可能需要合併這些單獨的通道,並創建 BGR 圖像。我們可以通過以下方式簡單地做到這一點:

    b, g, r = cv.split(img)
    img = cv.merge((b, r, g))

這裏有意沒有按照原來的數據格式合併數據,而是把所有像素點的綠色通道和紅色通道的值做了交換。此外,分割獲得的單個色彩通道的值可以作爲一幅灰度圖來繪製。

或者:

    b = img[:, :, 0]

假設我們想要把所有像素的紅色通道值都設置爲 0 —— 我們不需要先分割通道。Numpy 的索引更快:

    img[:, :, 2] = 0

警告 cv.split() 是一項代價高昂的操作(就時間而言)。因此只在需要的時候使用它。否則使用 Numpy 的索引。

爲圖像製作邊框(填充)

如果要在圖像周圍創建邊框,例如相框,可以使用 cv.copyMakeBorder()。 但它在卷積運算、零填充等方面有更多應用。此函數接收以下參數:

  • src - 輸入圖像

  • topbottomleftright - 相應方向上邊框的以像素爲單位的寬度。

  • borderType - 定義了添加何種邊框的標記。它可以是以下類型:

  • value - 如果邊框類型是 cv.BORDER_CONSTANT 這個是邊框的顏色

下面這段代碼演示了所有這些邊框類型,以使我們獲得更好的理解。

def border_type():
    BLUE = [255, 0, 0]

    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    filepath = cv.samples.findFile("opencv-logo.png")
    img1 = cv.imread(filepath)

    replicate = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_REPLICATE)
    reflect = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_REFLECT)
    reflect101 = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_REFLECT_101)
    wrap = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_WRAP)
    constant = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_CONSTANT, value=BLUE)

    isolated = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_ISOLATED)

    plt.subplot(231), plt.imshow(img1, 'gray'), plt.title('ORIGINAL')
    plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
    plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')

    plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
    plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
    plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')

    plt.subplots_adjust(wspace=0.4, hspace=0.4)

    plt.show()

這裏用到的示例圖像文件同樣在 OpenCV 的示例數據中,因而先查找這個文件的完整路徑並加載。圖像由 matplotlib 顯示。因而 RED 和 BLUE 通道將會被交換 。用 matplotlib 畫圖時,爲了防止不同圖之間相互遮蓋,這裏通過 plt.subplots_adjust(wspace=0.4, hspace=0.4) 對子圖做了一些調整。

來看下最終的結果:

Image

參考文檔

Basic Operations on Images

Done.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章