机器人视觉系统设计与实现
在上一节中,我们已经介绍了如何使用OpenCV进行基本的图像处理和特征提取。本节将深入探讨机器人视觉系统的设计与实现,包括摄像头校准、目标检测、图像分割、深度学习模型的集成以及实时处理等方面的内容。通过本节的学习,你将能够设计并实现一个完整的机器人视觉系统,为机器人提供可靠的视觉感知能力。
摄像头校准
摄像头校准是机器人视觉系统中非常重要的一环,它能够消除相机的畸变,确保图像数据的准确性。OpenCV提供了一套完整的摄像头校准工具,可以帮助我们实现这一目标。
原理
摄像头校准的主要目的是确定相机的内参和外参。内参包括焦距、主点偏移等参数,外参包括相机相对于世界坐标系的位置和方向。通过校准,我们可以获取这些参数,从而在后续的图像处理中进行畸变校正和坐标变换。
内容
-
准备校准图像
-
检测角点
-
计算内参和外参
-
应用校正
1. 准备校准图像
为了进行摄像头校准,我们需要准备一系列校准图像。这些图像通常包含一个已知的棋盘格图案,因为棋盘格的角点位置是已知的,可以用来计算相机参数。
import cv2
import numpy as np
import glob
# 定义棋盘格的尺寸
chessboard_size = (9, 6)
# 准备角点的3D坐标
objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
# 存储3D和2D点的向量
objpoints = [] # 3D点
imgpoints = [] # 2D点
# 加载校准图像
images = glob.glob('calibration_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 寻找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
# 绘制并显示角点
cv2.drawChessboardCorners(img, chessboard_size, corners, ret)
cv2.imshow('Chessboard', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
2. 检测角点
使用cv2.findChessboardCorners
函数可以检测图像中的棋盘格角点。该函数返回一个布尔值ret
,表示是否成功找到角点。如果找到角点,我们会将这些角点的3D坐标和2D坐标分别存储在objpoints
和imgpoints
中。
3. 计算内参和外参
使用cv2.calibrateCamera
函数可以计算相机的内参和外参。该函数需要输入3D和2D点的坐标,以及图像的尺寸。
# 计算内参和外参
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# 打印内参矩阵和畸变系数
print("内参矩阵:\n", mtx)
print("畸变系数:\n", dist)
4. 应用校正
使用cv2.undistort
函数可以对图像进行畸变校正。该函数需要输入畸变校正后的内参矩阵和畸变系数。
# 应用畸变校正
for fname in images:
img = cv2.imread(fname)
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibrated_' + fname, dst)
# 显示校正后的图像
cv2.imshow('Calibrated', dst)
cv2.waitKey(500)
cv2.destroyAllWindows()
代码解释
-
准备校准图像:
-
chessboard_size
定义了棋盘格的尺寸。 -
objp
是棋盘格角点的3D坐标。 -
objpoints
和imgpoints
分别存储3D和2D点的坐标。 -
glob
模块用于加载校准图像。
-
-
检测角点:
-
cv2.findChessboardCorners
函数用于检测图像中的棋盘格角点。 -
cv2.drawChessboardCorners
函数用于在图像中标记找到的角点。
-
-
计算内参和外参:
-
cv2.calibrateCamera
函数用于计算相机的内参和外参。 -
mtx
是内参矩阵,dist
是畸变系数。
-
-
应用校正:
-
cv2.getOptimalNewCameraMatrix
函数用于获取新的相机矩阵,以便在消除畸变时保持图像的分辨率。 -
cv2.undistort
函数用于对图像进行畸变校正。 -
roi
是校正后的图像的感兴趣区域,用于裁剪图像。
-
目标检测
目标检测是机器人视觉中的一项基本任务,用于识别和定位图像中的特定对象。OpenCV提供了多种目标检测算法,如HOG、SIFT、SURF等。本节将介绍如何使用这些算法进行目标检测。
原理
目标检测的基本原理是通过特征提取和匹配,识别图像中的特定对象。常见的特征提取方法包括HOG(方向梯度直方图)、SIFT(尺度不变特征变换)和SURF(加速稳健特征)等。这些特征提取方法可以帮助我们识别不同尺度和旋转下的目标。
内容
-
使用HOG进行目标检测
-
使用SIFT进行目标检测
-
使用SURF进行目标检测
1. 使用HOG进行目标检测
HOG(方向梯度直方图)是一种广泛用于目标检测的特征描述子。它通过计算图像中每个小区域的梯度方向直方图来描述对象的形状。
import cv2
import numpy as np
# 加载图像
img = cv2.imread('test_image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 初始化HOG描述子
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
# 检测行人
found, w = hog.detectMultiScale(gray, winStride=(8, 8), padding=(32, 32), scale=1.05)
# 绘制检测结果
for (x, y, w, h) in found:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('HOG Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
cv2.cvtColor
函数用于将图像转换为灰度图像。
-
-
初始化HOG描述子:
-
cv2.HOGDescriptor
类用于创建HOG描述子。 -
cv2.HOGDescriptor_getDefaultPeopleDetector
函数用于获取预训练的SVM检测器,用于检测行人。
-
-
检测行人:
-
hog.detectMultiScale
函数用于在图像中检测行人。 -
winStride
参数表示滑动窗口的步长。 -
padding
参数表示滑动窗口的填充。 -
scale
参数表示图像金字塔的缩放比例。
-
-
绘制检测结果:
cv2.rectangle
函数用于在图像中标记检测到的行人。
2. 使用SIFT进行目标检测
SIFT(尺度不变特征变换)是一种用于图像特征提取和匹配的算法。它可以在不同的尺度和旋转下识别目标。
import cv2
import numpy as np
# 加载图像
img1 = cv2.imread('object_image.jpg', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('scene_image.jpg', cv2.IMREAD_GRAYSCALE)
# 初始化SIFT检测器
sift = cv2.SIFT_create()
# 检测关键点和计算描述子
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 初始化FLANN匹配器
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 进行特征匹配
matches = flann.knnMatch(des1, des2, k=2)
# 筛选匹配点
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
# 获取匹配点的坐标
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
# 计算单应性矩阵
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# 获取目标边界
h, w = img1.shape
pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, M)
# 绘制匹配结果
img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
draw_params = dict(matchColor=(0, 255, 0), singlePointColor=None, matchesMask=mask.ravel().tolist(), flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, **draw_params)
cv2.imshow('SIFT Detection', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
cv2.IMREAD_GRAYSCALE
参数表示读取灰度图像。
-
-
初始化SIFT检测器:
cv2.SIFT_create
函数用于创建SIFT检测器。
-
检测关键点和计算描述子:
sift.detectAndCompute
函数用于检测关键点并计算描述子。
-
初始化FLANN匹配器:
-
FLANN_INDEX_KDTREE
表示使用KD树算法。 -
index_params
和search_params
分别用于配置FLANN匹配器的索引和搜索参数。 -
cv2.FlannBasedMatcher
类用于创建FLANN匹配器。
-
-
进行特征匹配:
-
flann.knnMatch
函数用于进行特征匹配。 -
good_matches
用于存储筛选后的匹配点。
-
-
获取匹配点的坐标:
src_pts
和dst_pts
分别存储源图像和目标图像的匹配点坐标。
-
计算单应性矩阵:
-
cv2.findHomography
函数用于计算单应性矩阵。 -
cv2.RANSAC
参数表示使用RANSAC算法进行鲁棒估计。
-
-
获取目标边界:
cv2.perspectiveTransform
函数用于计算目标的边界。
-
绘制匹配结果:
-
cv2.polylines
函数用于在图像中绘制目标边界。 -
cv2.drawMatches
函数用于绘制匹配点。
-
3. 使用SURF进行目标检测
SURF(加速稳健特征)是SIFT的加速版本,适用于实时目标检测。它通过使用积分图像和快速近似算法来提高检测速度。
import cv2
import numpy as np
# 加载图像
img1 = cv2.imread('object_image.jpg', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('scene_image.jpg', cv2.IMREAD_GRAYSCALE)
# 初始化SURF检测器
surf = cv2.xfeatures2d.SURF_create(400)
# 检测关键点和计算描述子
kp1, des1 = surf.detectAndCompute(img1, None)
kp2, des2 = surf.detectAndCompute(img2, None)
# 初始化FLANN匹配器
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 进行特征匹配
matches = flann.knnMatch(des1, des2, k=2)
# 筛选匹配点
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
# 获取匹配点的坐标
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
# 计算单应性矩阵
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# 获取目标边界
h, w = img1.shape
pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, M)
# 绘制匹配结果
img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
draw_params = dict(matchColor=(0, 255, 0), singlePointColor=None, matchesMask=mask.ravel().tolist(), flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, **draw_params)
cv2.imshow('SURF Detection', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
cv2.IMREAD_GRAYSCALE
参数表示读取灰度图像。
-
-
初始化SURF检测器:
-
cv2.xfeatures2d.SURF_create
函数用于创建SURF检测器。 -
400
参数表示检测器的阈值。
-
-
检测关键点和计算描述子:
surf.detectAndCompute
函数用于检测关键点并计算描述子。
-
初始化FLANN匹配器:
-
FLANN_INDEX_KDTREE
表示使用KD树算法。 -
index_params
和search_params
分别用于配置FLANN匹配器的索引和搜索参数。 -
cv2.FlannBasedMatcher
类用于创建FLANN匹配器。
-
-
进行特征匹配:
-
flann.knnMatch
函数用于进行特征匹配。 -
good_matches
用于存储筛选后的匹配点。
-
-
获取匹配点的坐标:
src_pts
和dst_pts
分别存储源图像和目标图像的匹配点坐标。
-
计算单应性矩阵:
-
cv2.findHomography
函数用于计算单应性矩阵。 -
cv2.RANSAC
参数表示使用RANSAC算法进行鲁棒估计。
-
-
获取目标边界:
cv2.perspectiveTransform
函数用于计算目标的边界。
-
绘制匹配结果:
-
cv2.polylines
函数用于在图像中绘制目标边界。 -
cv2.drawMatches
函数用于绘制匹配点。
-
图像分割
图像分割是将图像划分为多个区域或对象的过程。在机器人视觉中,图像分割可以帮助我们更好地理解场景,识别不同的对象。OpenCV提供了多种图像分割算法,如阈值分割、区域生长、GrabCut等。本节将介绍如何使用这些算法进行图像分割。
原理
图像分割的基本原理是通过不同的特征(如颜色、纹理、边缘等)将图像划分为多个区域。每个区域代表一个特定的对象或背景。常见的图像分割方法包括阈值分割、区域生长、GrabCut等。
内容
-
阈值分割
-
区域生长
-
GrabCut
1. 阈值分割
阈值分割是一种简单的图像分割方法,通过设置一个或多个阈值将图像中的像素划分为不同的区域。
import cv2
import numpy as np
# 加载图像
img = cv2.imread('test_image.jpg', cv2.IMREAD_GRAYSCALE)
# 应用阈值分割
ret, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 显示结果
cv2.imshow('Thresholded Image', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
cv2.IMREAD_GRAYSCALE
参数表示读取灰度图像。
-
-
应用阈值分割:
-
cv2.threshold
函数用于对图像进行阈值分割。该函数需要输入图像、阈值、最大值和阈值类型。 -
127
是阈值,表示将像素值小于127的像素设为0(黑色),大于或等于127的像素设为255(白色)。 -
cv2.THRESH_BINARY
表示使用二值化阈值分割。
-
-
显示结果:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey
函数用于等待用户按键,0
表示无限等待。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
2. 区域生长
区域生长是一种基于像素相似性的图像分割方法。通过选择一个种子点,然后逐步将与种子点相似的像素添加到同一区域,最终形成一个完整的分割区域。
import cv2
import numpy as np
# 加载图像
img = cv2.imread('test_image.jpg', cv2.IMREAD_GRAYSCALE)
# 选择种子点
seed_point = (50, 50)
# 定义区域生长函数
def region_growing(image, seed, threshold=10):
height, width = image.shape
seed_value = image[seed[1], seed[0]]
segmented = np.zeros_like(image)
segmented[seed[1], seed[0]] = 255
stack = [seed]
while stack:
x, y = stack.pop()
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = x + dx, y + dy
if 0 <= nx < width and 0 <= ny < height and segmented[ny, nx] == 0:
if abs(int(image[ny, nx]) - int(seed_value)) < threshold:
segmented[ny, nx] = 255
stack.append((nx, ny))
return segmented
# 应用区域生长
segmented = region_growing(img, seed_point)
# 显示结果
cv2.imshow('Region Growing', segmented)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
cv2.IMREAD_GRAYSCALE
参数表示读取灰度图像。
-
-
选择种子点:
seed_point
是一个元组,表示种子点的坐标。
-
定义区域生长函数:
-
region_growing
函数用于实现区域生长算法。 -
image
是输入的灰度图像。 -
seed
是种子点的坐标。 -
threshold
是像素值相似性的阈值。 -
height
和width
分别表示图像的高度和宽度。 -
seed_value
是种子点的像素值。 -
segmented
是一个与输入图像相同大小的二维数组,用于存储分割后的图像。 -
stack
是一个栈,用于存储待处理的像素点。 -
通过遍历种子点周围的像素,如果像素值与种子点的像素值差小于阈值,则将其标记为同一区域,并将其添加到栈中,继续处理。
-
-
应用区域生长:
region_growing
函数被调用,传入图像和种子点,返回分割后的图像。
-
显示结果:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey
函数用于等待用户按键,0
表示无限等待。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
3. GrabCut
GrabCut是一种基于图割理论的交互式图像分割方法。它通过用户提供的矩形区域或种子点,自动分割出感兴趣的对象。
import cv2
import numpy as np
# 加载图像
img = cv2.imread('test_image.jpg')
mask = np.zeros(img.shape[:2], np.uint8)
# 定义前景和背景模型
bgd_model = np.zeros((1, 65), np.float64)
fgd_model = np.zeros((1, 65), np.float64)
# 选择感兴趣区域
rect = (50, 50, 300, 400)
# 应用GrabCut
cv2.grabCut(img, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)
# 提取前景
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img = img * mask2[:, :, np.newaxis]
# 显示结果
cv2.imshow('GrabCut', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
mask
是一个与输入图像相同大小的二维数组,用于存储分割结果。
-
-
定义前景和背景模型:
bgd_model
和fgd_model
分别用于存储背景和前景模型的初始值。
-
选择感兴趣区域:
rect
是一个元组,表示用户提供的矩形区域的坐标和尺寸。
-
应用GrabCut:
-
cv2.grabCut
函数用于进行图像分割。该函数需要输入图像、掩码、矩形区域、背景模型、前景模型、迭代次数和初始化方法。 -
5
表示迭代次数。 -
cv2.GC_INIT_WITH_RECT
表示使用矩形区域进行初始化。
-
-
提取前景:
-
mask2
是一个二值掩码,用于提取前景区域。 -
img * mask2[:, :, np.newaxis]
将前景区域与原图像相乘,得到分割后的前景图像。
-
-
显示结果:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey
函数用于等待用户按键,0
表示无限等待。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
深度学习模型的集成
深度学习模型在目标检测和图像分割等任务中表现出色。本节将介绍如何将深度学习模型集成到机器人视觉系统中,以提高系统的准确性和鲁棒性。
原理
深度学习模型通过学习大量的标注数据,可以自动提取图像中的特征并进行分类或分割。常见的深度学习模型包括卷积神经网络(CNN)、YOLO、Mask R-CNN等。这些模型可以用于检测和识别图像中的目标,以及分割图像中的不同区域。
内容
-
使用YOLO进行目标检测
-
使用Mask R-CNN进行实例分割
1. 使用YOLO进行目标检测
YOLO(You Only Look Once)是一种实时目标检测算法,通过一个单一的神经网络同时进行目标的分类和定位。
import cv2
import numpy as np
# 加载YOLO模型
net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg')
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 加载类别名称
with open('coco.names', 'r') as f:
classes = [line.strip() for line in f.readlines()]
# 加载图像
img = cv2.imread('test_image.jpg')
height, width, channels = img.shape
# 进行图像预处理
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
outs = net.forward(output_layers)
# 解析输出
class_ids = []
confidences = []
boxes = []
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# 应用非最大抑制
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
# 绘制检测结果
font = cv2.FONT_HERSHEY_PLAIN
colors = np.random.uniform(0, 255, size=(len(classes), 3))
for i in range(len(boxes)):
if i in indexes:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
color = colors[class_ids[i]]
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, label, (x, y - 10), font, 1, color, 2)
# 显示结果
cv2.imshow('YOLO Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载YOLO模型:
-
cv2.dnn.readNet
函数用于加载YOLO模型的权重文件和配置文件。 -
net.getLayerNames
和net.getUnconnectedOutLayers
用于获取模型的输出层名称。
-
-
加载类别名称:
coco.names
文件包含COCO数据集中的类别名称。
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
height
、width
和channels
分别表示图像的高度、宽度和通道数。
-
-
进行图像预处理:
-
cv2.dnn.blobFromImage
函数用于将图像转换为模型输入所需的格式。 -
0.00392
是图像归一化系数。 -
(416, 416)
是输入图像的尺寸。 -
(0, 0, 0)
是均值减去值。 -
True
表示交换图像的R和B通道。 -
crop=False
表示不裁剪图像。
-
-
解析输出:
-
outs
是模型的输出,包含检测结果。 -
class_ids
、confidences
和boxes
分别存储检测到的目标的类别、置信度和边界框。
-
-
应用非最大抑制:
cv2.dnn.NMSBoxes
函数用于去除冗余的检测框,保留置信度最高的框。
-
绘制检测结果:
-
cv2.rectangle
函数用于在图像中标记检测到的目标。 -
cv2.putText
函数用于在图像中显示目标的类别名称。
-
-
显示结果:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey
函数用于等待用户按键,0
表示无限等待。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
2. 使用Mask R-CNN进行实例分割
Mask R-CNN是一种扩展了Faster R-CNN的目标检测和实例分割算法。它不仅能够检测目标,还能生成目标的分割掩码。
import cv2
import numpy as np
import torch
import torchvision
# 加载Mask R-CNN模型
model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)
model.eval()
# 加载类别名称
with open('coco.names', 'r') as f:
classes = [line.strip() for line in f.readlines()]
# 加载图像
img = cv2.imread('test_image.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_tensor = torch.from_numpy(img / 255.0).permute(2, 0, 1).float().unsqueeze(0)
# 进行图像预处理
with torch.no_grad():
predictions = model(img_tensor)
# 解析输出
boxes = predictions[0]['boxes'].cpu().numpy()
masks = predictions[0]['masks'].cpu().numpy()
scores = predictions[0]['scores'].cpu().numpy()
labels = predictions[0]['labels'].cpu().numpy()
# 过滤置信度低的检测结果
min_confidence = 0.5
boxes = boxes[scores > min_confidence]
masks = masks[scores > min_confidence]
labels = labels[scores > min_confidence]
# 绘制检测结果
for i in range(len(boxes)):
x1, y1, x2, y2 = boxes[i]
label = str(classes[labels[i] - 1])
mask = masks[i][0]
mask = (mask > 0.5).astype(np.uint8)
# 绘制边界框
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 绘制分割掩码
colored_mask = np.zeros_like(img, dtype=np.uint8)
colored_mask[:, :, 1] = mask * 255
img = cv2.addWeighted(img, 1, colored_mask, 0.5, 0)
# 显示类别名称
cv2.putText(img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 2)
# 显示结果
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
cv2.imshow('Mask R-CNN Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码解释
-
加载Mask R-CNN模型:
-
torchvision.models.detection.maskrcnn_resnet50_fpn
函数用于加载预训练的Mask R-CNN模型。 -
model.eval
将模型设置为评估模式。
-
-
加载类别名称:
coco.names
文件包含COCO数据集中的类别名称。
-
加载图像:
-
cv2.imread
函数用于读取图像。 -
cv2.cvtColor
函数将图像从BGR格式转换为RGB格式。 -
torch.from_numpy
函数将图像转换为PyTorch张量。 -
permute
函数用于调整张量的维度顺序。 -
unsqueeze
函数用于增加一个批次维度。
-
-
进行图像预处理:
model
在torch.no_grad
上下文中进行推理,以禁用梯度计算。
-
解析输出:
-
predictions
包含模型的输出,包括边界框、分割掩码、置信度和类别标签。 -
boxes
、masks
、scores
和labels
分别存储检测到的目标的边界框、分割掩码、置信度和类别标签。
-
-
过滤置信度低的检测结果:
-
min_confidence
表示置信度阈值。 -
通过筛选,保留置信度大于阈值的检测结果。
-
-
绘制检测结果:
-
cv2.rectangle
函数用于在图像中标记检测到的目标。 -
cv2.addWeighted
函数用于将分割掩码与原图像进行加权混合,生成带有掩码的图像。 -
cv2.putText
函数用于在图像中显示目标的类别名称。
-
-
显示结果:
-
cv2.cvtColor
函数将图像从RGB格式转换回BGR格式。 -
cv2.imshow
函数用于显示图像。 -
cv2.waitKey
函数用于等待用户按键,0
表示无限等待。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
实时处理
在机器人视觉应用中,实时处理是非常重要的。本节将介绍如何使用OpenCV进行实时的图像处理,包括摄像头捕获、目标检测、图像分割等。
原理
实时处理的基本原理是通过摄像头连续捕获图像,并在每一帧图像上进行处理。处理速度需要足够快,以满足实时应用的要求。
内容
-
摄像头捕获
-
实时目标检测
-
实时图像分割
1. 摄像头捕获
使用OpenCV的VideoCapture
类可以方便地从摄像头捕获图像。
import cv2
# 初始化摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取一帧图像
ret, frame = cap.read()
if not ret:
break
# 显示图像
cv2.imshow('Camera Capture', frame)
# 按'q'键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头并关闭窗口
cap.release()
cv2.destroyAllWindows()
代码解释
-
初始化摄像头:
cv2.VideoCapture(0)
函数用于初始化摄像头,0
表示第一个摄像头。
-
读取一帧图像:
-
cap.read
函数用于读取一帧图像,返回一个布尔值ret
和图像帧frame
。 -
if not ret
表示如果读取失败,退出循环。
-
-
显示图像:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey(1)
函数用于等待1毫秒,0xFF
表示按键的ASCII码。 -
ord('q')
表示’q’键的ASCII码,用于检测用户是否按下了’q’键。
-
-
释放摄像头并关闭窗口:
-
cap.release
函数用于释放摄像头资源。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
2. 实时目标检测
在实时应用中,我们可以结合摄像头捕获和目标检测算法,实现实时的目标检测。
import cv2
import numpy as np
# 加载YOLO模型
net = cv2.dnn.readNet### 续写:实时目标检测
在实时应用中,我们可以结合摄像头捕获和目标检测算法,实现实时的目标检测。以下是一个使用YOLO模型进行实时目标检测的示例代码。
```python
import cv2
import numpy as np
# 加载YOLO模型
net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg')
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 加载类别名称
with open('coco.names', 'r') as f:
classes = [line.strip() for line in f.readlines()]
# 初始化摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取一帧图像
ret, frame = cap.read()
if not ret:
break
# 获取图像的尺寸
height, width, channels = frame.shape
# 进行图像预处理
blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
outs = net.forward(output_layers)
# 解析输出
class_ids = []
confidences = []
boxes = []
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# 应用非最大抑制
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
# 绘制检测结果
font = cv2.FONT_HERSHEY_PLAIN
colors = np.random.uniform(0, 255, size=(len(classes), 3))
for i in range(len(boxes)):
if i in indexes:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
color = colors[class_ids[i]]
cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
cv2.putText(frame, label, (x, y - 10), font, 1, color, 2)
# 显示图像
cv2.imshow('Real-time YOLO Detection', frame)
# 按'q'键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头并关闭窗口
cap.release()
cv2.destroyAllWindows()
代码解释
-
加载YOLO模型:
-
cv2.dnn.readNet
函数用于加载YOLO模型的权重文件和配置文件。 -
net.getLayerNames
和net.getUnconnectedOutLayers
用于获取模型的输出层名称。
-
-
加载类别名称:
coco.names
文件包含COCO数据集中的类别名称。
-
初始化摄像头:
cv2.VideoCapture(0)
函数用于初始化摄像头,0
表示第一个摄像头。
-
读取一帧图像:
-
cap.read
函数用于读取一帧图像,返回一个布尔值ret
和图像帧frame
。 -
if not ret
表示如果读取失败,退出循环。
-
-
获取图像的尺寸:
height
、width
和channels
分别表示图像的高度、宽度和通道数。
-
进行图像预处理:
-
cv2.dnn.blobFromImage
函数用于将图像转换为模型输入所需的格式。 -
0.00392
是图像归一化系数。 -
(416, 416)
是输入图像的尺寸。 -
(0, 0, 0)
是均值减去值。 -
True
表示交换图像的R和B通道。 -
crop=False
表示不裁剪图像。 -
net.setInput(blob)
将预处理后的图像设置为模型的输入。 -
net.forward(output_layers)
进行前向传播,获取模型的输出。
-
-
解析输出:
-
outs
是模型的输出,包含检测结果。 -
class_ids
、confidences
和boxes
分别存储检测到的目标的类别、置信度和边界框。
-
-
应用非最大抑制:
cv2.dnn.NMSBoxes
函数用于去除冗余的检测框,保留置信度最高的框。
-
绘制检测结果:
-
cv2.rectangle
函数用于在图像中标记检测到的目标。 -
cv2.putText
函数用于在图像中显示目标的类别名称。
-
-
显示图像:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey(1)
函数用于等待1毫秒,0xFF
表示按键的ASCII码。 -
ord('q')
表示’q’键的ASCII码,用于检测用户是否按下了’q’键。
-
-
释放摄像头并关闭窗口:
-
cap.release
函数用于释放摄像头资源。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
3. 实时图像分割
在实时应用中,我们可以结合摄像头捕获和图像分割算法,实现实时的图像分割。以下是一个使用Mask R-CNN模型进行实时图像分割的示例代码。
import cv2
import numpy as np
import torch
import torchvision
# 加载Mask R-CNN模型
model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)
model.eval()
# 加载类别名称
with open('coco.names', 'r') as f:
classes = [line.strip() for line in f.readlines()]
# 初始化摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取一帧图像
ret, frame = cap.read()
if not ret:
break
# 将图像转换为RGB格式
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame_tensor = torch.from_numpy(frame / 255.0).permute(2, 0, 1).float().unsqueeze(0)
# 进行图像预处理
with torch.no_grad():
predictions = model(frame_tensor)
# 解析输出
boxes = predictions[0]['boxes'].cpu().numpy()
masks = predictions[0]['masks'].cpu().numpy()
scores = predictions[0]['scores'].cpu().numpy()
labels = predictions[0]['labels'].cpu().numpy()
# 过滤置信度低的检测结果
min_confidence = 0.5
boxes = boxes[scores > min_confidence]
masks = masks[scores > min_confidence]
labels = labels[scores > min_confidence]
# 绘制检测结果
for i in range(len(boxes)):
x1, y1, x2, y2 = boxes[i]
label = str(classes[labels[i] - 1])
mask = masks[i][0]
mask = (mask > 0.5).astype(np.uint8)
# 绘制边界框
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 绘制分割掩码
colored_mask = np.zeros_like(frame, dtype=np.uint8)
colored_mask[:, :, 1] = mask * 255
frame = cv2.addWeighted(frame, 1, colored_mask, 0.5, 0)
# 显示类别名称
cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 2)
# 将图像转换回BGR格式
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
# 显示图像
cv2.imshow('Real-time Mask R-CNN Segmentation', frame)
# 按'q'键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头并关闭窗口
cap.release()
cv2.destroyAllWindows()
代码解释
-
加载Mask R-CNN模型:
-
torchvision.models.detection.maskrcnn_resnet50_fpn
函数用于加载预训练的Mask R-CNN模型。 -
model.eval
将模型设置为评估模式。
-
-
加载类别名称:
coco.names
文件包含COCO数据集中的类别名称。
-
初始化摄像头:
cv2.VideoCapture(0)
函数用于初始化摄像头,0
表示第一个摄像头。
-
读取一帧图像:
-
cap.read
函数用于读取一帧图像,返回一个布尔值ret
和图像帧frame
。 -
if not ret
表示如果读取失败,退出循环。
-
-
将图像转换为RGB格式:
-
cv2.cvtColor
函数将图像从BGR格式转换为RGB格式。 -
torch.from_numpy
函数将图像转换为PyTorch张量。 -
permute
函数用于调整张量的维度顺序。 -
unsqueeze
函数用于增加一个批次维度。
-
-
进行图像预处理:
model
在torch.no_grad
上下文中进行推理,以禁用梯度计算。
-
解析输出:
-
predictions
包含模型的输出,包括边界框、分割掩码、置信度和类别标签。 -
boxes
、masks
、scores
和labels
分别存储检测到的目标的边界框、分割掩码、置信度和类别标签。
-
-
过滤置信度低的检测结果:
-
min_confidence
表示置信度阈值。 -
通过筛选,保留置信度大于阈值的检测结果。
-
-
绘制检测结果:
-
cv2.rectangle
函数用于在图像中标记检测到的目标。 -
cv2.addWeighted
函数用于将分割掩码与原图像进行加权混合,生成带有掩码的图像。 -
cv2.putText
函数用于在图像中显示目标的类别名称。
-
-
将图像转换回BGR格式:
cv2.cvtColor
函数将图像从RGB格式转换回BGR格式。
-
显示图像:
-
cv2.imshow
函数用于显示图像。 -
cv2.waitKey(1)
函数用于等待1毫秒,0xFF
表示按键的ASCII码。 -
ord('q')
表示’q’键的ASCII码,用于检测用户是否按下了’q’键。
-
-
释放摄像头并关闭窗口:
-
cap.release
函数用于释放摄像头资源。 -
cv2.destroyAllWindows
函数用于关闭所有图像窗口。
-
通过以上内容,你已经了解了如何设计和实现一个完整的机器人视觉系统,包括摄像头校准、目标检测、图像分割和实时处理。这些技术的结合可以为机器人提供强大的视觉感知能力,使其在各种应用场景中更加智能和可靠。希望这些内容对你有所帮助,祝你在机器人视觉领域取得更大的进展!