小白学OpenCV系列3-图像算数运算

我们之前讲过,图像读取到计算机之后就是一个矩阵,那么它就可以做各种各样的运算,今天就先来讲讲最基础的加减乘除以及位运算,它们是其他高阶运算的基础。

加法

有两种方式可以进行常规的加法运算,一种是通过加法运算符"+",一种是通过cv2.add()函数。但是由于灰度图的像素值范围通常是[0, 255],如果求和,得到的数值很可能会超过255,因此对超过255的数值,两者的处理方式是不一样的。

加号运算符

使用加法运算符的时候,超过255的数值会对256取模,以便让数值范围重新回到[0,255],我们可以通过下列例子来演示该过程。

python 复制代码
import cv2  
  
  
img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:", img1)  
img2 = cv2.imread("lena.bmp", flags=0)  
print("img2", img2)  
print("img1+img2:", img1 + img2)

img1和img2都是下图的像素值矩阵。

最终计算的结果如下图所示:

可以看到图中第一个红框和第二个红框的数值加起来就超过了255,可是最后计算的结果却是68,实际上就是(162+162)mod 256 = 68。

cv2.add函数

使用add函数的时候,对于超过255的数值会截止到255,以便让数值范围仍然在[0,255],我们将上面的代码稍微改一改。

python 复制代码
import cv2  


img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:\n", img1)  
img2 = cv2.imread("lena.bmp", flags=0)  
print("img2:\n", img2)  
print("cv2.add(img1,img2):\n", cv2.add(img1, img2))

计算的结果如下所示:

可以看到图中第一个红框和第二个红框的数值加起来就超过了255,可是最后计算的结果却被截止到了255。

加权和

除了上述两种常规加法外,实际上还可以计算图像的加权和,就是在计算两幅图像的像素值之和时,将每幅图像的权重考虑进来,如下式所示:
dst=saturate(src1∗α+scr2∗β+γ) dst=saturate(src1 * \alpha + scr2 * \beta + \gamma) dst=saturate(src1∗α+scr2∗β+γ)

最后计算出来的值如果超过了255,同样地会被截止到255,而且src1和src2的大小和类型必须相同才行。我们还是使用上述图片来演示该过程。

python 复制代码
import cv2 


img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:\n", img1)  
img2 = cv2.imread("lena.bmp", flags=0)  
print("img2:\n", img2)  
print("cv2.addWeighted(img1,2,img2,3,4):\n", cv2.addWeighted(img1, 2, img2, 3, 4))

我们可以简单地计算下上述的数值是否满足加权求和的规律:45 * 2 + 45 * 3 + 4 = 229,说明加权成功了,而计算后超过255的数值就会被重置为255。

减法

和加法一样,同样有两种方式可以进行常规的减法运算,一种是通过减法运算符"-",一种是通过cv2.subtract()函数。但是由于灰度图的像素值范围通常是[0, 255],如果求差,得到的数值很可能会小于0,因此对小于0的数值,两者的处理方式也是不一样的。

减法运算符

使用减法运算符的时候,小于0的数值会对256取模,以便让数值范围重新回到[0,255],我们可以通过下列例子来演示该过程。

python 复制代码
import cv2  
import numpy as np  
  
  
img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:\n", img1) 
# ones_like的作用为创建一个形状和img1一样的单位矩阵 
img2 = np.ones_like(img1) * 100  
print("img2:\n", img2)  
print("img1-img2:\n", img1 - img2)

最终计算的结果如下图所示,从中可以看到大于0的数值仍然保持原样,而小于0的数值则对256取模了。

cv2.subtract函数

使用subtract函数的时候,对于小于0的数值会截止到0,以便让数值范围仍然在[0,255],我们将上面的代码稍微改一改。

python 复制代码
import cv2  
import numpy as np  


img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:\n", img1)  
# ones_like的作用为创建一个形状和img1一样的单位矩阵 
img2 = np.ones_like(img1) * 100  
print("img2:\n", img2)  
print("cv2.subtract(img1, img2):\n", cv2.subtract(img1, img2))

最终计算的结果如下所示:

乘法

和加减法一样,同样有两种方式可以进行常规的乘法运算,一种是通过乘法运算符"*",一种是通过cv2.multiply()函数。和加减法一样,前者对超过255的数值会对256取模,后者则会截止到255。

我们可以使用下列代码来感受二者的区别。

