python3使用cv2對圖像進行基本操作


技術背景

在機器視覺等領域,最基本的圖像處理處理操作,可以通過opencv這個庫來實現。opencv提供了python的接口,所需安裝的庫為opencv-python,但是在庫的導入的時候一般用的是import cv2,因此很多也把opencv-python簡稱為cv2

cv2的安裝

如果是使用anaconda所搭建的python的編程環境,一般會事先安裝好cv2這個倉庫。在上面的超鏈接中可以找到適合自己本地環境的anaconda環境進行安裝,這是一個非常常用的python包集成管理工具,其中預安裝了很多python庫,使得我們不需要去手動安裝各種的第三方庫,我們知道自己取手動安裝的過程中,很容易就會遇到一些報錯,解決起來也非常的麻煩。

如果系統中沒有這個庫,可以通過pip來進行安裝和管理:

[dechin@dechin-manjaro cv2]$ python3 -m pip install opencv-python
Requirement already satisfied: opencv-python in /home/dechin/anaconda3/lib/python3.8/site-packages (4.5.1.48)
Requirement already satisfied: numpy>=1.17.3 in /home/dechin/anaconda3/lib/python3.8/site-packages (from opencv-python) (1.20.1)

需要注意的是,這里雖然安裝的時候是使用opencv-python這個名字,但是在python代碼中調用的時候是用的cv2這個名字:

[dechin@dechin-manjaro cv2]$ ipython
Python 3.8.5 (default, Sep  4 2020, 07:30:14) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import cv2

In [2]: quit()

cv2基本圖像操作

首先假定我們已經獲取了這么一個圖片,接下來我們要對這個圖片進行各式各樣的處理(圖片來自於參考鏈接1):

重構大小

我們可以對輸入的圖片進行大小調整,由於大小被改變,因此會涉及到一些插值算法。cv2內置的有線性插值和最近鄰插值等,我們可以直接使用:

# cv2_reshape.py

import cv2
import numpy as np

width = 400
height = 200
img = cv2.imread('test.png') # 讀取圖像
print ('The shape of initial graph is: {}'.format(img.shape)) # 打印原圖大小
img = cv2.resize(img, (width, height), interpolation=cv2.INTER_NEAREST) # 最近鄰插值縮放
print ('The changed shape of graph is: {}'.format(img.shape)) # 打印更改后圖片大小
cv2.imwrite('new_logo.png', img) # 保存圖片

在這個案例中,我們首先讀取了一個516×254的圖片,由於是RGB格式的,因此會有三層圖像。然后通過cv2將該圖像重構成一個400×200的圖像。上述代碼的執行結果如下:

[dechin@dechin-manjaro cv2]$ python3 cv2_reshape.py 
The shape of initial graph is: (254, 516, 3)
The changed shape of graph is: (200, 400, 3)

同時會在當前的目錄下生成一個新的圖像,這個圖像就是經過我們縮放重構之后的圖像:

圖像翻轉

圖像的翻轉也是一種常用的基本操作,cv2里面提供了三種模式的翻轉:編碼為1的橫向翻轉,編碼為0的縱向翻轉,以及編碼為-1的同時翻轉,這里我們演示其中的一種縱向翻轉:

# cv2_rotate.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of initial graph is: {}'.format(img.shape))
img = cv2.flip(img, 0)
print ('The changed shape of graph is: {}'.format(img.shape))
cv2.imwrite('rotate_logo.png', img)

執行完成后,因為是翻轉操作,所以並不會影響圖像大小:

[dechin@dechin-manjaro cv2]$ python3 cv2_rotate.py 
The shape of initial graph is: (254, 516, 3)
The changed shape of graph is: (254, 516, 3)

同樣的,會在當前目錄下生成一個翻轉之后的圖像:

灰度圖

在很多圖像特征提取的場景中,其實並不需要RGB配色。比如我們判斷一個圖片中的動物是貓還是狗,這跟貓和狗身上的顏色並沒有太大的關系,因此我們需要一個灰度圖就夠了:

# cv2_color.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of initial graph is: {}'.format(img.shape))
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
print ('The changed shape of graph is: {}'.format(img.shape))
cv2.imwrite('gray_logo.png', img)

