计算机视觉是人工智能的一个分支,它使计算机能够从图像、视频和其他视觉输入中提取有意义的信息,并采取必要的行动。例如,自动驾驶汽车、自动交通管理、监控、基于图像的质量检查等。
OpenCV是一个主要针对计算机视觉的库。它包含了在计算机视觉工作中需要的所有工具。'Open'代表开源,'CV'代表计算机视觉。
本文包含了使用OpenCV库开始计算机视觉所需的一切。将对计算机视觉感到更自信、更高效。所有的代码和数据都在这里。
首先,来了解如何读取和显示图像,这是计算机视觉的基础。
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread('../input/images-for-computer-vision/tiger1.jpg')
print(type(img))
print(img.shape)
'img'包含了以numpy数组形式的图像。让打印它的类型和形状。numpy数组的形状为(667, 1200, 3),其中,667是图像高度,1200是图像宽度,3是通道数。在这种情况下,有RGB通道,所以有3个。原始图像以RGB形式存在,但OpenCV默认以BGR读取图像,所以需要在显示之前将其转换回RGB。
可以在图像上绘制线条、形状和文本。
# 绘制矩形
color = (240, 150, 240) # 矩形的颜色
cv.rectangle(img, (100, 100), (300, 300), color, thickness=10, lineType=8) # 填充矩形使用thickness = -1
# 绘制圆形
color = (150, 260, 50)
cv.circle(img, (650, 350), 100, color, thickness=10) # 填充圆形使用thickness = -1
# 添加文本
color = (50, 200, 100)
font = cv.FONT_HERSHEY_SCRIPT_COMPLEX
cv.putText(img, 'Save Tigers', (200, 150), font, 5, color, thickness=5, lineType=20)
img_convert = cv.cvtColor(img, cv.COLOR_BGR2RGB)
plt.imshow(img_convert)
还可以混合两个或更多的图像。图像不过是数字,可以加、减、乘、除数字,因此也可以混合图像。需要注意的是,图像的大小应该相同。
def myplot(images, titles):
fig, axs = plt.subplots(1, len(images), sharey=True)
fig.set_figwidth(15)
for img, ax, title in zip(images, axs, titles):
if img.shape[-1] == 3:
img = cv.cvtColor(img, cv.COLOR_BGR2RGB) # OpenCV读取图像为BGR,所以转换回RGB
else:
img = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
ax.imshow(img)
ax.set_title(title)
img1 = cv.imread('../input/images-for-computer-vision/tiger1.jpg')
img2 = cv.imread('../input/images-for-computer-vision/horse.jpg')
img1_resize = cv.resize(img1, (img2.shape[1], img2.shape[0]))
img_add = cv.add(img1_resize, img2)
img_subtract = cv.subtract(img1_resize, img2)
img_multiply = cv.multiply(img1_resize, img2)
img_divide = cv.divide(img1_resize, img2)
img_blend = cv.addWeighted(img1_resize, 0.3, img2, 0.7, 0) # 30%老虎和70%马
myplot([img1_resize, img2], ['老虎', '马'])
myplot([img_add, img_subtract, img_multiply, img_divide, img_blend], ['加法', '减法', '乘法', '除法', '混合'])
乘法图像几乎是白色的,除法图像是黑色的,这是因为白色代表255,黑色代表0。当乘以两个图像的像素值时,得到一个更高的数字,所以它的颜色变成白色或接近白色,除法图像则相反。
图像变换包括平移、旋转、缩放、剪切和翻转图像。
img = cv.imread('../input/images-for-computer-vision/tiger1.jpg')
width, height, _ = img.shape
# 平移
M_translate = np.float32([[1, 0, 200], [0, 1, 100]]) # 200=>沿x轴平移,100=>沿y轴平移
img_translate = cv.warpAffine(img, M_translate, (height, width))
# 旋转
center = (width / 2, height / 2)
M_rotate = cv.getRotationMatrix2D(center, angle=90, scale=1)
img_rotate = cv.warpAffine(img, M_rotate, (width, height))
# 缩放
scale_percent = 50
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
img_scale = cv.resize(img, dim, interpolation=cv.INTER_AREA)
# 翻转
img_flip = cv.flip(img, 1) # 0:沿水平轴,1:沿垂直轴,-1:先垂直后水平
# 剪切
srcTri = np.array([[0, 0], [img.shape[1] - 1, 0], [0, img.shape[0] - 1]]).astype(np.float32)
dstTri = np.array([[0, img.shape[1] * 0.33], [img.shape[1] * 0.85, img.shape[0] * 0.25], [img.shape[1] * 0.15, img.shape[0] * 0.7]]).astype(np.float32)
warp_mat = cv.getAffineTransform(srcTri, dstTri)
img_warp = cv.warpAffine(img, warp_mat, (height, width))
myplot([img, img_translate, img_rotate, img_scale, img_flip, img_warp],
['原始图像', '平移后的图像', '旋转后的图像', '缩放后的图像', '翻转后的图像', '剪切后的图像'])
图像预处理包括阈值处理、滤波等操作。
在阈值处理中,小于阈值的像素值变为0(黑色),大于阈值的像素值变为255(白色)。选择的阈值是150,但可以选择任何其他数字。
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def plot_3d(img1, img2, titles):
fig = make_subplots(rows=1, cols=2,
specs=[[{'is_3d': True}, {'is_3d': True}]],
subplot_titles=[titles[0], titles[1]],
)
x, y = np.mgrid[0:img1.shape[0], 0:img1.shape[1]]
fig.add_trace(go.Surface(x=x, y=y, z=img1[:, :, 0]), row=1, col=1)
fig.add_trace(go.Surface(x=x, y=y, z=img2[:, :, 0]), row=1, col=2)
fig.update_traces(contours_z=dict(show=True, usecolormap=True,
highlightcolor="limegreen", project_z=True))
fig.show()
img = cv.imread('../input/images-for-computer-vision/simple_shapes.png')
_, img_threshold = cv.threshold(img, 150, 255, cv.THRESH_BINARY)
plot_3d(img, img_threshold, ['原始图像', '阈值图像=150'])
应用阈值处理后,值为150的值变为255。
图像滤波是通过改变像素值来改变图像的外观。每种类型的滤波器根据相应的数学公式改变像素值。这里不详细介绍数学知识,但将展示每个滤波器如何通过3D可视化工作。如果对滤波器背后的数学感兴趣,可以查看相关资料。
img = cv.imread('../input/images-for-computer-vision/simple_shapes.png')
# 高斯滤波器
ksize = (11, 11) # 两者都应该是奇数
img_gaussian = cv.GaussianBlur(img, ksize, 0)
plot_3d(img, img_gaussian, ['原始图像', '高斯图像'])
# 中值滤波器
ksize = 11
img_median = cv.medianBlur(img, ksize)
plot_3d(img, img_median, ['原始图像', '中值模糊'])
# 双边滤波器
img_bilateral = cv.bilateralFilter(img, d=5, sigmaColor=50, sigmaSpace=5)
myplot([img, img_bilateral], ['原始图像', '双边模糊图像'])
plot_3d(img, img_bilateral, ['原始图像', '双边模糊'])
高斯滤波器:通过去除细节和噪声来模糊图像。更多细节,可以阅读相关资料。中值滤波器:非线性过程,有助于减少冲动性或椒盐噪声。双边滤波器:保持边缘,并减少噪声平滑。简而言之,滤波器有助于减少或去除噪声,这是亮度或颜色的随机变化,这被称为平滑。
特征检测是一种在每个图像点进行局部决策的方法,通过计算图像信息的抽象。例如,对于人脸图像,特征是眼睛、鼻子、嘴唇、耳朵等,尝试识别这些特征。
img = cv.imread('../input/images-for-computer-vision/simple_shapes.png')
img_canny1 = cv.Canny(img, 50, 200)
# 在将图像输入Canny之前平滑图像以去除噪声
filter_img = cv.GaussianBlur(img, (7, 7), 0)
img_canny2 = cv.Canny(filter_img, 50, 200)
myplot([img, img_canny1, img_canny2],
['原始图像', 'Canny边缘检测器(无平滑)', 'Canny边缘检测器(有平滑)'])
这里使用的是Canny边缘检测器,它是一个边缘检测算子,使用多阶段算法检测图像中的广泛边缘。它由John F. Canny在1986年开发。这里不详细介绍Canny的工作原理,但关键是它用于提取边缘。要了解更多关于其工作原理的信息,可以查看相关资料。在应用Canny边缘检测方法之前,平滑图像以去除噪声。从图像中可以看到,平滑后得到了清晰的边缘。
img = cv.imread('../input/images-for-computer-vision/simple_shapes.png')
img_copy = img.copy()
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
_, img_binary = cv.threshold(img_gray, 50, 200, cv.THRESH_BINARY)
# 腐蚀和膨胀以平滑轮廓
img_binary_erode = cv.erode(img_binary, (10, 10), iterations=5)
img_binary_dilate = cv.dilate(img_binary, (10, 10), iterations=5)
contours, hierarchy = cv.findContours(img_binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(img, contours, -1, (0, 0, 255), 3) # 在原始图像上绘制轮廓
myplot([img_copy, img], ['原始图像', '图像中的轮廓'])