保存时间:2026/4/2 17:57:06
| 维度 | 多模态大模型(文生视频) | 纯记忆模型(文生记忆) |
|---|---|---|
| 核心目标 | 基于语义生成 “合理且新颖” 的视觉内容 | 基于索引 “精确复现” 已存储的记忆片段 |
| 数据处理 | 不存储原始视频,只学习特征分布规律 | 压缩存储原始视频特征,并保留索引映射关系 |
| 输出特性 | 每次生成结果有随机性(如细节、视角变化) | 输出结果确定性高(重复输入返回相同片段) |
| 关键技术 | 扩散模型(Diffusion)、Transformer 生成架构 | 特征提取(CNN / 自编码器)、向量检索(ANN) |
| 误差容忍度 | 允许视觉细节与文字有偏差(如 “猫” 生成 “狗” 属错误) | 严格要求 “文字 - 记忆” 精确匹配(偏差即 “记忆错误”) |
# 视频帧的概念编码示例
{
"场景": "厨房",
"物体": [
{"类别": "咖啡杯", "颜色": "棕色", "位置": (x1,y1), "状态": "静止"},
{"类别": "微波炉", "颜色": "银色", "位置": (x2,y2), "状态": "运行中"}
],
"动作": "人正在倒咖啡",
"时间": "早晨8点"
}
| 层级 | 人类记忆类比 | 技术实现 |
|---|---|---|
| 概念层 | 语义符号系统(如 “咖啡杯” 概念) | 多模态大模型解析视觉 / 听觉输入为结构化概念,存储为图数据库(如 Neo4j)。 |
| 关联层 | 神经突触网络(概念关联) | 用图神经网络(GNN)学习概念间的关联权重,支持联想检索(如 “咖啡”→“厨房”)。 |
| 生成层 | 大脑皮层的视觉表象生成 | 确定性生成模型根据概念 + 锚点特征重建记忆画面,误差控制在人类可接受范围内。 |
sensor(用于传感器操作)、image(用于图像处理)、time(用于时间相关操作)等。sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
find_blobs函数查找图像中的色块;若使用神经网络识别,可通过tf.load函数加载模型,并使用net.detect函数进行目标检测。find_blobs、find_lines等)是独立实现的轻量级算法,针对低算力设备做了内存和效率优化。sensor.reset()等 Python 函数,本质是调用底层 C/C++ 实现的硬件操作函数。calibrate_cameras可一键完成相机标定,自动优化参数;find_shape_model对光照变化、旋转的鲁棒性强,阈值自动计算。import cv2
import cv2.aruco as aruco
# 加载ArUco字典,检测Marker
aruco_dict = aruco.Dictionary_get(aruco.DICT_6X6_250)
corners, ids, _ = aruco.detectMarkers(img, aruco_dict)
CLAHE(对比度受限直方图均衡化)自动增强光照不均的图像:clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
img_clahe = clahe.apply(gray_img)
imutils的adjust_gamma函数可动态调整亮度。SVM或KNN训练分类器,将特征(如颜色、形状)转化为模型,避免手动调阈值。例如:YOLO-Lite、MobileNet-SSD(基于 TensorFlow/PyTorch),只需标注数据即可训练模型,比传统算法更抗干扰。OpenCV 从 4.5 版本开始支持直接加载 TensorFlow 模型:net = cv2.dnn.readNetFromTensorFlow('model.pb', 'model.pbtxt')
sudo apt-get install libopencv-dev
#include <opencv2/aruco.hpp>
#include <opencv2/opencv.hpp>
int main() {
// 加载图像
cv::Mat image = cv::imread("marker_image.jpg");
// 定义ARUCO字典(如DICT_4X4_50)
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);
// 检测Marker
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners;
cv::aruco::detectMarkers(image, dictionary, markerCorners, markerIds);
// 绘制检测结果
cv::aruco::drawDetectedMarkers(image, markerCorners, markerIds);
cv::imshow("Markers", image);
cv::waitKey(0);
return 0;
}
g++ -o aruco_demo aruco_demo.cpp -std=c++11 -lopencv_core -lopencv_aruco -lopencv_highgui -lopencv_imgproc
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d.hpp>
int main() {
// 1. 初始化相机和参数
cv::VideoCapture cap(0); // 打开默认摄像头
if (!cap.isOpened()) {
std::cerr << "无法打开摄像头!" << std::endl;
return -1;
}
// 2. 设置围棋棋盘参数(19x19网格,实际角点为18x18)
int boardWidth = 18; // 横向角点数量
int boardHeight = 18; // 纵向角点数量
float squareSize = 30.0f; // 假设棋盘格子边长为30mm(实际需根据棋盘尺寸调整)
// 创建棋盘格角点的三维坐标(Z=0平面)
std::vector<cv::Point3f> objPoints;
for (int i = 0; i < boardHeight; i++) {
for (int j = 0; j < boardWidth; j++) {
objPoints.push_back(cv::Point3f(j * squareSize, i * squareSize, 0));
}
}
// 3. 相机标定(实际应用中应提前完成标定)
// 此处使用预设的标定参数,实际需通过标定板获取
cv::Mat cameraMatrix = cv::Mat::eye(3, 3, CV_64F);
cameraMatrix.at<double>(0, 0) = 1000; // 焦距x
cameraMatrix.at<double>(1, 1) = 1000; // 焦距y
cameraMatrix.at<double>(0, 2) = 320; // 光心x
cameraMatrix.at<double>(1, 2) = 240; // 光心y
cv::Mat distCoeffs = cv::Mat::zeros(5, 1, CV_64F); // 畸变系数
// 4. 实时检测棋盘
while (true) {
cv::Mat frame, grayFrame;
cap >> frame;
if (frame.empty()) {
std::cerr << "无法获取图像!" << std::endl;
break;
}
// 图像预处理
cv::cvtColor(frame, grayFrame, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(grayFrame, grayFrame, cv::Size(5, 5), 0);
// 5. 检测棋盘角点
std::vector<cv::Point2f> corners;
bool found = cv::findChessboardCorners(
grayFrame, cv::Size(boardWidth, boardHeight), corners,
cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE
);
// 6. 优化角点位置(亚像素精度)
if (found) {
cv::TermCriteria termCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.001);
cv::cornerSubPix(grayFrame, corners, cv::Size(11, 11), cv::Size(-1, -1), termCriteria);
// 7. 绘制角点
cv::drawChessboardCorners(frame, cv::Size(boardWidth, boardHeight), corners, found);
// 8. 计算棋盘姿态(旋转矩阵和平移向量)
std::vector<cv::Point3f> objPoints_curr;
for (size_t i = 0; i < corners.size(); i++) {
objPoints_curr.push_back(objPoints[i]);
}
cv::Mat rvec, tvec;
cv::solvePnP(objPoints_curr, corners, cameraMatrix, distCoeffs, rvec, tvec);
// 9. 绘制坐标轴(用于可视化姿态)
std::vector<cv::Point3f> axisPoints;
axisPoints.push_back(cv::Point3f(0, 0, 0)); // 原点
axisPoints.push_back(cv::Point3f(squareSize * 3, 0, 0)); // X轴(红色)
axisPoints.push_back(cv::Point3f(0, squareSize * 3, 0)); // Y轴(绿色)
axisPoints.push_back(cv::Point3f(0, 0, squareSize * 3)); // Z轴(蓝色)
std::vector<cv::Point2f> imgPoints;
cv::projectPoints(axisPoints, rvec, tvec, cameraMatrix, distCoeffs, imgPoints);
// 绘制坐标轴
cv::line(frame, imgPoints[0], imgPoints[1], cv::Scalar(0, 0, 255), 2); // X轴(红)
cv::line(frame, imgPoints[0], imgPoints[2], cv::Scalar(0, 255, 0), 2); // Y轴(绿)
cv::line(frame, imgPoints[0], imgPoints[3], cv::Scalar(255, 0, 0), 2); // Z轴(蓝)
// 显示检测到的角点数量
std::string text = "Corners: " + std::to_string(corners.size());
cv::putText(frame, text, cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);
} else {
cv::putText(frame, "未检测到棋盘", cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);
}
// 显示结果
cv::imshow("围棋棋盘检测", frame);
if (cv::waitKey(1) == 27) { // ESC键退出
break;
}
}
cap.release();
cv::destroyAllWindows();
return 0;
}
cameraMatrix和distCoeffs。findChessboardCorners函数检测棋盘角点,结合cornerSubPix优化角点到亚像素精度solvePnP计算棋盘在相机坐标系中的姿态(旋转向量rvec和平移向量tvec)DICT_CHESSBOARD)进行标定,参考 OpenCV 官方文档。cv::CLAHE预处理增强对比度:cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(2.0, cv::Size(8, 8));
clahe->apply(grayFrame, grayFrame);
findChessboardCorners的标志位,如增加CALIB_CB_FILTER_QUADS过滤非棋盘格区域。g++ -o go_board_detection go_board_detection.cpp \
-std=c++11 -lopencv_core -lopencv_imgproc -lopencv_highgui \
-lopencv_aruco -lopencv_calib3d
w mm × h mm,输入标定时作为已知参数。[fx 0 cx]
[0 fy cy]
[0 0 1 ]
fx、fy是焦距(像素单位),(cx, cy)是光心坐标。这里的 Z=0 并非指相机高度,而是坐标系的设定。import cv2
import numpy as np
import glob
import matplotlib.pyplot as plt
# 1. 定义标定板真实尺寸(单位:mm)
pattern_size = (9, 6) # 棋盘格行列数
square_size = 20.0 # 单个格子边长(mm)
# 2. 生成世界坐标点(Z=0,位于棋盘平面)
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
objp *= square_size # 转换为真实物理坐标
# 3. 存储世界坐标和图像坐标
objpoints = [] # 真实世界坐标
imgpoints = [] # 图像平面坐标
# 4. 读取图像并检测角点
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, pattern_size, None)
if ret:
objpoints.append(objp) # 同一标定板,世界坐标相同
# 亚像素优化角点位置,提高精度
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
imgpoints.append(corners2)
# 可视化角点检测结果(可选)
cv2.drawChessboardCorners(img, pattern_size, corners2, ret)
cv2.imshow('Corners Detected', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 5. 执行相机标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
# 输出标定结果
print("内参矩阵(Camera Matrix):\n", mtx)
print("畸变系数(Distortion Coefficients):\n", dist)
print("旋转向量(Rotation Vectors):\n", rvecs[0]) # 第一张图像的旋转参数
print("平移向量(Translation Vectors):\n", tvecs[0]) # 第一张图像的平移参数(包含相机高度信息)
# 6. 标定精度评估(均方误差RMSE)
mean_error = 0
for i in range(len(objpoints)):
imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
mean_error += error
print(f"平均重投影误差:{mean_error/len(objpoints):.6f} 像素")
# 7. 畸变校正(以某张图像为例)
img = cv2.imread(images[0])
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_image.jpg', dst)
# 8. 可视化对比(原图vs校正图)
plt.figure(figsize=(12, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('原始图像'), plt.axis('off')
plt.subplot(122), plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB))
plt.title('校正后图像'), plt.axis('off')
plt.tight_layout()
plt.show()
fx, fy:焦距(像素单位),反映相机对图像的缩放能力。cx, cy:光心坐标(像素),理论上为图像中心,实际可能因装配偏差偏移。[k1, k2, p1, p2, k3],分别对应径向畸变(k1, k2, k3)和切向畸变(p1, p2)。findChessboardCorners)依赖于 “完整棋盘边界” 的假设,而围棋棋盘无明确边界,算法可能无法区分 “局部格子” 和 “完整棋盘”。例如,若拍摄角度倾斜,棋盘在图像中呈梯形,算法可能误判角点数量或位置。import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1. 准备:拍摄多组不同角度的围棋棋盘图像
# 2. 手动测量:每个格子的实际边长(如30mm×30mm)
grid_size = 30 # 单位:mm
pattern_size = (9, 6) # 假设棋盘格子为9×6(根据实际情况调整)
# 3. 生成世界坐标(角点的物理坐标)
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) * grid_size
objpoints = [] # 世界坐标点
imgpoints = [] # 图像坐标点
# 4. 读取图像并手动标注角点(示例:假设已获取图像列表images)
for img in images:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 手动标注角点(实际需通过交互方式选择角点)
# 此处仅为示意,真实场景需用鼠标点击或其他方式获取角点坐标
corners = manual_select_corners(gray, pattern_size) # 自定义手动选点函数
if len(corners) == pattern_size[0] * pattern_size[1]:
objpoints.append(objp)
imgpoints.append(corners)
# 5. 执行相机标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
print("相机内参矩阵:\n", mtx)
print("畸变系数:", dist)
cv2.selectROI或自定义交互工具),确保角点顺序与世界坐标一一对应。findChessboardCorners和cornerSubPix可自动检测角点并优化位置,大幅减少人工工作量。import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
# 1. 配置参数:根据实际测量结果修改
grid_size = 30.0 # 单个格子的实际边长(mm)
pattern_size = (10, 8) # 选取的棋盘格子行列数(如10列×8行)
images_path = "chessboard_images/" # 图像存储路径
output_dir = "calibration_results/"
os.makedirs(output_dir, exist_ok=True)
# 2. 生成世界坐标系下的角点坐标(Z=0平面)
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) * grid_size
objpoints = [] # 世界坐标点集合
imgpoints = [] # 图像坐标点集合
image_files = [f for f in os.listdir(images_path) if f.endswith(('.jpg', '.png'))]
# 3. 手动标注角点函数(通过鼠标点击选择角点)
def select_corners(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
corners.append((x, y))
cv2.circle(img, (x, y), 5, (0, 255, 0), -1)
cv2.imshow('image', img)
print(f"点击了角点 {len(corners)}: ({x}, {y})")
# 4. 遍历图像并手动标注角点
for i, img_file in enumerate(image_files):
img_path = os.path.join(images_path, img_file)
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
h, w = gray.shape[:2]
print(f"\n处理图像 {i+1}/{len(image_files)}: {img_file}")
print(f"请按顺序点击{pattern_size[0]*pattern_size[1]}个角点(从左到右,从上到下)")
print("点击完成后按ESC键保存,按'q'键跳过此图像")
corners = []
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image', img)
cv2.setMouseCallback('image', select_corners)
while True:
key = cv2.waitKey(1) & 0xFF
if key == 27: # ESC键,保存角点
if len(corners) == pattern_size[0] * pattern_size[1]:
imgpoints.append(np.array(corners, dtype=np.float32))
objpoints.append(objp)
print(f"成功保存{len(corners)}个角点")
break
else:
print(f"角点数量不足,需要{pattern_size[0]*pattern_size[1]}个,当前{len(corners)}个")
elif key == ord('q'): # 'q'键,跳过
print("跳过此图像")
break
cv2.destroyAllWindows()
# 5. 执行相机标定
if objpoints and imgpoints:
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, (w, h), None, None)
# 保存标定结果
np.savez(os.path.join(output_dir, "calibration_results.npz"),
mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
# 打印结果
print("\n标定结果:")
print(f"重投影误差:{ret}")
print("相机内参矩阵:\n", mtx)
print("畸变系数:", dist)
# 示例:计算相机到棋盘的距离(可选)
# 选取某一帧的旋转向量和平移向量
rvec = rvecs[0]
tvec = tvecs[0]
# 将旋转向量转换为旋转矩阵
R, _ = cv2.Rodrigues(rvec)
# 相机到棋盘的距离(Z轴方向,单位:mm)
camera_distance = np.linalg.norm(tvec)
print(f"相机到棋盘的近似距离:{camera_distance:.2f} mm")
else:
print("角点数据不足,无法完成标定")
objp的顺序一致(这是标定精度的核心)。objp定义了棋盘角点在世界坐标系中的位置,假设棋盘位于 Z=0 平面,所有点的 Z 坐标为 0,X/Y 坐标由格子尺寸和行列数确定。tvec的模长,可估算相机到棋盘平面的大致距离(单位与grid_size一致),这对后续三维重建或位姿估计有参考价值。cv2.cornerSubPix函数优化角点位置,减少像素级误差:criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
refined_corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
ret),理想情况下应小于 1,若误差过大(如 > 5),需重新拍摄图像或检查角点标注是否正确。cv2.findChessboardCorners函数自动检测角点,若检测失败(因棋盘无边界),再手动调整;或使用开源工具(如kalibr_calibrate_camera)辅助交互标注。findChessboardCorners自动检测角点,大幅简化流程:# 标准标定板自动检测示例(替代手动标注)
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
d 毫米,标准围棋盘格子边长约 25-30mm),并记录棋盘的角点物理坐标(例如,以棋盘左上角为原点,建立三维坐标系,假设棋盘位于 z=0 平面,每个角点坐标为 (i*d, j*d, 0),i,j 为行列索引)。findChessboardCorners函数自动提取棋盘角点,即使图像呈梯形,该函数也能通过边缘检测和角点响应算法识别交点。示例代码框架:import cv2
import numpy as np
# 读取图像
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 定义棋盘格行列数(如围棋盘为19x19,但通常使用内角点,如18x18)
rows, cols = 18, 18 # 根据实际棋盘格数调整
# 提取角点(即使图像扭曲,函数也能处理)
ret, corners = cv2.findChessboardCorners(gray, (rows, cols), None)
if ret:
# 角点亚像素优化,提高精度
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
# 绘制角点
cv2.drawChessboardCorners(img, (rows, cols), corners2, ret)
cv2.imshow('Corners', img)
cv2.waitKey(0)
z=0 平面,以左上角为原点,每个内角点的三维坐标为 (i*d, j*d, 0),其中 i 从 0 到cols-1,j 从 0 到rows-1,d 为格子边长(单位:毫米)。(u, v) 即为像素平面坐标。calibrateCamera函数,输入所有图像的角点像素坐标和对应的物理坐标,即可计算相机参数:# 构建世界坐标点(所有图像共用同一组物理坐标)
objp = np.zeros((rows*cols, 3), np.float32)
objp[:, :2] = np.mgrid[0:cols, 0:rows].T.reshape(-1, 2) * d # d为格子边长
# 存储所有图像的世界坐标和像素坐标
objpoints = [] # 世界坐标
imgpoints = [] # 像素坐标
# 遍历所有图像,提取角点并添加到列表
for img_path in image_paths:
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (cols, rows), None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
# 执行标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
# 输出结果
print("相机内参矩阵:\n", mtx)
print("畸变系数:\n", dist)
mtx:内参矩阵,包含焦距 (fx, fy) 和主点 (cx, cy),与相机自身特性相关,不受拍摄角度影响。dist:畸变系数,用于校正图像的径向和切向畸变。rvecs 和 tvecs:外参矩阵,分别表示相机相对于棋盘的旋转向量和平移向量,反映每次拍摄的视角姿态。# 图像校正
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.imshow('Undistorted', dst)
findChessboardCorners的参数(如flags),或先对图像进行透视变换预处理,将梯形 “拉平” 为矩形后再提取角点(但需提前估计变换矩阵)。cv2的说明cv2与 OpenCV 的关系cv2是 Python 版 OpenCV 的库名:OpenCV 最初用 C/C++ 开发,Python 接口通过封装 C++ 库实现,为了区分历史版本(如早期的cv模块),Python 库命名为cv2,本质是对 C++ 核心功能的封装。cv::calibrateCamera,Python 中为cv2.calibrateCamera,参数定义和功能完全对应。findChessboardCorners用于检测棋盘角点,calibrateCamera用于标定)。ndarray)存储图像,C++ 中使用cv::Mat。Mat.release())。cv2.imread),C++ 接口带命名空间(如cv::imread),功能完全一致。opencv-contrib-python)轻松安装扩展模块,C++ 需手动编译扩展库(如opencv_contrib)。#include <opencv2/opencv.hpp>
using namespace cv;
int main() {
// 读取图像、检测角点等操作
vector<Point2f> corners;
bool found = findChessboardCorners(image, boardSize, corners);
// 标定相机
calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs);
return 0;
}
import cv2
import numpy as np
# 读取图像、检测角点
corners = cv2.findChessboardCorners(image, boardSize)
# 标定相机
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, img_size, None, None)
findChessboardCorners、calibrateCamera)的参数逻辑和输出结果完全对应,仅语法格式不同。cv2是 Python 版 OpenCV 的库名,核心功能与 C++ 版一致,仅接口形式适应不同语言特性。cv2.前缀,添加cv::命名空间),参数定义完全兼容。findChessboardCorners函数的存在性与使用说明findChessboardCorners是 OpenCV 中用于检测棋盘格角点的核心函数,在 OpenCV 2.x、3.x、4.x 版本的 C++ 和 Python 接口中均存在。bool cv::findChessboardCorners(
InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE
);
corners, found = cv2.findChessboardCorners(
image, patternSize, flags=cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
)
cv命名空间:#include <opencv2/opencv.hpp> // 或具体头文件如<opencv2/calib3d/calib3d.hpp>
using namespace cv;
Mat image = imread("chessboard.jpg");
vector<Point2f> corners;
bool found = findChessboardCorners(image, Size(7, 7), corners);
libopencv_calib3d.so或opencv_calib3d.lib)。calib3d模块(该函数属于calib3d模块)。image:输入图像(灰度图或彩色图,彩色图会自动转灰度)。patternSize:棋盘格内角点行列数(如Size(7, 7)表示 7×7 的角点网格)。corners:输出角点坐标(按行列顺序排列)。flags:可选参数,常用标志:CALIB_CB_ADAPTIVE_THRESH:自适应阈值处理,适应光照不均。CALIB_CB_NORMALIZE_IMAGE:归一化图像,提高检测精度。findChessboardCorners的参数和返回值逻辑未发生重大变更,高版本代码可直接参考 2.x 文档。(corners, found),而 C++ 中通过引用返回corners并返回布尔值found,本质逻辑相同。#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
// 读取图像
cv::Mat image = cv::imread("chessboard.jpg");
if (image.empty()) {
std::cerr << "无法读取图像!" << std::endl;
return -1;
}
// 定义棋盘格角点数量(如8×6的格子对应7×5内角点)
cv::Size boardSize(7, 7);
std::vector<cv::Point2f> corners;
// 检测角点
bool found = cv::findChessboardCorners(
image,
boardSize,
corners,
cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE
);
if (found) {
std::cout << "成功检测到" << corners.size() << "个角点!" << std::endl;
// 绘制角点(可选)
cv::drawChessboardCorners(image, boardSize, corners, found);
cv::imshow("Chessboard Corners", image);
cv::waitKey(0);
} else {
std::cout << "未检测到角点,请检查图像或棋盘格参数!" << std::endl;
}
return 0;
}
findChessboardCorners在 OpenCV 2.x 的 C++ 库中存在,属于calib3d模块,需正确包含头文件和命名空间。nm命令(Linux)/dumpbin命令(Windows)确认库中是否包含该函数符号。flags参数以提高检测成功率。calibrateCamera函数的使用指南calibrateCamera是 OpenCV 中用于相机标定的核心函数,通过检测棋盘格等标定板的角点,计算相机的内参矩阵和畸变系数,解决相机成像的透视变形和径向 / 切向畸变问题。double cv::calibrateCamera(
const vector<vector<Point3f>>& objectPoints, // 三维世界坐标点
const vector<vector<Point2f>>& imagePoints, // 二维图像坐标点
Size imageSize, // 图像尺寸
Mat& cameraMatrix, // 输出内参矩阵
Mat& distCoeffs, // 输出畸变系数
vector<Mat>& rvecs, // 输出旋转向量
vector<Mat>& tvecs, // 输出平移向量
int flags = 0, // 标定选项
TermCriteria criteria = TermCriteria( // 迭代终止条件
TermCriteria::EPS + TermCriteria::MAX_ITER, 30, DBL_EPSILON
)
);
objectPoints:三维世界坐标点集合,通常设为棋盘格角点在世界坐标系中的坐标(如以棋盘格平面为 XY 平面,Z=0)。imagePoints:二维图像中检测到的角点坐标,需与objectPoints一一对应。imageSize:输入图像的尺寸(用于确定内参矩阵的像素单位)。cameraMatrix:内参矩阵(3×3),包含焦距(fx, fy)和光心(cx, cy)。distCoeffs:畸变系数(通常为 4~5 个参数,如 [k1, k2, p1, p2, k3])。rvecs/tvecs:各标定图像的旋转和平移向量(用于计算外参)。// 假设已通过findChessboardCorners获取多组角点数据
vector<vector<Point3f>> objectPoints; // 世界坐标点(多组图像共用)
vector<vector<Point2f>> imagePoints; // 各图像的角点坐标
// 创建世界坐标系中的棋盘格角点(Z=0,单位:毫米)
Size boardSize(7, 7); // 内角点行列数
vector<Point3f> objPts;
for (int i = 0; i < boardSize.height; i++) {
for (int j = 0; j < boardSize.width; j++) {
objPts.push_back(Point3f(j * 20, i * 20, 0)); // 假设格子边长20mm
}
}
// 对每组图像(假设已存储为images数组):
for (const auto& img : images) {
vector<Point2f> corners;
bool found = findChessboardCorners(img, boardSize, corners);
if (found) {
objectPoints.push_back(objPts);
imagePoints.push_back(corners);
}
}
Mat cameraMatrix = Mat::eye(3, 3, CV_64F); // 初始化内参矩阵
Mat distCoeffs = Mat::zeros(5, 1, CV_64F); // 初始化畸变系数(k1,k2,p1,p2,k3)
vector<Mat> rvecs, tvecs;
// 标定相机(返回重投影误差,越小表示标定越准确)
double reprojError = calibrateCamera(
objectPoints, imagePoints,
images[0].size(), // 图像尺寸
cameraMatrix, distCoeffs,
rvecs, tvecs
);
cout << "重投影误差: " << reprojError << endl;
cout << "内参矩阵: " << endl << cameraMatrix << endl;
cout << "畸变系数: " << endl << distCoeffs << endl;
// 示例:对单张图像去畸变
Mat undistortedImg;
undistort(images[0], undistortedImg, cameraMatrix, distCoeffs);
imshow("Undistorted Image", undistortedImg);
CALIB_USE_INTRINSIC_GUESS:若已知初始内参,可传入以加速收敛。CALIB_FIX_PRINCIPAL_POINT:固定光心在图像中心(适用于对称镜头)。CALIB_ZERO_TANGENT_DIST:忽略切向畸变(p1,p2 设为 0)。drawChessboardCorners可视化检查)。import cv2
import numpy as np
# 假设已获取objectPoints和imagePoints(格式与C++类似)
image_size = (width, height) # 图像宽高
# 标定相机
camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
objectPoints, imagePoints, image_size, None, None
)
# 输出结果
print("重投影误差:", reproj_error)
print("内参矩阵:\n", camera_matrix)
print("畸变系数:\n", dist_coeffs)
calibrateCamera的核心是通过多组棋盘格角点的三维 - 二维对应关系,求解相机的内参与畸变参数。使用时需注意:objectPoints和imagePoints的对应关系正确;flags参数,优化标定精度;include路径有以下两种常见方法:c_cpp_properties.json文件设置c_cpp_properties.json文件:点击VS Code右下角的环境标识(如Win32),或者使用快捷键Ctrl + Shift + P,输入“编辑配置”,选择“C/C++: 编辑配置(UI)”,这会在项目的.vscode文件夹中生成c_cpp_properties.json文件。includePath属性:在c_cpp_properties.json文件中,找到includePath属性。它通常已经包含了"${workspaceFolder}/**",表示搜索工作区文件夹及其子文件夹。如果要添加其他路径,直接在数组中添加新的路径字符串即可,例如"C:/mylibs/include"。Ctrl +,(或Cmd +,在Mac上),打开设置界面,然后搜索“include path”。include路径。findChessboardCorners检测围棋棋盘的可行性分析findChessboardCorners的核心原理是检测规则排列的黑白相间格子的内角点。围棋棋盘虽然没有明确的边界标记,但具备规则的网格结构,理论上可以被该函数识别。CALIB_CB_FAST_CHECK标志快速筛选可能包含棋盘的图像。CLAHE(对比度受限的自适应直方图均衡化)增强对比度。CALIB_CB_ADAPTIVE_THRESH和CALIB_CB_NORMALIZE_IMAGE标志,提高抗干扰能力。#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 读取图像
cv::Mat image = cv::imread("go_board.jpg");
if (image.empty()) {
std::cerr << "无法读取图像!" << std::endl;
return -1;
}
// 图像预处理
cv::Mat gray, blurred, thresh;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
cv::adaptiveThreshold(blurred, thresh, 255,
cv::ADAPTIVE_THRESH_GAUSSIAN_C,
cv::THRESH_BINARY_INV, 11, 2);
// 定义棋盘格尺寸(围棋棋盘通常为19×19,但可根据实际检测区域调整)
cv::Size patternSize(10, 10); // 检测10×10的角点网格
std::vector<cv::Point2f> corners;
// 检测角点
bool found = cv::findChessboardCorners(
thresh, // 二值化后的图像更易检测
patternSize,
corners,
cv::CALIB_CB_ADAPTIVE_THRESH |
cv::CALIB_CB_NORMALIZE_IMAGE |
cv::CALIB_CB_FAST_CHECK
);
if (found) {
// 亚像素级角点优化
cv::cornerSubPix(
gray,
corners,
cv::Size(11, 11), // 搜索窗口大小
cv::Size(-1, -1), // 不使用的区域(负值表示忽略)
cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.1)
);
// 绘制角点
cv::drawChessboardCorners(image, patternSize, corners, found);
cv::imshow("Detected Corners", image);
cv::waitKey(0);
} else {
std::cout << "未检测到棋盘角点!" << std::endl;
// 可视化预处理结果,辅助调试
cv::imshow("Preprocessed Image", thresh);
cv::waitKey(0);
}
return 0;
}
adaptiveThreshold的第 5 个参数)需根据图像尺寸调整。GaussianBlur的ksize)影响细节保留程度。findChessboardCorners持续失败,可考虑:findChessboardCorners可用于检测围棋棋盘,但需通过预处理和参数调优克服无边界、光照不均和变形等问题。对于复杂场景,可能需要结合多种算法或手动辅助(如指定 ROI)提高成功率。#include <opencv2/opencv.hpp>
#include <iostream>
// 围棋盘参数(19×19网格,但检测时可缩小范围提高效率)
const cv::Size GO_BOARD_PATTERN(10, 10); // 检测10×10的角点区域
// 图像预处理函数
cv::Mat preprocessImage(const cv::Mat& input) {
cv::Mat gray, blurred, thresh;
// 灰度化
cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);
// 高斯模糊去噪
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
// 自适应阈值二值化(应对光照不均)
cv::adaptiveThreshold(
blurred, thresh, 255,
cv::ADAPTIVE_THRESH_GAUSSIAN_C,
cv::THRESH_BINARY_INV, 11, 2
);
return thresh;
}
// 检测围棋盘角点并绘制
bool detectGoBoard(cv::Mat& frame, std::vector<cv::Point2f>& corners) {
cv::Mat processed = preprocessImage(frame);
// 检测角点(使用增强型参数)
bool found = cv::findChessboardCorners(
processed,
GO_BOARD_PATTERN,
corners,
cv::CALIB_CB_ADAPTIVE_THRESH |
cv::CALIB_CB_NORMALIZE_IMAGE |
cv::CALIB_CB_FAST_CHECK
);
// 若检测到角点,进行亚像素优化并绘制
if (found) {
cv::cornerSubPix(
processed,
corners,
cv::Size(11, 11),
cv::Size(-1, -1),
cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.1)
);
cv::drawChessboardCorners(frame, GO_BOARD_PATTERN, corners, found);
}
return found;
}
int main() {
// 初始化摄像头(0表示默认摄像头,可根据设备ID调整)
cv::VideoCapture cap(0);
if (!cap.isOpened()) {
std::cerr << "无法打开摄像头!" << std::endl;
return -1;
}
std::vector<cv::Point2f> boardCorners;
cv::Mat frame;
std::cout << "开始检测围棋盘...(按'q'退出)" << std::endl;
while (true) {
// 读取帧
cap >> frame;
if (frame.empty()) {
std::cerr << "无法读取图像帧!" << std::endl;
break;
}
// 检测围棋盘
bool found = detectGoBoard(frame, boardCorners);
// 显示结果
std::string status = found ? "已检测到围棋盘" : "未检测到";
cv::putText(frame, status, cv::Point(10, 30),
cv::FONT_HERSHEY_SIMPLEX, 1, found ? cv::Scalar(0, 255, 0) : cv::Scalar(0, 0, 255), 2);
cv::imshow("Go Board Detector", frame);
// 按'q'退出
if (cv::waitKey(1) == 'q') {
break;
}
}
cap.release();
cv::destroyAllWindows();
return 0;
}
findChessboardCorners函数检测规则网格角点,参数CALIB_CB_ADAPTIVE_THRESH等增强了对光照和变形的适应性。adaptiveThreshold)而非固定阈值,避免因光照变化导致检测失败。cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280); // 设置宽度
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720); // 设置高度
cap.set(cv::CAP_PROP_EXPOSURE, -8); // 调整曝光(负数为降低曝光,避免反光)
cv::Rect roi(x, y, width, height); // 指定ROI区域
cv::Mat roiFrame = frame(roi);
detectGoBoard(roiFrame, boardCorners);
// 假设已获取4个角点坐标,计算透视变换矩阵
std::vector<cv::Point2f> srcPoints = { /* 变形角点坐标 */ };
std::vector<cv::Point2f> dstPoints = {
cv::Point2f(0, 0), cv::Point2f(600, 0),
cv::Point2f(600, 600), cv::Point2f(0, 600)
};
cv::Mat perspectiveMat = cv::getPerspectiveTransform(srcPoints, dstPoints);
cv::warpPerspective(frame, correctedFrame, perspectiveMat, cv::Size(600, 600));
cv::aruco模块检测:#include <opencv2/aruco.hpp>
// 初始化ArUco字典和检测器
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr<cv::aruco::DetectorParameters> parameters = cv::aruco::DetectorParameters::create();
g++ -std=c++11 go_board_detector.cpp -o go_detector `pkg-config --cflags --libs opencv4`
c_cpp_properties.json中配置 OpenCV 头文件路径,并在tasks.json中设置编译命令:"args": [
"-std=c++11",
"
findChessboardCorners通过色块模式匹配检测角点。HoughLinesP)检测水平和垂直直线。#include <opencv2/opencv.hpp>
#include <vector>
std::vector<cv::Point2f> detectGoBoardCorners(cv::Mat& frame) {
// 1. 图像预处理
cv::Mat gray, blurred, edges;
cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
cv::Canny(blurred, edges, 50, 150);
// 2. 霍夫直线检测
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 100, 100, 20);
// 3. 分离水平和垂直直线(根据斜率筛选)
std::vector<cv::Vec4i> horizontalLines, verticalLines;
for (auto& line : lines) {
int dx = line[2] - line[0];
int dy = line[3] - line[1];
if (std::abs(dy) < std::abs(dx) * 0.1) { // 水平直线(斜率接近0)
horizontalLines.push_back(line);
} else if (std::abs(dx) < std::abs(dy) * 0.1) { // 垂直直线(斜率接近无穷)
verticalLines.push_back(line);
}
}
// 4. 按距离排序并筛选等间距直线(围棋盘网格等距)
std::sort(horizontalLines.begin(), horizontalLines.end(),
[](const cv::Vec4i& a, const cv::Vec4i& b) { return a[1] < b[1]; });
std::sort(verticalLines.begin(), verticalLines.end(),
[](const cv::Vec4i& a, const cv::Vec4i& b) { return a[0] < b[0]; });
// 筛选等间距的水平/垂直线(假设围棋盘19×19,间距相近)
std::vector<cv::Vec4i> validHorizontals, validVerticals;
float avgDistH = 0, avgDistV = 0;
if (horizontalLines.size() >= 19) {
// 计算平均间距并筛选(简化逻辑,实际需更严谨的模式匹配)
for (int i = 1; i < 19; i++) {
avgDistH += horizontalLines[i][1] - horizontalLines[i-1][1];
}
avgDistH /= 18;
// 保留间距接近平均值的直线(误差范围可调整)
for (int i = 0; i < horizontalLines.size(); i++) {
if (i < 19 || std::abs(horizontalLines[i][1] - horizontalLines[i-1][1] - avgDistH) < avgDistH * 0.2) {
validHorizontals.push_back(horizontalLines[i]);
}
}
}
// 同理处理垂直线...
// 5. 计算直线交点,生成角点
std::vector<cv::Point2f> corners;
for (auto& hLine : validHorizontals) {
for (auto& vLine : validVerticals) {
// 计算两直线交点(需处理直线方程求解)
float x, y;
// 简化示例:假设直线足够长,交点在图像内
// 实际需用直线参数方程计算交点
x = vLine[0];
y = hLine[1];
corners.emplace_back(x, y);
}
}
return corners;
}
int main() {
cv::VideoCapture cap(0); // 打开摄像头
if (!cap.isOpened()) return -1;
cv::Mat frame;
while (cap.read(frame)) {
std::vector<cv::Point2f> corners = detectGoBoardCorners(frame);
// 绘制检测到的角点
for (auto& pt : corners) {
cv::circle(frame, pt, 3, cv::Scalar(0, 255, 0), -1);
}
cv::imshow("Go Board Detection", frame);
if (cv::waitKey(1) == 27) break; // ESC键退出
}
cap.release();
cv::destroyAllWindows();
return 0;
}
getPerspectiveTransform将棋盘校正为正视图,便于后续落子识别。leela-zero或katago的视觉模块,可能包含棋盘检测代码。// 假设已获取初步角点(前序代码中的corners)
std::vector<cv::Point2f> filterBoardCorners(std::vector<cv::Point2f>& corners) {
if (corners.size() < 10) return {}; // 角点太少,无法处理
// 1. 计算角点横纵坐标的极值(初步定位四角)
float minX = FLT_MAX, maxX = FLT_MIN, minY = FLT_MAX, maxY = FLT_MIN;
for (auto& pt : corners) {
minX = std::min(minX, pt.x);
maxX = std::max(maxX, pt.x);
minY = std::min(minY, pt.y);
maxY = std::max(maxY, pt.y);
}
// 2. 定义四角区域(梯形四边的近似范围)
std::vector<cv::Point2f> roughCorners = {
{minX, minY}, {maxX, minY}, {maxX, maxY}, {minX, maxY}
};
// 3. 对每个初步角点,计算到四边的距离,筛选最接近四边交点的点
std::vector<cv::Point2f> finalCorners(4);
for (int i = 0; i < 4; i++) {
int next = (i + 1) % 4;
// 边i->next的直线方程
float a = roughCorners[next].y - roughCorners[i].y;
float b = roughCorners[i].x - roughCorners[next].x;
float c = roughCorners[next].x * roughCorners[i].y - roughCorners[i].x * roughCorners[next].y;
// 寻找距离边i->next最近的角点,并靠近两边交点
float minDist = FLT_MAX;
for (auto& pt : corners) {
// 点到直线距离公式:|a*x + b*y + c| / sqrt(a²+b²)
float dist = std::abs(a * pt.x + b * pt.y + c) / std::sqrt(a*a + b*b);
if (dist < minDist) {
minDist = dist;
finalCorners[i] = pt;
}
}
}
// 4. 按顺时针/逆时针顺序排序四角(便于后续透视变换)
// 计算中心点,按极角排序
cv::Point2f center((minX+maxX)/2, (minY+maxY)/2);
std::sort(finalCorners.begin(), finalCorners.end(), [¢er](const cv::Point2f& a, const cv::Point2f& b) {
float angleA = std::atan2(a.y - center.y, a.x - center.x);
float angleB = std::atan2(b.y - center.y, b.x - center.x);
return angleA < angleB;
});
return finalCorners;
}
cv::Mat adjustIllumination(cv::Mat& frame) {
// 1. 直方图均衡化处理灰度图
cv::Mat gray, equalized;
cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
cv::equalizeHist(gray, equalized);
// 2. 局部自适应阈值(处理明暗不均)
cv::Mat adaptiveThresh;
cv::adaptiveThreshold(equalized, adaptiveThresh, 255,
cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, 11, 2);
// 3. 形态学操作增强网格线(闭运算连接断点)
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::morphologyEx(adaptiveThresh, adaptiveThresh, cv::MORPH_CLOSE, kernel);
return adaptiveThresh;
}
cv::Mat rectifyBoard(cv::Mat& frame, std::vector<cv::Point2f>& corners) {
if (corners.size() != 4) return frame;
// 1. 定义目标矩形(假设校正后为600x600的正方形)
int targetSize = 600;
std::vector<cv::Point2f> targetCorners = {
{0, 0}, {targetSize, 0}, {targetSize, targetSize}, {0, targetSize}
};
// 2. 计算透视变换矩阵
cv::Mat M = cv::getPerspectiveTransform(corners, targetCorners);
// 3. 执行透视变换
cv::Mat rectified;
cv::warpPerspective(frame, rectified, M, cv::Size(targetSize, targetSize));
return rectified;
}
// 全局变量(示例)
std::vector<cv::Point2f> prevCorners(4);
float fusionWeight = 0.7; // 历史帧权重
// 融合当前帧与历史帧结果
for (int i = 0; i < 4; i++) {
finalCorners[i].x = fusionWeight * prevCorners[i].x + (1-fusionWeight) * finalCorners[i].x;
finalCorners[i].y = fusionWeight * prevCorners[i].y + (1-fusionWeight) * finalCorners[i].y;
}
prevCorners = finalCorners;
HoughLinesP的minLineLength和maxLineGap)。dnn 模块加载主流深度学习框架(如 TensorFlow、PyTorch、Darknet 等)训练的模型,包括轻量级目标检测模型(如 YOLOv5s、YOLOv6n、MobileNet-SSD 等)。你可以先用这些框架训练一个检测围棋盘四角的模型,再通过 OpenCV 加载部署。#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <vector>
int main() {
// 1. 加载模型(假设已转换为 ONNX 或 Darknet 格式)
cv::dnn::Net net = cv::dnn::readNetFromONNX("yolov5n_chessboard.onnx");
// 或 readNetFromDarknet("cfg/yolov5n.cfg", "weights/yolov5n.weights");
// 2. 配置输入尺寸和后端(CPU/GPU)
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); // CPU 推理
// net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); // GPU 推理(需 CUDA 支持)
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
// 3. 读取视频流
cv::VideoCapture cap(0); // 打开摄像头
if (!cap.isOpened()) {
std::cerr << "无法打开摄像头!" << std::endl;
return -1;
}
cv::Mat frame, blob;
while (cap.read(frame)) {
// 4. 预处理:调整尺寸并转换为 blob
cv::dnn::blobFromImage(frame, blob, 1/255.0, cv::Size(320, 320),
cv::Scalar(0, 0, 0), true, false);
net.setInput(blob);
// 5. 前向推理
std::vector<cv::Mat> outputs;
net.forward(outputs, net.getUnconnectedOutLayersNames());
// 6. 后处理:解析检测结果,提取棋盘边界或角点
// (此处需根据模型输出格式解析,例如 YOLO 的边界框坐标)
std::vector<int> classIds;
std::vector<float> confidences;
std::vector<cv::Rect> boxes;
// ... 解析 outputs 并筛选有效检测(如置信度>0.5 的棋盘区域)
// 7. 若检测到棋盘,计算四角坐标(可通过边界框顶点或透视变换)
if (!boxes.empty()) {
cv::Rect box = boxes[0]; // 假设取第一个检测到的棋盘
// 棋盘边界框的四个角点(可能需根据实际模型输出调整)
cv::Point2f corners[4] = {
cv::Point2f(box.x, box.y),
cv::Point2f(box.x + box.width, box.y),
cv::Point2f(box.x + box.width, box.y + box.height),
cv::Point2f(box.x, box.y + box.height)
};
// 8. 透视变换:将斜视角图像校正为正视图
cv::Size targetSize(600, 600); // 目标尺寸(假设围棋盘为正方形)
cv::Mat M = cv::getPerspectiveTransform(corners,
cv::Point2f[4] {
cv::Point2f(0, 0),
cv::Point2f(targetSize.width, 0),
cv::Point2f(targetSize.width, targetSize.height),
cv::Point2f(0, targetSize.height)
});
cv::Mat corrected;
cv::warpPerspective(frame, corrected, M, targetSize);
// 显示校正后的图像
cv::imshow("Corrected Chessboard", corrected);
}
// 显示原图和检测结果
cv::imshow("Frame", frame);
if (cv::waitKey(1) == 27) break; // ESC 键退出
}
cap.release();
cv::destroyAllWindows();
return 0;
}
findChessboardCorners 尝试检测小范围棋盘格(即使变形),再利用棋盘的几何特性(如正方形网格、行列对齐)推断四角位置。dnn 模块和轻量级深度学习模型,可有效提升斜视视角下围棋盘四角检测的鲁棒性。若对实时性要求极高,还可进一步优化模型结构(如剪枝、量化)或结合边缘计算设备(如 NVIDIA Jetson)加速推理。approxPolyDP function is used to approximate a polygonal curve with a specified precision. The function relies on the Ramer - Douglas - Peucker algorithm, which simplifies a curve to a reduced set of points. The behavior of this function is significantly influenced by its parameters. Let's take a detailed look at these parameters and their effects:epsilonepsilon value leads to a more precise approximation, which means the approximated curve will be closer to the original curve. Conversely, a larger epsilon results in a more simplified polygon with fewer vertices.epsilon = 0.01 * arcLength(curve, true) (where arcLength calculates the perimeter of the curve), the approximation will closely follow the original contour.epsilon = 0.1 * arcLength(curve, true), the approximation will be a highly simplified polygon.closedtrue, the function assumes that the input curve is closed, meaning the first and last points are connected.false, the curve is treated as open.approxCurve#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// Create a sample contour (a rectangle with a small perturbation)
std::vector<cv::Point> contour = {
cv::Point(10, 10), cv::Point(100, 15),
cv::Point(105, 100), cv::Point(15, 95),
cv::Point(10, 10) // Closing point
};
// Vary epsilon values
std::vector<double> epsilons = {0.001, 0.01, 0.05, 0.1};
for (double eps : epsilons) {
std::vector<cv::Point> approx;
double epsilon = eps * cv::arcLength(contour, true);
cv::approxPolyDP(contour, approx, epsilon, true);
std::cout << "Epsilon = " << eps << ", Vertices: " << approx.size() << std::endl;
// Draw the original and approximated contours
cv::Mat image = cv::Mat::zeros(200, 200, CV_8UC3);
cv::polylines(image, contour, true, cv::Scalar(0, 255, 0), 2); // Green: original
cv::polylines(image, approx, true, cv::Scalar(0, 0, 255), 2); // Red: approximated
cv::imshow("Approximation", image);
cv::waitKey(0);
}
return 0;
}
epsilon, such as 0.05 * arcLength, to obtain a rough approximation with fewer points.epsilon, like 0.01 * arcLength, to preserve the details of the shape.epsilon: A common approach is to express epsilon as a percentage of the contour's perimeter. Values typically range from 0.001 to 0.1.epsilon values generally lead to longer computation times because the algorithm needs to retain more points.epsilon parameter, you can strike a balance between the accuracy and simplicity of the polygonal approximation according to your specific application requirements.