核心操作目录
- 图片的基础操作
- 学习读和编辑像素值,使用 image ROI和其他基础操作工作
- 图片的加工算法
- 在图片上执行加工算法
- 性能测量和改进技术
- 得到解决方式是重要的。但是获取的速度也是更重要的。学习检查你代码的速度,代码优化等等
文章目录
图片的基础操作
目标
学习:
- 访问像素值并更改他们
- 访问图片的属性
- 设置 感兴趣区域(Region of Interest ROI)
- 分割和混合图片
本章几乎所有的操作都和numpy更有关系而不是opencv。想要使用opencv优化你的代码需要一个好的Numpy基础
*(单行的案例会更多的展示在python终端)
访问和改变像素值
让我们首先读取一个图片
import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg')
你可以访问一个像素的值通过他的行和列的数据。对于BGR的图片,返回一个BGR的一个列表。对于灰度图,只返回相应的强度。
px = img[100,100]
print(px)
>>[255 255 255]
blue=img[100,100,0]
print(blue)
>>255
同样的,你可以通过这种方法更改其值
img[100,100] = [0,0,0]
print( img[100,100] )
>>[0 0 0]
彩色图片更改为灰度图片
grayimg=cv.cvtColor(img,code=cv.COLOR_BGR2GRAY)
灰度图片的访问:
gray=grayimg[100,100]
print(gray)
>>255
注意
Numpy 是一个数学计算库用于快速的列表计算。所以简单的访问每一组和每一个像素的值 并且更改 是非常非常的缓慢的并且也不鼓励这么做
注意:
上面的方法经常用于选择数组中的一个区域,例如前五行和后三行。对于单独的像素访问,Numpy数组方法,array.item()和array.itemset()用起来更好,但是他总是返回一个标量,例如要访问所有的B,G,R的值,你需要分别调用array.item()
git checkout v2.0
code/basic_operation_image.py
更好的像素方位和编辑方法:
>>> img.item(10,10,2)
59
# modifying RED value
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100
git checkout v2.1
访问图片属性
图片属性包括了行数,列和评到,图片数据的类型,像素的数量等等
img.shape
可以访问图片的形状。他返回了一个元组(行,列,色彩(RGB的数量))
>>> print( img.shape )
(342, 548, 3)
注意
如果一个图片是灰度图,元组仅仅返回行和列,所以这是一个很好的办法去检测一个图片是彩色的还是灰度的
img.size
可以访问总的像素数量
>>> print( img.size )
562248
img.dtype
包含了图片的的数据类型
>>> print( img.dtype )
uint8
注意
img.dtype 在debug的时候非常重要的。因为一个大型数据会引发Open-CV-python代码的 无效数据类型的错误
图片ROI
有时候,你会需要中央的图片。对于图像中的眼睛检测,首先对图像进行人脸检测。当我们得到人脸时,我们只选择人脸区域,并在其中搜索眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是盯着脸:D)和性能(因为我们在一个小区域搜索)。
ROI又一次由Numpy的检索功能获得。这里我选区了求,并且复制他到另一个图片中的区域中。
分割和混和图片的频道
有时候你需要分别工作于图片的 B,G,R频道中。在这个案例中,你需要分割BGR图片到单个通道。在这个案例中,你会需要加如到这些单独的频道到BGP图片中。你可以简单的做到他 通过:
或者
>>> b = img[:,:,0]
假如你想要设置所有的红色像素为0,你不需要首先封开这个频道,Numpy的检索很快
>>> img[:,:,2] = 0
注意:
cv.split()(就时间而言)分割是一项昂贵的操作。所以只有在你需要的时候才去做。否则就使用Numpy索引。
为图片创建边界(填充)
如果你想要创建一个图片的边界,有时候类似照片框,你可以使用cv.copyMakeBorder(),但它在卷积运算、零填充等方面的应用较多。该函数具有以下参数:
- src:输入的图片
- top,bottom,left,right : 边框宽度以对应方向的像素数表示
- borderType :标志,定义要添加的边框类型。它可以是以下类型:
- cv.BORDER_CONSTANT :添加一个固定的彩色边框。这个值应该作为下一个参数给出。
- cv.BORDER_REFLECT : 边界将是边界元素的镜像反射,如下:fedcba|abcdefgh|hgfedcb
- cv.BORDER_REFLECT_101 或cv.BORDER_DEFAULT :和上面一样,但是有一点变化,像这样:gfedcb|abcdefgh|gfedcba
- cv.BORDER_REPLICATE:最后一个元素被完全复制,如下所示:aaaaaa|abcdefgh|hhhhhhh
- cv.BORDER_WRAP:无法解释,它会是这样的:cdefgh|abcdefgh|abcdefg
- value : 边界的颜色 如果边界 的类型是 cv.BORDER_CONSTANT
看如下的结果(图片通过matplotlib展示,所以红色和蓝色频道是互换的)
#图片的加工算法
目标
- 学习图像的几种算术运算,如加法、减法、位运算等。
- 你会学习到一下几种方法:cv.add(),cv.addWeighted() 等等
图片叠加
您可以通过OpenCV函数,cv.add()或者简单地通过numpy操作,res = img1 + img2来添加两个图像。这两个图像应该具有相同的深度和类型,否则第二个图像只能是标量值。
注意
OpenCV添加和Numpy添加是有区别的。OpenCV加法是饱和运算,Numpy加法是模运算。
例如,思考一下的例子:
>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print( cv.add(x,y) ) # 250+10 = 260 => 255
[[255]]
>>> print( x+y ) # 250+10 = 260 % 256 = 4
[4]
当您添加两个图像时,它将更加可见。OpenCV函数将提供更好的结果。所以最好还是用OpenCV函数。
图片融合
这也是图像的添加,但不同的权重赋予图像,使其给人一种混合或透明的感觉。图像按下式添加:
通过改变从0→α1中,您可以执行一个很酷的一个图像之间的过渡到另一个地方。
这里我拍了两张照片把它们混合在一起。第一幅图像的权值为0.7,第二幅图像的权值为0.3。cv.addWeighted()对图像应用以下方程。
γ是视为零。
img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows(
查看一下结果
按位操作
这包括位和、或、非和XOR操作。它们在提取图像的任何部分(我们将在下一章中看到)、定义和处理非矩形ROI等方面非常有用。
我想把OpenCV的logo放在图片上面。如果我添加两个图像,它会改变颜色。如果我混合它,我得到一个透明的效果。但我希望它是不透明的。如果它是一个矩形区域,我可以像上一章那样使用ROI。但是OpenCV的logo不是一个矩形。所以你可以按位操作如下:
# Load two images
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.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 ]
# Now create a mask of logo and create its inverse mask also
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# Now black-out the area of logo in ROI
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
# Take only region of logo from logo image.
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# Put logo in ROI and modify the main image
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()
请看下面的结果。左边的图像显示了我们创建的掩码。右图显示了最终结果。为了更好地理解,请显示上述代码中的所有中间图像,特别是img1_bg和img2_fg。
代码解读:
# Load two images
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
##抓出需要进行覆盖的图像
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 创建一张灰度图
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
### 从灰度图获取一张2值图,调整阈值 (参数中的10)到可以看到整个logo。返回的ret是阈值,mask是最终的二值图
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
### 反向选取LOGO以外的像素点
mask_inv = cv.bitwise_not(mask)
### 在roi中将logo将要移动的地方 全部置为黑色,清空
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
### 将logo中不需要的地方 剪掉
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
### 将两种图合并
dst = cv.add(img1_bg,img2_fg)
### 代替原图
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()
练习
- 在文件夹中创建图像幻灯片,使用cv在图像之间进行平滑转换。addWeighted函数
性能测量和改进技术
目标
在图像处理中,由于每秒要处理大量操作,因此必须确保代码不仅提供正确的解决方案,而且以最快的方式提供解决方案。所以在这一章,你会学到
- 衡量代码的性能
- 一些优化你代码性能的技巧
- 你会看到这些方法:cv.getTickCount(),cv.getTickFrequency() 等
除了OpenCV, Python还提供了一个模块time,它有助于测量执行时间。另一个模块配置profile有助于获得关于代码的详细报告,比如代码中的每个函数花费了多少时间、调用了多少次等等。但是,如果您使用IPython,那么所有这些特性都是以用户友好的方式集成的。我们将看到一些重要的链接,有关更多细节,请参阅附加参考资料部分中的链接。
衡量OpenCV的性能
cv,getTickCount 函数返回引用事件(如机器切换到开启的时间)到调用该函数时的时钟周期数。因此,如果在函数执行前后调用它,就会得到用于执行函数的时钟周期数。
cv.getTickFrequency 函数返回时钟周期的频率,或每秒时钟周期的数量。因此,要计算以秒为单位的执行时间,可以执行以下操作:
e1 = cv.getTickCount()
# your code execution
e2 = cv.getTickCount()
time = (e2 - e1)/ cv.getTickFrequency()
我们将用以下示例进行演示。下面的示例应用中值过滤,其内核大小从5到49不等。(不要担心结果会是什么样子,那不是我们的目标):
img1 = cv.imread('messi5.jpg')
e1 = cv.getTickCount()
for i in xrange(5,49,2):
img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()
print( t )
# Result I got is 0.521107655 seconds
注意
time模块也可以这样做。,使用time.time()函数,取代cv.getTickCount,然后取两倍的差。
##OpenCV中的默认优化
OpenCV的许多函数都是使用SSE2、AVX等优化的,它也包含未优化的代码。因此,如果我们的系统支持这些特性,我们应该利用它们(几乎所有现代处理器都支持它们)。它在编译时默认启用。所以OpenCV在启用的情况下会运行优化的代码,否则会运行未优化的代码。您可以使用cv. useoptimization()来检查是否启用/禁用它,使用cv. setuseoptimization()来启用/禁用它。让我们看一个简单的例子