因為提取的灰度圖並沒有顯含RGB的配色,因此得到的圖片沒有3層,只有1層:

[dechin@dechin-manjaro cv2]$ python3 cv2_color.py 
The shape of initial graph is: (254, 516, 3)
The changed shape of graph is: (254, 516)

同時在本地目錄下會生成一個新的灰度圖:

卷積與滑窗

卷積操作在卷積神經網絡中有重要的應用,其本質是通過滑窗的方式,對原本的圖像進行小范圍內的指定操作,而這個小范圍內的指定操作,則是由卷積核來定義的。我們先來看一下三個卷積核的使用案例,這些卷積核的作用是進行邊緣檢測。並且這三個卷積核都是3×3的大小,也就是說,原圖像經過卷積核操作之后,在橫向和縱向兩個維度的大小都會減去2。

# convolution.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of input img is: {}'.format(img.shape))

conv_img = np.zeros((int(img.shape[0])-2,
                        int(img.shape[1])-2,
                        int(img.shape[2])))
for i in range(int(img.shape[0])-2):
    for j in range(int(img.shape[1])-2):
        conv_img[i][j] = img[i][j] - img[i+2][j] - img[i][j+2] + img[i+2][j+2]

print ('The shape of output img is: {}'.format(conv_img.shape))
cv2.imwrite('conv.png', conv_img)

這個案例所對應的卷積核為:

\[\left[ \begin{matrix} 1&0&-1\\ 0&0&0\\ -1&0&1 \end{matrix} \right] \]

執行結果如下:

[dechin@dechin-manjaro cv2]$ python3 convolution.py 
The shape of input img is: (254, 516, 3)
The shape of output img is: (252, 514, 3)

我們可以看到圖像的最終大小是符合我們所預期的,再看看生成的圖像:

我們可以明顯的發覺,原本圖像中一些不太重要的信息就被忽略了,僅保留了一些邊緣的信息。那么在一些圖像特征識別的場景下,就可以先用卷積層轉換成這種邊緣圖像,再結合池化層和潛藏層構成一個卷積神經網絡,對圖像進行分辨和識別。由於卷積核並不是唯一固定的,因此我們可以對比以下另外兩種卷積核:

# convolution1.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of input img is: {}'.format(img.shape))

conv_img = np.zeros((int(img.shape[0])-2,
                        int(img.shape[1])-2,
                        int(img.shape[2])))
for i in range(int(img.shape[0])-2):
    for j in range(int(img.shape[1])-2):
        conv_img[i][j] = img[i][j+1] + img[i+1][j] + img[i+2][j+1] + img[i+1][j+2] - 4*img[i+1][j+1]

print ('The shape of output img is: {}'.format(conv_img.shape))
cv2.imwrite('conv1.png', conv_img)

這個案例所對應的卷積核為:

\[\left[ \begin{matrix} 0&1&0\\ 1&-4&1\\ 0&1&0 \end{matrix} \right] \]

執行結果如下:

[dechin@dechin-manjaro cv2]$ python3 convolution1.py 
The shape of input img is: (254, 516, 3)
The shape of output img is: (252, 514, 3)

得到的新圖像與第一種卷積核有顯著的差異:

再看看另外一種卷積和:

# convolution2.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of input img is: {}'.format(img.shape))

conv_img = np.zeros((int(img.shape[0])-2,
                        int(img.shape[1])-2,
                        int(img.shape[2])))
for i in range(int(img.shape[0])-2):
    for j in range(int(img.shape[1])-2):
        conv_img[i][j] = -img[i][j] - img[i][j+1] - img[i][j+2] -\
                          img[i+1][j] + 8*img[i+1][j+1] - img[i+1][j+2] -\
                          img[i+2][j] - img[i+2][j+1] - img[i+2][j+2]

print ('The shape of output img is: {}'.format(conv_img.shape))
cv2.imwrite('conv2.png', conv_img)

這個案例所對應的卷積核為:

\[\left[ \begin{matrix} -1&-1&-1\\ -1&8&-1\\ -1&-1&-1 \end{matrix} \right] \]

執行結果如下所示:

