OpenCV基础操作(2)OpevCV算术运算

OpenCV基础操作(2)OpevCV算术运算

在这里插入图片描述

import cv2 as cv
import numpy as np

一、图像的基础操作

1、获取并修改像素值

'''
你可以根据像素的行和列的坐标获取他的像素值。
对 BGR 图像而言,返回值为 B,G,R 的值。对灰度图像而言,会返回他的灰度值
'''

img = cv.imread('img.jpg')
px = img[100,100]
print(px)
blue = img[100,100,0]
print(blue)
[255 255 255]
255
# 以类似的方式修改像素值
img[100,100]=[0,0,0]
print(img[100,100])
[0 0 0]
'''
上面提到的方法被用来选取矩阵的一个区域,比如说前 5 行的后 3列。
对于获取每一个像素值,也许使用 Numpy 的 array.item() 和 array.itemset() 会更好。但是返回值是标量。
如果你想获得所有 B,G,R 的值,你需要使用 array.item() 分割他们。
'''
(494, 650, 3)
'''
获取像素值及修改的更好方法(推荐)
'''

img = cv.imread('img.jpg')
# 使用Image对象的item()取值
print(img.item(10,10,0))
# 使用Image对象的itemset()设置值
img.itemset((10,10,2),100)
print(img.item(10,10,2))
255
100

2、获取图像属性

img = cv.imread('img.jpg')
# img.shape 可以获取图像的形状。他的返回值是一个包含行数,列数,通道数的元组
# 如果图像是灰度图,返回值仅有行数和列数。所以通过检查这个返回值就可以知道加载的是灰度图还是彩色图
print(img.shape)
(494, 650, 3)
# img.size 可以返回图像的像素数目
print(img.size)
963300
# img.dtype 返回的是图像的数据类型
# 在debug时 img.dtype 非常重要。因为在 OpenCV Python 代码中经常出现数据类型的不一致
print(img.dtype)
uint8

3、图像ROI

'''
有时你需要对一幅图像的特定区域进行操作。例如我们要检测一副图像中眼睛的位置,我们首先应该在图像中找到脸,
再在脸的区域中找眼睛,而不是直接在一幅图像中搜索。这样会提高程序的准确性和性能。


ROI 也是使用 Numpy 索引来获得的。
'''
img = cv.imread('img.jpg')
img[0:100,0:100] = img[300:400,250:350]


cv.imshow('small_img',img)
cv.waitKey(0)
cv.destroyAllWindows()

4、拆分及合并图像通道

'''
有时我们需要对 BGR 三个通道分别进行操作。
这时你就需要把 BGR 拆分成单个通道。
有时你需要把独立通道的图片合并成一个 BGR 图像。

cv2.split() 是一个比较耗时的操作。只有真正需要时才用它,能用Numpy 索引就尽量用。
'''

img = cv.imread('img.jpg')
b,g,r = cv.split(img)
img = cv.merge([b,g,r])

# 或者
b= img[:,:,0]
# 假如你想使所有像素的红色通道值都为 0,你不必先拆分再赋值。你可以直接使用 Numpy 索引,这会更快

img[:,:,2] = 0

5、为图像扩边(填充)

'''
如果你想在图像周围创建一个边,就像相框一样,你可以使用 cv2.copyMakeBorder()函数。


这经常在卷积运算或 0 填充时被用到。这个函数包括如下参数:
• src 输入图像
• top, bottom, left, right 对应边界的像素数目。
• borderType 要添加那种类型的边界,类型如下
    – cv2.BORDER_REPLICATE 重复最后一个元素。例如: aaaaaa|abcdefgh|hhhhhhh
    – cv2.BORDER_REFLECT 边界元素的镜像。比如: fedcba|abcdefgh|hgfedcb
    – cv2.BORDER_REFLECT_101 or cv2.BORDER_DEFAULT 跟上面一样,但稍作改动。例如: gfedcb|abcdefgh|gfedcba
    – cv2.BORDER_WRAP 不知道怎么说了, 就像这样: cdefgh|abcdefgh|abcdefg
    – cv2.BORDER_CONSTANT 添加有颜色的常数值边界,还需要下一个参数(value)。
• value 边界颜色,如果边界的类型是 cv2.BORDER_CONSTANT
'''
from matplotlib import pyplot as plt
BLUE=[255,0,0]
img1 = cv.imread('open_cv.png')



replicate =  cv.copyMakeBorder(img1,30,30,30,30,cv.BORDER_REPLICATE)
reflect =    cv.copyMakeBorder(img1,30,30,30,30,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,30,30,30,30,cv.BORDER_REFLECT_101)
wrap =       cv.copyMakeBorder(img1,30,30,30,30,cv.BORDER_WRAP)
constant=    cv.copyMakeBorder(img1,30,30,30,30,cv.BORDER_CONSTANT,value=BLUE)


plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL'),plt.xticks([]),plt.yticks([])
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE'),plt.xticks([]),plt.yticks([])
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT'),plt.xticks([]),plt.yticks([])
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101'),plt.xticks([]),plt.yticks([])
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP'),plt.xticks([]),plt.yticks([])
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT'),plt.xticks([]),plt.yticks([])

plt.show()


在这里插入图片描述

二、图像上算数运算

1、图像加法

'''
你可以使用函数 cv2.add() 将两幅图像进行加法运算,当然也可以直接使用 numpy,res=img1+img。
两幅图像的大小,类型必须一致,或者第2个图像可以使一个简单的标量值。


注意:OpenCV 中的加法与 Numpy 的加法是有所不同的。OpenCV 的加法是一种饱和操作,而 Numpy 的加法是一种模操作

这种差别在你对两幅图像进行加法时会更加明显。OpenCV 的结果会更好一点。所以我们尽量使用 OpenCV 中的函数。
'''
x = np.uint8([250])
y = np.uint8([10])
print(cv.add(x,y)) # 250+10 = 260 => 255
print(x + y)       # 250+10 = 260 % 256 = 4
[[255]]
[4]

2、图像混合

'''
这其实也是加法,但是不同的是两幅图像的权重不同,这就会给人一种混合或者透明的感觉。

现在我们把两幅图混合在一起。第一幅图的权重是 0.7,第二幅图的权重是 0.3。

函数 cv2.addWeighted() 可以按下面的公式对图片进行混合操作
dst = α · img1 + β · img2 + γ
这里 γ 的取值为 0
'''
img1 = cv.imread('img.jpg')[:494,:467]
img2 = cv.imread('open_cv.png')[:494,:467]

dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()

3、按位运算

'''
按位操作有:AND,OR,NOT,XOR 等。

我想把 OpenCV 的标志放到另一幅图像上。
如果我使用加法,颜色会改变,如果使用混合,会得到透明效果,但是我不想要透明。
如果是矩形我可以使用 ROI。但是他不是矩形。
'''

# 加载图像
img1 = cv.imread('player.png')
img2 = cv.imread('small_open_cv.png')
# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]


# 转换为灰度图像
img2gray = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)
'''
cv.threshold()
图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果。
在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。
double threshold( InputArray src, OutputArray dst,double thresh, double maxval, int type );
		enum ThresholdTypes {T
			THRESH_BINARY     = 0,	   // 大于thresh 为 maxval,否则为 0
			THRESH_BINARY_INV = 1,	  // 大于thresh 为 0,否则为 maxval
			THRESH_TRUNC      = 2,	  // 大于thresh 为 thresh,否则为 0
			THRESH_TOZERO     = 3,	  // 大于thresh  不变,否则为 0
			THRESH_TOZERO_INV = 4,	  // 大于thresh 为 0,否则 不变
			THRESH_MASK       = 7,
			THRESH_OTSU       = 8,	 // 使用OTSU阈值
			THRESH_TRIANGLE   = 16	 // 使用三角形算法阈值
		}

src:原始数组 (单通道 , 8-bit of 32-bit 浮点数)。
dst:输出数组,必须与 src 的类型一致,或者为 8-bit。
threshold:阈值
max_value:使用 THRESH_BINARY 和 THRESH_BINARY_INV 的最大值。
threshold_type:阈值类型
threshold_type = THRESH_BINARY      :如果 src(x,y) > thresh ,dst(x,y) = max_value; 否则,dst(x,y)=0;
threshold_type = THRESH_BINARY_INV  :如果 src(x,y) > thresh,dst(x,y) = 0; 否则,dst(x,y) = max_value.
threshold_type = THRESH_TRUNC       :如果 src(x,y) > thresh,dst(x,y) = max_value; 否则dst(x,y) = src(x,y).
threshold_type = THRESH_TOZERO      :如果src(x,y)  > thresh,dst(x,y) = src(x,y) ; 否则 dst(x,y) = 0。
threshold_type = THRESH_TOZERO_INV  :如果 src(x,y)> thresh,dst(x,y) = 0 ; 否则dst(x,y) = src(x,y).
'''
# 二值化操作,转换为黑白图像
ret, mask = cv.threshold(img2gray, thresh=175, maxval=255, type=cv.THRESH_BINARY)
print('ret',ret)
print('mask',mask[0,:])
# 位运算,取相反的颜色
mask_inv = cv.bitwise_not(mask)
cv.imshow('img2gray', img2gray)
cv.imshow('1mask', mask)         # logo为黑色,即0
cv.imshow('2mask_inv', mask_inv) # logo为白色,即255
cv.waitKey(0)
cv.destroyAllWindows()





