OpenCV基本图像运算

367 阅读4分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战

前言

图像处理技术是计算机视觉项目的核心,通常是计算机视觉项目中的关键工具,可以使用它们来完成各种计算机视觉任务。因此,如果要构建计算机视觉项目,就需要对图像处理有足够的了解。图像运算也是图像处理技术的一种,在本文中,将介绍可以对图像执行的常见算术运算。

饱和运算

饱和运算( saturated operation )是一种算术运算,其通过限制运算可以采用的最大值和最小值将运算限制在固定范围内。例如,对图像的某些操作(例如插值等)可能会产生超出可用范围的值,使用饱和运算就可以解决这个问题。

例如,要存储图像 img,它是对 8 位图像(值范围为 0 到 255 )执行特定操作的结果,则饱和运算计算公式如下:

result(x,y)=min(max(round(img),0),255)result(x,y)=min(max(round(img),0),255)

通过以下简单示例以更好的理解:

x = np.uint8([250])
y = np.uint8([50])
# OpenCV中加法:250+50 = 300 => 255:
result_opencv = cv2.add(x, y)
print("cv2.add(x:'{}' , y:'{}') = '{}'".format(x, y, result_opencv))
# Numpy中加法:250+50 = 300 % 256 = 44:
result_numpy = x + y
print("x:'{}' + y:'{}' = '{}'".format(x, y, result_numpy))

OpenCV 中,这些值会被裁剪至 [0, 255] 范围内,这种运算就称为饱和操作。而在 NumPy 中,值是环绕的,这也称为模运算。

图像加减法与图像混合

图像加法和减法可以分别使用 cv2.add()cv2.subtract() 函数执行。这些函数两个数组执行逐元素求和/相减,也可用于对数组和标量求和/相减。例如,对图像的所有像素添加 60,首先要构建图像以添加到原始图像:

M = np.ones(image.shape, dtype="uint8") * 60

然后,使用以下代码执行加法或减法:

added_image = cv2.add(image, M)
subtracted_image = cv2.subtract(image, M)

也可以创建一个标量并将其添加到原始图像中。例如,要给图像的所有像素加上 110,首先构建标量:

scalar = np.ones((1, 3), dtype="float") * 110

然后,使用以下代码执行加法或减法:

added_image_2 = cv2.add(image, scalar)
subtracted_image_2 = cv2.subtract(image, scalar)

代码的执行结果如下图所示:

图像加减法

从上图中,可以清楚地看到加减预定义值的效果。

图像混合也是图像相加的一种,只是其可以赋予相加以图像不同的权重,可以得到类似透明的效果,可以使用 cv2.addWeighted() 函数进行图像混合。

接下来,结合 Sobel 算子来观察 cv2.addWeighted() 函数效果。

Sobel 算子用于边缘检测,它创建一个检测到图中边缘的图像。 Sobel 算子使用两个 3 × 3 核,它们与原始图像卷积以计算导数的近似值,捕获水平和垂直梯度:

# 输出深度设置为CV_16S,以避免溢出
# CV_16S是由2字节有符号整数(16位有符号整数)组成的通道
gradient_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0, 3)
gradient_y = cv2.Sobel(gray_image, cv2.CV_16S, 0, 1, 3)

在计算出水平和垂直梯度后,可以使用函数 cv2.addWeighted() 将它们混合成图像,如下所示:

abs_gradient_x = cv2.convertScaleAbs(gradient_x)
abs_gradient_y = cv2.convertScaleAbs(gradient_y)
# 使用相同的权重混合两个图像
sobel_image = cv2.addWeighted(abs_gradient_x, 0.5, abs_gradient_y, 0.5, 0)

最后绘制图像:

def show_with_matplotlib(color_img, title, pos):
    img_RGB = color_img[:, :, ::-1]
    ax = plt.subplot(1, 4, pos)
    plt.imshow(img_RGB)
    plt.title(title, fontsize=8)
    plt.axis('off')
plt.figure(figsize=(10, 4))
plt.suptitle("Sobel operator and cv2.addWeighted() to show the output", fontsize=14, fontweight='bold')
show_with_matplotlib(image, "Image", 1)
show_with_matplotlib(cv2.cvtColor(abs_gradient_x, cv2.COLOR_GRAY2BGR), "Gradient x", 2)
show_with_matplotlib(cv2.cvtColor(abs_gradient_y, cv2.COLOR_GRAY2BGR), "Gradient y", 3)
show_with_matplotlib(cv2.cvtColor(sobel_image, cv2.COLOR_GRAY2BGR), "Sobel output", 4)
# Show the Figure:
plt.show()

代码的运行结果如下图所示:

图像混合

按位运算

OpenCV 中包含一些操作可以使用按位运算符在位级别执行,这些按位运算很简单,计算速度很快,因此,它们也是处理图像时的有用工具。

按位运算包括 ANDORNOTXOR

为了进行演示按位运算,我们首先创建一些图像:

img_1 = np.zeros((300, 300), dtype='uint8')
cv2.rectangle(img_1, (10, 10), (110, 110), (255, 255, 255), -1)
cv2.circle(img_1, (200, 200), 50, (255, 255, 255), -1)

img_2 = np.zeros((300, 300), dtype='uint8')
cv2.rectangle(img_2, (50, 50), (150, 150), (255, 255, 255), -1)
cv2.circle(img_2, (225, 200), 50, (255, 255, 255), -1)

image = cv2.imread('sigonghuiye.jpeg')
image = cv2.resize(image,(300, 300))

img_3 = np.zeros((300, 300), dtype="uint8")
cv2.circle(img_3, (150, 150), 150, (255, 255, 255), -1)

然后对所创建的图像执行按位运算:

# OR
bitwise_or = cv2.bitwise_or(img_1, img_2)
# AND
bitwise_and = cv2.bitwise_and(img_1, img_2)
# XOR
bitwise_xor = cv2.bitwise_xor(img_1, img_2)
# NOT
bitwise_not_1 = cv2.bitwise_not(img_1)
bitwise_not_2 = cv2.bitwise_not(img_2)
# AND with mask
bitwise_and_example = cv2.bitwise_and(image, image, mask=img_3)

显示运算结果:

按位运算

接下来,我们使用真实图像,进一步使用按位运算,需要注意的是,加载的真实图像应该具有相同的形状:

image = cv2.imread('8.png')
binary_image = cv2.imread('250.png')

image = image[250:500,170:420]

bitwise_and = cv2.bitwise_and(image, binary_image)
bitwise_or = cv2.bitwise_or(image, binary_image)
bitwise_xor = cv2.bitwise_xor(image, binary_image)

代码的运行结果如下,可以看到执行按位运算后看到生成的图像:

按位运算