OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
图像上的算术运算

目标

  • 学习图像上的几种算术运算,例如加法、减法、按位运算等等。
  • 学习这些函数: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]
void add(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
计算两个数组或一个数组与一个标量的逐元素和。

当您添加两个图像时,这将更加明显。坚持使用 OpenCV 函数,因为它们会提供更好的结果。

图像混合

这也是图像加法,但是为了给出混合或透明的感觉,图像被赋予不同的权重。图像按照下面的公式相加

\[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\]

通过改变 \(\alpha\) 从 \(0 \rightarrow 1\),您可以在一个图像到另一个图像之间执行一个很酷的过渡。

这里我选取了两个图像进行混合。第一个图像的权重为 0.7,第二个图像的权重为 0.3。cv.addWeighted() 将以下公式应用于图像

\[dst = \alpha \cdot img1 + \beta \cdot img2 + \gamma\]

这里 \(\gamma\) 被设为零。

img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
assert img1 is not None, "无法读取文件,请检查 os.path.exists()"
assert img2 is not None, "无法读取文件,请检查 os.path.exists()"
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1)
计算两个数组的加权和。
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void destroyAllWindows()
销毁所有HighGUI窗口。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。

查看下面的结果

image

按位运算

这包括按位 AND、OR、NOT 和 XOR 运算。在提取图像的任何部分时(我们将在后面的章节中看到),定义和处理非矩形 ROI 等,它们将非常有用。下面我们将看到一个如何更改图像特定区域的示例。

我想将 OpenCV 徽标放在图像上方。如果我添加两个图像,它会改变颜色。如果我混合它们,我会得到一个透明的效果。但我希望它是完全不透明的。如果它是一个矩形区域,我可以使用 ROI,就像我们在上一章中所做的那样。但是 OpenCV 徽标不是一个矩形形状。所以你可以使用按位运算来做到这一点,如下所示

# 加载两个图像
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
assert img1 is not None, "无法读取文件,请检查 os.path.exists()"
assert img2 is not None, "无法读取文件,请检查 os.path.exists()"
# 我想将徽标放在左上角,所以我创建一个 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)
# 现在将 ROI 中徽标的区域涂黑
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
# 只从徽标图像中提取徽标区域。
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# 将徽标放在 ROI 中并修改主图像
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
void bitwise_not(InputArray src, OutputArray dst, InputArray mask=noArray())
反转数组的每个位。
void bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray())
计算两个数组的按位与运算 (dst = src1 & src2) 计算每个元素的按位...
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
将图像从一个颜色空间转换为另一个颜色空间。
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
对每个数组元素应用固定级别的阈值。

查看下面的结果。左图显示了我们创建的掩码。右图显示了最终结果。为了更好地理解,请在上面的代码中显示所有中间图像,尤其是 img1_bg 和 img2_fg。

image

练习

  1. 创建一个幻灯片,显示文件夹中的图像,并使用 cv.addWeighted 函数在图像之间进行平滑过渡