OpenCV Python教程(2、圖像元素的訪問、通道分離與合並)


OpenCV Python教程之圖像元素的訪問、通道分離與合並

轉載請詳細注明原作者及出處,謝謝!

訪問像素

像素的訪問和訪問numpy中ndarray的方法完全一樣,灰度圖為:

img[j,i] = 255

其中j,i分別表示圖像的行和列。對於BGR圖像,為:

img[j,i,0]= 255
img[j,i,1]= 255
img[j,i,2]= 255

第三個數表示通道。

 

下面通過對圖像添加人工的椒鹽現象來進一步說明OpenCV Python中需要注意的一些問題。完整代碼如下:

import cv2
import numpy as np

def salt(img, n):
	for k in range(n):
		i = int(np.random.random() * img.shape[1]);
		j = int(np.random.random() * img.shape[0]);
		if img.ndim == 2: 
			img[j,i] = 255
		elif img.ndim == 3: 
			img[j,i,0]= 255
			img[j,i,1]= 255
			img[j,i,2]= 255
	return img

if __name__ == '__main__':
	img = cv2.imread("圖像路徑")
	saltImage = salt(img, 500)
	cv2.imshow("Salt", saltImage)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

處理后能得到類似下面這樣帶有模擬椒鹽現象的圖片:

 



上面的代碼需要注意幾點:

1、與C++不同,在Python中灰度圖的img.ndim = 2,而C++中灰度圖圖像的通道數img.channel() =1

2、為什么使用np.random.random()?
這里使用了numpy的隨機數,Python自身也有一個隨機數生成函數。這里只是一種習慣,np.random模塊中擁有更多的方法,而Python自帶的random只是一個輕量級的模塊。不過需要注意的是np.random.seed()不是線程安全的,而Python自帶的random.seed()是線程安全的。如果使用隨機數時需要用到多線程,建議使用Python自帶的random()和random.seed(),或者構建一個本地的np.random.Random類的實例。

分離、合並通道

由於OpenCV Python和NumPy結合的很緊,所以即可以使用OpenCV自帶的split函數,也可以直接操作numpy數組來分離通道。直接法為:

import cv2
import numpy as np

img = cv2.imread("D:/cat.jpg")
b, g, r = cv2.split(img)
cv2.imshow("Blue", r)
cv2.imshow("Red", g)
cv2.imshow("Green", b)
cv2.waitKey(0)
cv2.destroyAllWindows()

其中split返回RGB三個通道,如果只想返回其中一個通道,可以這樣:

b = cv2.split(img)[0]
g = cv2.split(img)[1]
r = cv2.split(img)[1]

最后的索引指出所需要的通道。

 

也可以直接操作NumPy數組來達到這一目的:

import cv2
import numpy as np

img = cv2.imread("D:/cat.jpg")

b = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)
g = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)
r = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)

b[:,:] = img[:,:,0]
g[:,:] = img[:,:,1]
r[:,:] = img[:,:,2]

cv2.imshow("Blue", r)
cv2.imshow("Red", g)
cv2.imshow("Green", b)
cv2.waitKey(0)
cv2.destroyAllWindows()

注意先要開辟一個相同大小的圖片出來。這是由於numpy中數組的復制有些需要注意的地方,具體事例如下:

>>> c= np.zeros(img.shape, dtype=img.dtype)
>>> c[:,:,:] = img[:,:,:]
>>> d[:,:,:] = img[:,:,:]
>>> c is a
False
>>> d is a
False
>>> c.base is a
False
>>> d.base is a #注意這里!!!
True

這里,d只是a的鏡像,具體請參考《 NumPy簡明教程(二,數組3)》中的“復制和鏡像”一節。

通道合並

同樣,通道合並也有兩種方法。第一種是OpenCV自帶的merge函數,如下:

merged = cv2.merge([b,g,r]) #前面分離出來的三個通道

接着是NumPy的方法:

mergedByNp = np.dstack([b,g,r]) 

注意:這里只是演示,實際使用時請用OpenCV自帶的merge函數!用NumPy組合的結果不能在OpenCV中其他函數使用,因為其組合方式與OpenCV自帶的不一樣,如下:

merged = cv2.merge([b,g,r])
print "Merge by OpenCV" 
print merged.strides

mergedByNp = np.dstack([b,g,r]) 
print "Merge by NumPy " 
print mergedByNp.strides

結果為:

Merge by OpenCV
(1125, 3, 1)
Merge by NumPy
(1, 500, 187500)

NumPy數組的strides屬性表示的是在每個維數上以字節計算的步長。這怎么理解呢,看下面這個簡單點的例子:

>>> a = np.arange(6)
>>> a
array([0, 1, 2, 3, 4, 5])
>>> a.strides
(4,)

a數組中每個元素都是NumPy中的整數類型,占4個字節,所以第一維中相鄰元素之間的步長為4(個字節)。

 

同樣,2維數組如下:

>>> b = np.arange(12).reshape(3,4)
>>> b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> b.strides
(16, 4)

從里面開始看,里面是一個4個元素的一維整數數組,所以步長應該為4。外面是一個含有3個元素,每個元素的長度是4×4=16。所以步長為16。

 

下面來看下3維數組:

>>> c = np.arange(27).reshape(3,3,3)

其結果為:

array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8]],

       [[ 9, 10, 11],
        [12, 13, 14],
        [15, 16, 17]],

       [[18, 19, 20],
        [21, 22, 23],
        [24, 25, 26]]])

根據前面了解的,推斷下這個數組的步長。從里面開始算,應該為(3×4×3,3×4,4)。驗證一下:

>>> c.strides
(36, 12, 4)

完整的代碼為:

import cv2
import numpy as np

img = cv2.imread("D:/cat.jpg")

b = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)
g = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)
r = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)

b[:,:] = img[:,:,0]
g[:,:] = img[:,:,1]
r[:,:] = img[:,:,2]

merged = cv2.merge([b,g,r])
print "Merge by OpenCV" 
print merged.strides
print merged

mergedByNp = np.dstack([b,g,r]) 
print "Merge by NumPy " 
print mergedByNp.strides
print mergedByNp

cv2.imshow("Merged", merged)
cv2.imshow("MergedByNp", merged)
cv2.imshow("Blue", r)
cv2.imshow("Red", g)
cv2.imshow("Green", b)
cv2.waitKey(0)
cv2.destroyAllWindows()


未完待續...

 

轉載請詳細注明原作者及出處,謝謝!






 



免責聲明!

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



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