建议阅读时长 15 分钟
OpenCV 中的通道存储为 BGR
学习目标:
这一节主要大部分涉及 Numpy 库的使用
1import cv2
2import numpy as np
1# 加载一个彩色图
2img = cv2.imread("img.jpg")
3img.shape
1(150, 220, 3)
1px = img[100, 100]
2print(px)
3blue = img[100, 100, 0]
4print(blue)
1[ 85 180 173]
285
1# 修改特定的像素
2img[100, 100] = [255, 255, 255]
3print(img[100, 100])
4
5# 这种修改每个像素的做法效率很低
1[255 255 255]
最好使用下面这种方法
1# 获取 RED 通道值
2img.item(10, 10, 1)
3
4# 修改
5img.itemset((10, 10, 2), 100)
6img.item(10, 10, 2)
1100
若是单通道的像素,像素有 256(0-255)个值,若是三通道,则颜色数就更多(一千六百多万种),如此多的颜色进行处理,会对算法的性能造成影响。这些颜色中,有代表性的颜色只是小部分。
颜色空间缩减(color space reduction)可以大大降低运算复杂度,具体做法是:
算法实现步骤:
1img = cv2.imread("img.jpg", 0) # img.shape = 150*220
2img_new = np.array([i for i in map(lambda x:(x//10)*10, img)])
3
4cv2.imshow("img", img)
5cv2.imshow("img_new", img_new)
6cv2.waitKey(0)
7cv2.destroyAllWindows()
8
9# 统计不同元素的个数
10np.unique(img),np.unique(img_new)
1(array([ 21, 26, 28, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43,
2 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
3 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
4 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
5 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
6 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
7 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
8 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
9 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
10 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
11 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
12 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
13 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
14 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
15 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
16 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
17 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
18 252, 253, 254, 255], dtype=uint8),
19 array([ 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140,
20 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250], dtype=uint8))
运行结果,从肉眼来看,差别不大
1img;
1img.shape
2# 若是彩色图像,则返回元组 1*3
3# 若是灰色图像,则返回元组 1*2 只有行和列
4# 此方法可以用来判断是否为彩色图像
1(150, 220)
1# 所有像素的总和即上面的元组值相乘
2img.size
133000
1# 图像的数据类型
2img.dtype
1dtype('uint8')
img.dtype 在调试时非常重要,因为大部分错误是因为无效的数据类型引起的
该部分的功能是对图像的一小部分区域进行处理(我们感兴趣的那部分),可以减少处理时间,增加精度,给图像处理带来便利。
1img = cv2.imread("messi5.jpg")
2cv2.imshow("image_init", img)
3ball = img[280:340, 330:390]
4img[273:333, 100:160] = ball
5cv2.imshow("image_roi", img)
6k = cv2.waitKey(0) & 0xFF
7if k == 27:
8 cv2.destroyAllWindows()
本例是对找到图像中球的位置,并复制一个放在另外一个地方,注意看的话,你会发现复制的球是一个矩形,看起来并不协调?
1# 拆分每个通道
2b,g,r = cv2.split(img) # 或者 b = img[:, :, 0]
3# 把各个通道合并起来
4img = cv2.merge((b,g,r))
1# 让红色通道置零,可以不用拆分红色通道,直接置零
2img[:, :, 2] = 0
cv2.split()
对系统来说开销很大,所以只在需要使用的时候再使用,使用 Numpy 索引的方法更有效
cv2.copyMakeBorder()
: 可以为图像创建边框它在 卷积运算,零填充等方面有更多的应用
1import cv2
2import numpy as np
3from matplotlib import pyplot as plt
4
5BLUE = [255, 0, 0]
6
7img1 = cv2.imread('opencv-logo.png')
8
9replicate = cv2.copyMakeBorder(
10 img1, 10, 10, 10, 10, cv2.BORDER_REPLICATE) # 最后一个元素的复制
11reflect = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_REFLECT) # 边框的镜面
12reflect101 = cv2.copyMakeBorder(
13 img1, 10, 10, 10, 10, cv2.BORDER_REFLECT_101) # 同上,但有细微变化
14wrap = cv2.copyMakeBorder(img1, 10, 10, 10, 10, cv2.BORDER_WRAP)
15# 添加一个固定颜色的边框,因为 matpltlib 和 opencv 颜色显示不一样,所有图标的红色和蓝色互换了
16constant = cv2.copyMakeBorder(
17 img1, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=BLUE)
18
19
20plt.subplot(231), plt.imshow(img1, 'gray'), plt.title('ORIGINAL')
21plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
22plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
23plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
24plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
25plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')
26
27plt.show()
函数说明
copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]]) -> dst
[本节原文] (https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_core/py_basic_ops/py_basic_ops.html#basic-ops)
学习目标:
一般的图像处理算子都是一个函数,它接受一个或多个输入图像,并产生输出图像。算子的一般形式:g(x)=h(f(x)) 或者
图像亮度和对比度的调整操作,属于图像操作中的点操作。点操作的特点:仅仅根据输入像素值(有时可加上某些全局信息或参数),来计算相应的输出像素值。这类算子包括:亮度(brightness)、对比度(contrast)调整、颜色校正(colorcorrection)、变换(transformations)。
常见的点操作(或说点算子)是乘上一个常数(对应对比度发调节)和加上一个常数(对应亮度的调节)。公式为:g(x)=a∗f(x)+b 各个参数的含义:
公式改写:g(i,j)=a∗f(i,j)+b 即对图像的 i 行 j 列的像素进行操作。(对所有的像素进行操作)
1import numpy as np
2import cv2
3from skimage import img_as_int
4
5
6def nothing(x):
7 pass
8
9# 对比度和亮度调节函数
10def adjust_bright_contrast(img, a=1, b=0):
11 new_img = np.zeros(img.shape, dtype=img.dtype)
12 for b in range(img.shape[0]):
13 for g in range(img.shape[1]):
14 for r in range(img.shape[2]):
15 new_img[b, g, r] = np.clip(a*img[b, g, r]+b, 0, 255)
16 return new_img
17
18# 使用轨迹条应该先创建一个窗口,因为
19cv2.namedWindow("image")
20cv2.createTrackbar("bright", "image", 0, 300, nothing)
21cv2.createTrackbar("contrast", "image", 0, 255, nothing)
22
23
24img = cv2.imread("img.jpg")
25new_img = np.zeros(img.shape, dtype=img.dtype)
26
27while(1):
28
29 cv2.imshow("image", new_img)
30 k = cv2.waitKey(1) & 0xFF
31 if k == 27:
32 break
33 brightness = cv2.getTrackbarPos("bright", "image")
34 contrast = cv2.getTrackbarPos("contrast", "image")
35 new_img[:] = adjust_bright_contrast(img, brightness*0.01, contrast)
36
37cv2.destroyAllWindows()
运行结果:
这个方式运行起来很慢,因为里面有三层循环
学习目标:
cv2.add()
使用该函数操作是 Numpy 操作,两个图片应该要有一样的数据类型和深度,否则第二个图像只能是标量值。
1x = np.uint8([250])
2y = np.uint8([10])
3z = np.uint8([255])
4print("opencv add operation:", cv2.add(x, y)) # 250+10 = 260 => 255
5print("opencv sub operation:", cv2.subtract(x, z)) # 250-255 = -5 => 0
6
7print("numpy add operation:", x+y) # 250+10 = 260 % 256 = 4
8print("numpy sub operation:", x-z) # 250-255 = -5 % 256 = 251
1opencv add operation: [[255]]
2opencv sub operation: [[0]]
3numpy add operation: [4]
4numpy sub operation: [251]
OpenCV 运算和 Numpy 运算有区别:OpenCV 是饱和运算,即相加最大只能是 255 ,相减最小只能是 0。Numpy 是模运算。见上面注释。
最好使用 OpenCV 中的 add 进行运算
cv2.addWeighted()
两张图片以权重进行融合,使其给人一种混合或透明的感觉。图片按以下公式运算
1img1 = cv2.imread("ml.png")
2img2 = cv2.imread("opencv-logo.png")
3
4dst = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)
5cv2.imshow('dst', dst)
6cv2.waitKey(0)
7cv2.destroyAllWindows()
addWeighted
运算公式为: dst=α∗img1+β∗img2+γ
轨迹条版本
1import cv2
2img1 = cv2.imread("ml.png")
3img2 = cv2.imread("opencv-logo.png")
4
5def nothing(x):
6 pass
7
8cv2.namedWindow("image")
9cv2.createTrackbar("alpha", "image", 0, 100, nothing)
10dst = np.zeros(img1.shape, dtype=img1.dtype)
11
12while(1):
13 cv2.imshow('image', dst)
14 alpha = cv2.getTrackbarPos("alpha", "image")
15 dst = cv2.addWeighted(img1, alpha*0.01, img2, 1 -alpha*0.01, 0)
16 k = cv2.waitKey(1) & 0xFF
17 if k == 27:
18 break
19
20cv2.destroyAllWindows()
包括 AND、OR、NOT 和 XOR 操作。它们在提取图像的任何部分、定义和处理非矩形 ROI 时非常有用。
1# 画矩形
2Rectangle = np.zeros((300, 300), dtype="uint8")
3cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
4cv2.imshow("Rectangle", Rectangle)
5cv2.waitKey(0)
6
7# 画圆形
8Circle = np.zeros((300, 300), dtype="uint8")
9cv2.circle(Circle, (150, 150), 150, 255, -1)
10cv2.imshow("Circle", Circle)
11cv2.waitKey(0)
12
13bit_and = cv2.bitwise_and(Rectangle, Circle)
14cv2.imshow("bit_and", bit_and)
15cv2.waitKey(0)
16
17"""
18sub = cv2.subtract(Rectangle, Circle)
19cv2.imshow("sub", sub)
20cv2.waitKey()
21"""
22
23cv2.destroyAllWindows()
24
25x = np.uint8([10])
26y = np.uint8([20])
27z = np.uint8([10])
28
29cv2.bitwise_and(x, y), cv2.bitwise_and(x, z)
1(array([[0]], dtype=uint8), array([[10]], dtype=uint8))
运行结果
1# 画矩形
2Rectangle = np.zeros((300, 300), dtype="uint8")
3cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
4cv2.imshow("Rectangle", Rectangle)
5cv2.waitKey(0)
6
7# 画圆形
8Circle = np.zeros((300, 300), dtype="uint8")
9cv2.circle(Circle, (150, 150), 150, 255, -1)
10cv2.imshow("Circle", Circle)
11cv2.waitKey(0)
12
13bit_or = cv2.bitwise_or(Rectangle, Circle)
14cv2.imshow("bit_or", bit_or)
15cv2.waitKey(0)
16
17
18add = cv2.add(Rectangle, Circle)
19cv2.imshow("add", add)
20cv2.waitKey(0)
21
22cv2.destroyAllWindows()
23x = np.uint8([10]) # 0000 1010
24y = np.uint8([20]) # 0001 0100
25z = np.uint8([40]) # 0010 1000
26
27print(cv2.bitwise_or(x, y)) # 0001 1110 => 30
28print(cv2.bitwise_or(x, z)) # 0010 1010 => 42
1[[30]]
2[[42]]
OR 运算和 cv2.add()
结果一致?
运行结果
1# 画矩形
2Rectangle = np.zeros((300, 300), dtype="uint8")
3cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
4cv2.imshow("Rectangle", Rectangle)
5cv2.waitKey(0)
6
7# 画圆形
8Circle = np.zeros((300, 300), dtype="uint8")
9cv2.circle(Circle, (150, 150), 150, 255, -1)
10cv2.imshow("Circle", Circle)
11cv2.waitKey(0)
12
13bit_xor = cv2.bitwise_xor(Rectangle, Circle)
14cv2.imshow("bit_xor", bit_xor)
15cv2.waitKey(0)
16cv2.destroyAllWindows()
运行结果
1# 画矩形
2Rectangle = np.zeros((300, 300), dtype="uint8")
3cv2.rectangle(Rectangle, (25, 25), (275, 275), 255, -1)
4cv2.imshow("Rectangle", Rectangle)
5cv2.waitKey(0)
6
7# 画圆形
8Circle = np.zeros((300, 300), dtype="uint8")
9cv2.circle(Circle, (150, 150), 150, 255, -1)
10cv2.imshow("Circle", Circle)
11cv2.waitKey(0)
12
13bit_not = cv2.bitwise_not(Rectangle, Circle)
14cv2.imshow("bit_not", bit_not)
15cv2.waitKey(0)
16cv2.destroyAllWindows()
运行结果
mask:图像掩模
1# 加载图片
2img1 = cv2.imread("messi5.jpg")
3img2 = cv2.imread("opencv-logo.png")
4
5print(img1.shape, img2.shape)
6
7# 把 OpenCV logo 放在图片的左上角,创建一个 ROI 大小和 logo 图像一致
8rows, cols, channels = img2.shape
9roi = img1[0:rows, 0:cols]
10print("roi.shape:", roi.shape)
11cv2.imshow("roi", roi)
12cv2.waitKey(0)
13
14# 灰度图
15img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
16cv2.imshow("img2gray", img2gray)
17cv2.waitKey(0)
18
19ret, mask = cv2.threshold(img2gray, 25, 255, cv2.THRESH_BINARY)
20# 解释:若图片 im2gray 的像素值小于 25,则像素置 0(黑),否则置 255(白)
21# ret :得到的阈值,mask:阈值化后的图像
22print("ret", ret)
23cv2.imshow("mask", mask)
24cv2.waitKey(0)
25
26mask_inv = cv2.bitwise_not(mask)
27# 把 mask 图像的白色像素变为黑色,黑色像素变为白色
28cv2.imshow("mask_inv", mask_inv)
29cv2.waitKey(0)
30
31
32# 将 img1 中的 ROI 的 logo 区域涂黑
33img1_bg = cv2.bitwise_and(roi, roi, mask=mask_inv)
34cv2.imshow("img1_bg", img1_bg)
35cv2.waitKey(0)
36# plt.imshow(img1_bg)
37
38# 只提取 logo 图像中的 logo
39img2_fg = cv2.bitwise_and(img2, img2, mask=mask)
40cv2.imshow("img2_fg", img2_fg)
41cv2.waitKey(0)
42
43
44# 将 ROI 区域与 logo 图标运算
45dst = cv2.add(img1_bg, img2_fg)
46cv2.imshow("dst", dst)
47cv2.waitKey(0)
48
49
50img1[0:rows, 0:cols] = dst
51
52cv2.imshow("res", img1)
53cv2.waitKey(0)
54cv2.destroyAllWindows()
1(342, 548, 3) (186, 152, 3)
2roi.shape: (186, 152, 3)
3ret 25.0
运行结果
要将两张图放在一起,且都是原来的图像,如上图所示,将 OpenCV logo (img2)放在 img1 上面,巧妙的利用黑色像素(0)。 如何将两个图一原来的图像进行重叠:
1def pinjie(img1, img2, x=0, y=0):
2 """
3 img1: 底图
4 img2: 上方的图
5 x, y:选择放置的位置
6 """
7 # 创建一个 ROI
8 rows, cols, channel = img2.shape
9 roi = img1[x:x+rows, y:y+cols]
10 img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
11 ret, mask = cv2.threshold(img2gray, 25, 255, cv2.THRESH_BINARY)
12 mask_inv = cv2.bitwise_not(mask)
13 roi = cv2.bitwise_and(roi, roi, mask=mask_inv)
14 img2_bg = cv2.bitwise_and(img2, img2, mask=mask)
15 img1[x:x+rows, y:y+cols] = cv2.add(roi, img2_bg)
16 return img1
17
18
19img1 = cv2.imread("messi5.jpg")
20img2 = cv2.imread("opencv-logo.png")
21dst = pinjie(img1, img2, 100, 100)
22cv2.imshow("dst", dst)
23cv2.waitKey(0)
24cv2.destroyAllWindows()
[本节原文] (https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_core/py_image_arithmetics/py_image_arithmetics.html)
[Python下opencv使用笔记(四)(图像的阈值处理)] (https://blog.csdn.net/on2way/article/details/46812121)
图像处理中,不仅需要准确,更需要快速的方法进行处理。 学习目标:
time
模块可以测量执行时间;
profile
模块有助于获得关于代码的详细报告,比如代码中每个函数花费了多少时间、调用了多少次等等。
cv2.getTickCount
在代码前后使用可以得到代码的运行时间cv2.getTickFrequency
返回时钟周期的频率,即每秒的时钟周期数1img1 = cv2.imread('messi5.jpg')
2
3e1 = cv2.getTickCount()
4for i in range(5,49,2):
5 img1 = cv2.medianBlur(img1,i)
6e2 = cv2.getTickCount()
7t = (e2 - e1)/cv2.getTickFrequency()
8e2 - e1, t
1(8315303, 0.8315303)
python 中的 time
模块也可以实现该功能
OpenCV 的许多函数都是使用SSE2、AVX等优化的。它还包含未优化的代码。因此,如果我们的系统支持这些特性,我们应该利用它们(几乎所有现代处理器都支持它们)。它在编译时默认启用。OpenCV 在启用时运行优化后的代码,否则运行未优化的代码。使用 cv2.useoptimization()
来检查它是否启用/禁用,使用
cv2.setuseoptimization()
来启用/禁用它。
1# 检查优化器是否开启
2cv2.setUseOptimized(True)
3cv2.useOptimized()
1True
1img = cv2.imread("messi5.jpg")
2%timeit res = cv2.medianBlur(img,49)
131.8 ms ± 1.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1# 设置优化器的开关
2cv2.setUseOptimized(False)
1cv2.useOptimized()
1False
1%timeit res = cv2.medianBlur(img,49)
130.9 ms ± 448 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
可以看出使用优化器前后的运行性能
测试一下下面几个计算平方的方法哪个更好。
例子:
x = 5; y = x**2
、x = 5; y = x*x
、x = np.uint8([5]); y = x*x
、y = np.square(x)
可以使用 %timeit
进行测试
1x = 5
1%timeit y = x**2
1505 ns ± 18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
1%timeit y = x*x
177.5 ns ± 1.98 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
1z = np.uint8([5])
2%timeit y=z*z
1709 ns ± 57.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
1%timeit y=np.square(z)
1674 ns ± 66.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
y = x*x
比 Numpy 运算快不少
Python 标量操作比 Numpy 标量操作快。因此对于包含一个或两个元素的操作,Python 标量优于 Numpy 数组。当数组的大小稍微大一点时,Numpy 就更有优势。
1# 比较 cv2.countNonZero() 和 np.count_nonzero() 的性能
2%timeit z = cv2.countNonZero(img[:,:, 0]) # 该函数只能计算单通道的图片
1269 µs ± 30.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1%timeit z = np.count_nonzero(img[:,:, 0])
1677 µs ± 54.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
首先尝试以一种简单的方式实现该算法。一旦开始工作,对其进行分析,找到瓶颈并对其进行优化。
即使在执行了所有这些操作之后,如果代码仍然很慢,或者不可避免地要使用大型循环,那么可以使用其他库,比如Cython来加快速度。
[Python Optimization Techniques] (https://wiki.python.org/moin/PythonSpeed/PerformanceTips)、 Scipy Lecture Notes - [Advanced Numpy] (http://scipy-lectures.org/advanced/advanced_numpy/index.html#advanced-numpy) time profile 模块
本文源码地址在本人的 Github 上,欢迎关注
CSDN 地址:https://blog.csdn.net/LSJ944830401
Github 地址:https://github.com/FLyingLSJ/ 感谢您的关注与支持!