[dechin@dechin-manjaro cv2]$ python3 convolution2.py 
The shape of input img is: (254, 516, 3)
The shape of output img is: (252, 514, 3)

最終生成的圖像與前兩種卷積和都截然不同:

最后還要介紹一種可以銳化圖像的卷積核,與前面介紹的邊緣檢測的卷積核不同的是,銳化的卷積核保留了大部分的圖像特征,只是更加顯著的突出了圖像的的邊緣:

# convolution3.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of input img is: {}'.format(img.shape))

conv_img = np.zeros((int(img.shape[0])-2,
                        int(img.shape[1])-2,
                        int(img.shape[2])))
for i in range(int(img.shape[0])-2):
    for j in range(int(img.shape[1])-2):
        conv_img[i][j] = - img[i][j+1] - img[i+1][j] - img[i+2][j+1] - img[i+1][j+2] + 5*img[i+1][j+1]

print ('The shape of output img is: {}'.format(conv_img.shape))
cv2.imwrite('conv3.png', conv_img)

這個案例所對應的卷積核為:

\[\left[ \begin{matrix} 0&-1&0\\ -1&5&-1\\ 0&-1&0 \end{matrix} \right] \]

執行結果如下:

[dechin@dechin-manjaro cv2]$ python3 convolution3.py 
The shape of input img is: (254, 516, 3)
The shape of output img is: (252, 514, 3)

可以看到跟其他其中卷積核相比,銳化的卷積核是最接近於原始圖像的:

在上述的幾個輸出圖像中,我們可以大致評估,第一種卷積邊緣檢測的方法有效的去除了很多無用的背景信息,可以在這種類型下的圖像中進行使用,我們可以針對不同的場景選擇不同的操作。

平均池化

在上面所介紹的卷積核中,我們使用的滑窗步長都是1,但是在實際場景中,增大滑窗的步長不僅可以達到很好的效果,還可以很大程度上介紹需要處理的圖像的大小。這里介紹的池化,可以認為是一種特殊的卷積運算。常用的池化方法有最大池化和平均池化,顧名思義,最大池化就是取卷積/池化區域中的最大值,而平均池化則是取平均值。這里我們展示一個平均池化的示例:

# avg_pooling.py

import cv2
import numpy as np

img = cv2.imread('test.png')
print ('The shape of input img is: {}'.format(img.shape))

pooling_img = np.zeros((int(int(img.shape[0])/2),
                        int(int(img.shape[1])/2),
                        int(img.shape[2])))
for i in range(int(int(img.shape[0])/2)):
    for j in range(int(int(img.shape[1])/2)):
        pooling_img[i][j] = (img[2*i][2*j] + img[2*i][2*j+1] + img[2*i+1][2*j] + img[2*i+1][2*j+1])/4

print ('The shape of output img is: {}'.format(pooling_img.shape))
cv2.imwrite('pooling.png', pooling_img)

該平均池化如果用卷積核來表示的化,大概是如下的形式:

\[\left[ \begin{matrix} 0.25&0.25\\ 0.25&0.25 \end{matrix} \right] \]

上述代碼的輸出結果如下:

[dechin@dechin-manjaro cv2]$ python3 avg_pooling.py 
The shape of input img is: (254, 516, 3)
The shape of output img is: (127, 258, 3)

我們發現由於這里的滑窗步長設置為了2,滑窗大小變為了2×2,因此得到的結果圖像縮小了一半。最終得到的池化的圖像如下:

在這個池化的圖片中我們其實並沒有得到太多的信息,更多的作用還是等效的去壓縮一個圖像的信息,尤其是最大池化,可以很好的保留原圖像中的顯著特征。

總結概要

本文介紹了使用opencv-python對輸入圖像進行處理的基本操作,包括圖像讀取、圖像變換等。有了這些基礎的操作支撐后,我們可以執行跟高層次的圖像處理,比如常用於深度學習的卷積和池化操作,這里我們也作了簡單介紹,並給出了使用示例。

版權聲明

本文首發鏈接為:https://www.cnblogs.com/dechinphy/p/cv2.html
作者ID:DechinPhy
更多原著文章請參考:https://www.cnblogs.com/dechinphy/

參考鏈接

  1. http://qutip.org/docs/latest/index.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM