OpenCV 與 Matplotlib


OpenCV 內建了顯示圖片的功能,然而有時候,我們會想結合 Matplotlib 的功能來顯示圖片,然而若直接將 cv2.imread 傳回的陣列,透過 matplotlib.pyplotimshow 顯示,會出現色彩不正確的問題。例如:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg')
plt.imshow(img)
plt.show()

OpenCV 與 Matplotlib

在〈OpenCV 與 NumPy〉中談過,cv2.imread 傳回的物件具有 NumPy 陣列的行為,若是讀取了圖片的 RGB,軸 2 用來指定像素的 BGR 欄位索引,也就是 RGB 的相反,之所以會使用 BGR,是歷史性的原因,早期一些相機或硬體使用的就是 BGR,而 OpenCV 就這麼一直用到現在了。

為了能在 Matplotlib 顯示正確的色彩,必須將 BGR 轉為 RGB,方式之一是將 BGR 拆分出來,再以 RGB 順序合併,這可以分別透過 cv2splitmerge 達到:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg')
b, g, r = cv2.split(img)
plt.imshow(cv2.merge([r, g, b]))
plt.show()

或者是透過 cv2.cvtColor 指定 cv2.COLOR_BGR2RGB,就可以專 BGR 轉為 RGB:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg')
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

如果你對〈NumPy 陣列索引〉夠熟悉,也可以透過範圍索引的方式來分離 BGR:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg')
b = img[:,:,0]
g = img[:,:,1]
r = img[:,:,2]
plt.imshow(cv2.merge([r, g, b]))
plt.show()

既然如此,在最後一個範圍索引處,直接透過 -1 來反轉不就好了?

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg')
plt.imshow(img[:,:,::-1])
plt.show()

以上四個範例,都可以得到正確的色彩顯示:

OpenCV 與 Matplotlib

如果是用 OpenCV 以灰階方式讀入圖片,因為元素只有灰階值,應該就沒有什麼 BGR 轉 RGB 的問題了吧?!是沒錯,不過有另一個小問題:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg', cv2.IMREAD_GRAYSCALE)
plt.imshow(img)
plt.show()

這會顯示以下的結果:

OpenCV 與 Matplotlib

這是因為不指定 imshowcmap 的話,會使用 rcParams["image.cmap"] 作為預設,自行指定 cmapgray 就可以了:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('caterpillar.jpg', cv2.IMREAD_GRAYSCALE)
plt.imshow(img, cmap = 'gray')
plt.show()

OpenCV 與 Matplotlib