二、核心操作

2.1 图像基本操作

学习目标:

  • 学会调整图像的某个像素
  • 学会调整图像的基本属性
  • 设置图像的ROI(Region Of Interest)
  • 切分/合并图像

这一节的操作与NumPy的关联性很强,所以实操前请先准备好NumPy的相关知识。

实例在Python终端执行

调整图像的某个像素

先在终端导入一幅图:

>>> import numpy as np
>>> import cv2 as cv

>>> img = cv.imread('image01.jpg')

我们可以根据某个像素点的坐标来修改它的值,对于一个BGR格式的图像而言,点坐标会返回它的BGR通道值,而对于一个灰度图像而言,返回的是灰度值。

>>> px = img[100,100]
>>> print( px )
[157 166 200]

# 只获取某像素的Blue通道的值
>>> blue = img[100,100,0]
>>> print( blue )
157

也可以通过类似的方法修改某个像素的值:

>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]

注意:

NumPy是一个及其高效的科学计算库,所以依次遍历某一个或每一个像素值会很慢,所以不提倡这么做。

上述方法只适用于查询/修改某一个像素值,在NumPy中有更高效的方法用于更高端的操作,所以好好学NumPy吧!

下面是一个使用NumPy访问/修改像素值的简单例子:

# 读取Red通道的值
>>> img.item(10,10,2)
59

# 修改Red通道的值
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100

调整图像的基本属性

“基本属性”包括行像素数、列像素数、颜色通道数、总像素数等等。

图像的形状由一个元组img.shape()储存,包括行数、列数、颜色通道数(如果有的话):

>>> print( img.shape )
(342, 548, 3)

灰度图像没有第三个参数,所以这也是判断一个图像是不是灰度图像的方法。

像素数由img.size()储存:

>>> print( img.size )
562248

图像的存储数据类型用img.dtype()表示:

>>> print( img.dtype )
uint8

设置图像的ROI(Region Of Interest)

有时候,我们只需要操作图像的某一部分,于是便要设置图像的ROI。比方说,为了实现某一个美颜算法,操作前就要先确定“脸”的大致位置,于是便把表示脸部的像素设置为ROI。这样做能加快计算机读取和操作的速度。

设置ROI这一步也要用到NumPy,以下的操作就是复制图像的每一部分到另一部分:

>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball

切分/缩放图像

对图像进行操作时,有时候要用到切分/合并图像的某一个颜色通道,下面的例子就是切分并图像的BGR通道:

>>> b,g,r = cv.split(img)
>>> img = cv.merge((b,g,r))

或者这样:

>>> b = img[:, :, 0]

如果想删除某一个通道,可以这样做:

>>> img[:, :, 2] = 0

注意:cv.split()是一个费时的操作,能不用尽量别用。

2.2 图像的相关运算

学习目标:

  • 学会对图像进行数学运算操作,如加法、减法、位运算等
  • 学会以下函数:cv.add()cv.addWeighted()

加法

OpenCV提供了cv.add()函数用于将两个图像相加,也可以简单地使用img1 + img2 以达到相同的效果。注意,两幅图像必须尺寸相同。

tips: OpenCV的cv.add()与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提供了另一种图像的加法,即加权加法,它遵循以下公式:
g(x) = (1-\alpha)f_0(x)+\alpha f_1(x)
在上式中改变\alpha的值(0 → 1)可以改变两幅图像的混合情况。

在下面这个实例中,我使用cv.addWeighted()将两幅图像以0.3:0.7的比例混合,并且加上一个修订值\gamma,如下公式表示:
dst=\alpha \cdot img1 + \beta \cdot img2 + \gamma
下面这个实例中,\gamma取0:

import cv2 as cv
import numpy as np

img1 = cv.imread('image01.png')
img2 = cv.imread('image02.png')

dst = cv.addWeighted(img1,0.7,img2,0.3,0)

cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()

位运算操作

位运算包括按位与、按位或、按位非、按位异或,这些运算相当重要,接下去的章节将展示它们的重要性,注意位运算最好在ROI中进行。

如果我现在想把一张黑底的图置于白底的图的上方,如果简单地相加或混合结果必然会不尽人意,这时候位运算就显得尤为重要,下面的实例将展示位运算操作有些操作现在看不懂不要紧

import cv2 as cv
import numpy as np

# 载入图像
img1 = cv.imread('image01.jpg')
img2 = cv.imread('image02.png')

# 创建ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]

# 创建图像的背景遮罩
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)

# 将黑色部分去除
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)

# 将剩余部分取出
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()

OpenCV: Arithmetic Operations on Images上有这个实例的展示图

2.3 计时/改进代码

目标

在图像处理的过程中机器可能要进行许多次运算操作,有时候会很耗时,因而优化代码/改进算法刻不容缓,在这一节我们将会学习:

  • 记录程序运行时间
  • 一些优化程序的小技巧
  • 了解并使用以下函数:cv.getTickCount, cv.getTickFrequency

除OpenCV之外,Python也提供了一个好用的计时模块Time,此外,profile模块能让用户获取程序报告,比如每个函数的执行时间什么的。然而,如果你使用的是IPython, 你会感到更友好的交互式体验,这些将会在本节“其他相关资源”中提及。

用OpenCV内置的方法计时

cv.getTickCount这个函数的返回值是一个某一个时钟刻(可能是机器开始运行时开始计时什么的,单位不是秒),所以调用两次这个函数并且将两次返回值相减就得到了这段时间的时钟刻数量。

cv.getTickFrequency这个函数的返回值表示时钟频,即每一秒的时钟刻的数量,所以你可以像如下实例这样计算某一段代码的运行时间:

e1 = cv.getTickCount()
# 运行的代码
e2 = cv.getTickCount()
time = (e2 - e1)/ cv.getTickFrequency()

接下来我们将用下面的实例进行展示,这个实例的作用目前不必了解,只是用来使用上述两个函数的:

img1 = cv.imread('image01.jpg')

e1 = cv.getTickCount()
for i in range(5,49,2):
    img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()

print( t )

我自己的电脑上运行用时0.0213916s

OpenCV内置的默认优化

OpenCV内置的许多函数使用了SSE2, AVX等优化方式,但它也有些没使用优化的函数。对于那些有优化的功能,如果系统支持使用,那就最好把它们打开。其内置的cv.useOptimized()函数用于检查优化是否被开启,cv.setUseOptimized()函数用于开关某种优化,具体如下:

>>> cv.useOptimized()
True
>>> %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop
    
>>> cv.setUseOptimized(False)
>>> cv.useOptimized()
False
>>> %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop

如你所见,优化后的运行速度大约是优化前的两倍,因为这里用到了一种叫SIMD的优化算法(默认开启)。

一些优化的建议

在Python或NumPy中有许多优化运行速度的代码技巧,其中一些主要的罗列在下面,请读者尝试在一些简单的程序中测试以下算法,一旦测试成功,再应用到已有的程序中:

  1. 尽量少用python中的循环,尤其是多重循环,这玩意很慢
  2. 尽量多地处理矢量,因为OpenCV和NumPy对矢量运算有优化
  3. 利用缓存的一致性
  4. 绝不要复制列表(arrays)除非真的必要,列表的复制很费时

如果你在采用上述步骤后依然不能得到满意的运行效率,或者你不可避免地要使用多重循环,那就试试Cython(译者注:Python的一个库)吧。

(译者注:Python在没有第三方库(如NumPy)的情况下,是一种开发高效但是运行低效的语言,若是要追求运行效率,建议使用OpenCV for C++。此外,为了开发效率,必要的基础算法希望在读的各位能掌握。)

2022.4.14

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容