OpenCV Python教程(2、图像元素的访问、通道分离与合并)

访问像素

像素的访问和访问numpy中ndarray的方法完全一样,灰度图为

i = int(np.random.random() * img.shape[1])
j = int(np.random.random() * img.shape[0])
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 numpy as np
import cv2

def salt(img,n):
	
	# 从k = 0到k = n,默认以步长1
	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(r'C:\contenpic\2018\09\18\20180918145129_1537248219323a8e8f817e2.jpg')
	salt_img = salt(img,500)
	cv2.imshow('image',img)
	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(r'C:\contenpic\2018\09\18\20180918145129_1537248219323a8e8f817e2.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)[2]

也可以直接操作NumPy数组来达到这一目的:

import cv2
import numpy as np

img = cv2.imread(r'C:\contenpic\2018\09\18\20180918145129_1537248219323a8e8f817e2.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()

通道合并

同样,通道合并也有两种方法。第一种是OpenCV自带的merge函数,如下:

merged = cv2.merge([b,g,r]) #前面分离出来的三个通道

接着是NumPy的方法:

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

注意:这里只是演示,实际使用时请用OpenCV自带的merge函数!wenti 

问题 用NumPy组合的结果不能在OpenCV中其他函数使用,因为其组合方式与OpenCV自带的不一样?

但我在测试中得到的结果是一致的

测试结果如下:

import cv2
import numpy as np
img = cv2.imread(r'C:\contenpic\2018\09\18\20180918145131_1536915880571738161b1a1.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)
merged = cv2.merge([b,g,r]) #前面分离出来的三个通道


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

结果为

Merge by OpenCV (1185, 3, 1)
Merge by NumPy  (1185, 3, 1)

NumPy数组的strides属性表示的是在每个维数上以字节计算的步长。这怎么理解呢,看下面这个简单点的例子:

a = np.arange(4)
print(a)
#结果:[0 1 2 3 4 5]
#a.strides
print(a.strides)
# 结果(4,)

a数组中每个元素都是NumPy中的整数类型,占4个字节,所以第一维中相邻元素之间的步长为4(个字节)。

同样,2维数组如下:

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

从里面开始看,里面是一个4个元素的一维整数数组,所以步长应该为4。外面是一个含有3个元素,每个元素的长度是4×4=16。所以步长为16。

下面来看下3维数组:

c = np.arange(27).reshape(3,3,3)
print(c)
#结果[[[ 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]]]
print(c.strides)
# 结果(36, 12, 4)

根据前面了解的,推断下这个数组的步长。从里面开始算,应该为(3×4×3,3×4,4)。验证一下:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值