python 复制代码
import cv2  
import numpy as np  
  
  
img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:\n", img1)  
# ones_like的作用为创建一个形状和img1一样的单位矩阵 
img2 = np.ones_like(img1) * 2  
print("img2:\n", img2)  
print("img1 * img2:\n", img1 * img2)  
print("cv2.multiply(img1, img2):\n", cv2.multiply(img1, img2))

最终计算的结果如下所示:

除法

和加减乘法一样,同样有两种方式可以进行常规的除法运算,一种是通过除法运算符"/",一种是通过cv2.divide()函数。但是前者计算出来的结果会存在小数,而后者则会做相应处理,将结果四舍五入为整数。另外如果除数为0的时候,后者也会将结果置为0,因此一般情况下使用后者会更好一些。我们可以使用下列代码来感受二者的区别。

python 复制代码
import cv2  
import numpy as np  
  
  
img1 = cv2.imread("lena.bmp", flags=0)  
print("img1:\n", img1)  
# ones_like的作用为创建一个形状和img1一样的单位矩阵 
img2 = np.ones_like(img1) * 2  
# zeros_like的作用为创建一个形状和img1一样的零矩阵 
# img2 = np.zeros_like(img1)  
print("img2:\n", img2)  
print("img1 / img2:\n", img1 / img2)  
print("cv2.divide(img1, img2):\n", cv2.divide(img1, img2))

位运算

在OpenCV中,常见的位运算有以下四种:

  • cv2.bitwise_and:按位与,当两个输入位都是1时,输出才是1,否则为0
  • cv2.bitwise_or:按位或,只要有一个输入位是1,输出就是1,只有当两个都是0时,输出才是0。
  • cv2.bitwise_xor:按位异或,当两个输入位不同时,输出是1,当相同时,输出是0。
  • cv2.bitwise_not:按位取反,如果输入是1,输出是0,如果输入是0,输出是1。

下面我们以按位与为例,来演示一下位运算的用处。假如我们只想要最开始例图中的头部画面,那么就可以利用按位与的性质来实现,其代码如下所示:

python 复制代码
import cv2  
import numpy as np  
  
  
# 读取图片  
img1 = cv2.imread("lena.bmp", flags=0)  
# 建立与之进行按位与的模板  
mask = np.zeros(img1.shape, dtype=np.uint8)  
mask[50: 200, 100: 200] = 255  
mask[50: 250, 50: 100] = 255  
masked_img1 = cv2.bitwise_and(img1, mask)  
  
cv2.imshow("mask_lena", masked_img1)  
cv2.waitKey()  
cv2.destroyAllWindows()

最终我们可以得到如下结果:

总结

本篇博客主要学习了OpenCV中的算数运算操作,对于加减乘除操作而言,总体规律就是:如果使用运算符号操作,结果需要对256取模,若使用函数操作,结果则被0和255截断。对于位运算而言,很多时候可以用来构建掩膜,以找到我们感兴趣的区域。

相关推荐
CoovallyAIHub19 小时前
【一周AI风暴】周鸿祎放话“不用AI就裁员”,前谷歌CEO鼓吹对华996血拼!
深度学习·算法·计算机视觉
余衫马19 小时前
实战指南:RVC 语音转换框架
人工智能·深度学习·ubuntu
说私域19 小时前
社交媒体与兴趣电商环境下品类创新机会研究——以“开源AI智能名片链动2+1模式S2B2C商城小程序”为例
人工智能·开源·媒体
top_designer20 小时前
还在手动“磨皮”:用AI降噪+智能蒙版,构建商业摄影的自动化后期管线
图像处理·人工智能·自动化·aigc·photoshop·摄影·lightroom
SelectDB技术团队20 小时前
Apache Doris 4.0 AI 能力揭秘(二):为企业级应用而生的 AI 函数设计与实践
数据库·人工智能·apache·olap·mcp
aneasystone本尊20 小时前
梳理 Dify 应用的会话接口
人工智能
Web3&Basketball20 小时前
大语言模型LLM解决AI幻觉方法的深度分析
人工智能·语言模型·自然语言处理
.银河系.20 小时前
9.28 深度学习10
人工智能·深度学习
jie*20 小时前
小杰深度学习(two)——全连接与链式求导
图像处理·人工智能·pytorch·python·深度学习·分类·回归
Bwcx_lzp20 小时前
深度学习核心技术演进:从函数到 Transformer 架构
人工智能·深度学习·transformer