'''
按位操作符:cv.bitwise_and(srt1, srt2, mask),该函数相当于数学运算的“与”
原理如下:1&1=1、1&0=0、0&1=0、0&0=0。
srt1、srt2分别为相加的两个窗口,mask为指定要更改的输出数组的元素。
该函数运行时,只会将mask部分的像素进行“与”运算,最后保留指定色彩。
'''
# 注意这里必须有 mask=mask 或者 mask=mask_inv, 其中的 mask= 不能忽略
# 获取到背景图
img1_bg = cv.bitwise_and(roi,roi,mask = mask)        # 将logo区域设置为黑色
# 取到前景图
img2_fg = cv.bitwise_and(img2,img2,mask = mask_inv)  # 将logo以外的区域设置为黑色,log区域为原来颜色

cv.imshow('roi', roi)
cv.imshow('bg', img1_bg)
cv.imshow('fg', img2_fg)
cv.waitKey(0)
cv.destroyAllWindows()


# 将背景图和前景图颜色合并,此时img1_bg(logo区域为0) + img2_fg(logo区域不为0) = img2_fg(logo区域)
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols] = dst
cv.imshow('res', img1)
cv.waitKey(0)
cv.destroyAllWindows()
ret 175.0
mask [255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
 255 255]

图片效果分别如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、程序性能检测及优化

在图像处理中你每秒钟都要做大量的运算,所以你的程序不仅要能给出正确的结果,同时还必须要快。

除了 OpenCV,Python 也提供了一个叫 time 的的模块,你可以用它来测量程序的运行时间。
另外一个叫做 profile 的模块会帮你得到一份关于你的程序的详细报告,其中包含了代码中每个函数运行需要的时间,
以及每个函数被调用的次数。

1、使用OpenCV检测程序的效率

'''
cv2.getTickCount 函数返回从参考点到这个函数被执行的时钟数。
所以当你在一个函数执行前后都调用它的话,你就会得到这个函数的执行时间(时钟数)。


cv2.getTickFrequency 返回时钟频率,或者说每秒钟的时钟数。
'''
img1 = cv.imread('open_cv.png')
e1 = cv.getTickCount()
for i in range(5,49,2):
    # 用窗口大小不同(5,7,9)的核函数来做中值滤波
    img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1) / cv.getTickFrequency()
print(t,' seconds')
0.5352825  seconds
import time

# 另一种方式

img1 = cv.imread('open_cv.png')
e1 = time.time()
for i in range(5,49,2):
    # 用窗口大小不同(5,7,9)的核函数来做中值滤波
    img1 = cv.medianBlur(img1,i)
e2 = time.time()
t = (e2 - e1)
print(t,' seconds')
0.5371222496032715  seconds

2、默认优化

'''
OpenCV 中的很多函数都被优化过。也包含一些没有被优化的代码。
如果我们的系统支持优化的话,要尽量利用。在编译时优化是被默认开启的。


因此 OpenCV 运行的就是优化后的代码,如果你把优化关闭的话就只能执行低效的代码了。
你可以使用函数 cv2.useOptimized()来查看优化是否被开启了,使用函数 cv2.setUseOptimized() 来开启优化。
'''
# check if optimization is enabled
print(cv.useOptimized())

%timeit res = cv.medianBlur(img,49)
True
27.6 ms ± 1.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 关闭
cv.setUseOptimized(False)
print(cv.useOptimized())

%timeit res = cv.medianBlur(img,49)
False
28.8 ms ± 1.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

3、在 IPython 中检测程序效率

'''
有时你需要比较两个相似操作的效率,这时你可以使用 IPython 为你提供的魔法命令%time。
他会让代码运行好几次从而得到一个准确的(运行)时间。它也可以被用来测试单行代码的。
'''
z = np.uint8([5])
%timeit y=z*z
%timeit y=np.square(z)
382 ns ± 8.59 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
370 ns ± 9.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
img = cv.imread('img.jpg',0)

# 一般情况下 OpenCV 的函数要比 Numpy 函数快。所以对于相同的操作最好使用 OpenCV 的函数。
%timeit z = cv.countNonZero(img)
%timeit z = np.count_nonzero(img)
12.4 µs ± 322 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
14.4 µs ± 180 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

猜你喜欢

转载自blog.csdn.net/qq_44665283/article/details/130785038