OPENCV+PYTHON 棋盘格及其使用 探索学习

提问 文心一言:

OPENCV+PYTHON 棋盘格的函数

答案:

在 OpenCV 和 Python 中,棋盘格(Chessboard)常用于相机标定(Camera Calibration)中,因为它提供了一种已知图案,用于计算相机的内部参数和外部参数。棋盘格图案通常由交替的黑白方块组成。

以下是一些常用的与棋盘格相关的函数和步骤:

    1. 生成棋盘格图像:
      虽然通常使用预打印的棋盘格图案,但你也可以使用 OpenCV 生成一个棋盘格图像。
复制代码
import cv2
import numpy as np

def generate_chessboard(squares_per_row, squares_per_col, square_size):
    # 棋盘格的大小
    rows = squares_per_row + 1
    cols = squares_per_col + 1
    
    # 创建一个空白图像
    img = np.ones((rows * square_size, cols * square_size, 3), dtype=np.uint8) * 255
    
    for i in range(squares_per_row + 1):
        for j in range(squares_per_col + 1):
            if (i + j) % 2 == 0:
                # 填充黑色方块
                cv2.rectangle(img, 
                              (j * square_size, i * square_size), 
                              ((j + 1) * square_size, (i + 1) * square_size), 
                              0, -1)
    
    return img

# 生成一个8x11 棋盘格(棋盘格的大小:内部角点的数量),每个方块大小为 50 像素
# 即生成一个有9x12 方格 的棋盘格(棋盘格的大小:方块的数量),每个方块大小为 50 像素
chessboard_img = generate_chessboard(8, 11, 50)
cv2.waitKey(0) cv2.destroyAllWindows()
复制代码
    1. 找到棋盘格角点:
      使用 cv2.findChessboardCorners 函数来检测棋盘格的角点。
复制代码
import cv2

# 棋盘格的大小 (内部角点的数量)
chessboard_size = (8, 11)

# 读取棋盘格图像
img = cv2.imread('chessboard.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 找到棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

# 如果找到角点,则绘制并显示它们
if ret:
    img = cv2.drawChessboardCorners(img, chessboard_size, corners, ret)
    cv2.imshow('Chessboard Corners', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
else:
    print("Chessboard corners not found")
复制代码
    1. 相机标定:
      使用 cv2.calibrateCamera 函数进行相机标定。这需要使用多张从不同角度拍摄的棋盘格图像。
复制代码
import cv2
import numpy as np
import glob

# 棋盘格的大小 (内部角点的数量)
chessboard_size = (8, 6)

# 准备对象点 (例如 (0,0,0), (1,0,0), (2,0,0), ....,(7,5,0))
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)

# 存储所有图像的对象点和图像点
objpoints = []  # 3d 点在真实世界空间
imgpoints = []  # 2d 点在图像平面

# 读取所有棋盘格图像
images = glob.glob('chessboard_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)

# 标定相机
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

print("相机矩阵:\n", mtx)
print("畸变系数:\n", dist)
复制代码

以上代码展示了如何生成棋盘格图像、检测棋盘格角点以及进行相机标定。注意,相机标定通常需要多张从不同角度拍摄的棋盘格图像,因此请确保你有足够的图像来进行标定。

 

提问:如何校正原图像

复制代码
import cv2
import numpy as np

# 假设你已经通过cv2.calibrateCamera得到了mtx和dist
# mtx是相机内参矩阵,dist是畸变系数
# img是你想要校正的原图像

# 读取图像(这里以灰度图像为例,但也可以是彩色图像)
img = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_GRAYSCALE)
# 如果是彩色图像,使用cv2.IMREAD_COLOR
# img = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_COLOR)

# 使用cv2.undistort函数校正图像
# 注意:如果img是彩色图像,你需要将每个通道分别校正,但OpenCV通常会自动处理这一点
undistorted_img = cv2.undistort(img, mtx, dist)

# 显示校正前后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Undistorted Image', undistorted_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 保存校正后的图像(可选)
cv2.imwrite('undistorted_image.jpg', undistorted_img)
复制代码

 

复制代码
# 求矩阵的逆,由像素求在相机坐标系中,Z=1平面的X,Y坐标
# def inv_matrix(matrix):
#     try:
#         # 使用 numpy.linalg.inv 计算逆矩阵
#         inv_mat = np.linalg.inv(matrix)
#     except np.linalg.LinAlgError:
#         print("Matrix is singular and cannot be inverted.")
#         return None
#     else:
#         return inv_mat
#
# print(inv_matrix(mtx))
# point_c= np.dot(inv_matrix(mtx),np.array(point_pixel )) # 投影
# print(point_c*z_prime)
复制代码

 

1)图像不要少于20张(中间,四周,远近),否则失败。相机的内参数要合理

2)  棋盘格坐标点经过 刚性变换(外参数矩阵)、相机矩阵得到 像素坐标,与经过校正图像找到的对应点像素坐标 一致么?

3)距离是否合理,特别是相机的外参数

复制代码
import cv2
import numpy as np
import glob

# json
import json
import os

# 棋盘格的大小 (内部角点的数量)  长对应X,宽对应Y
chessboard_size = (11, 8)

# 准备对象点 (例如 (0,0,0), (1,0,0), (2,0,0), ....,(7,5,0))
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)
objp  = objp*1
# 存储所有图像的对象点和图像点
objpoints = []  # 3d 点在真实世界空间
imgpoints = []  # 2d 点在图像平面

# 读取所有棋盘格图像
#images = glob.glob('D:\Python_OpenCV\SC_vantage\SingleCamera\calibrationSingleCamera\*.png')
images = glob.glob('D:/Python_OpenCV/vantage1080/stero_calibration_imgs_2\left\*.png')
#gray = cv2.cvtColor(images[0],cv2.COLOR_BGR2GRAY)

# 亚像素 termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
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:
        # If found, add object points, image points (after refining them)
        objpoints.append(objp)
        # 亚像素
        cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        imgpoints.append(corners)

# 标定相机  mtx,dist 是列表; rvecs, tvecs 是numpy数组
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)


print("相机矩阵:\n", mtx)
print("畸变系数 k1,k2,p1,p2,k3:\n", dist)
# print("旋转矩阵:\n", rvecs)
# print("平移矩阵:\n", tvecs )

#****************棋盘格坐标计算相机坐标****************************************
# 由旋转向量得到旋转矩阵
rmat, _ = cv2.Rodrigues(rvecs[0])  # 转换第 i 个旋转向量

# rmat 是已经转换得到的旋转矩阵,tvec 是对应的平移向量,构造4X4矩阵,三维空间的齐次坐标实现
homog_mat = np.eye(4)  # 创建一个 4x4 的单位矩阵
homog_mat[:3, :3] = rmat  # 填充旋转部分
homog_mat[:3, 3] = np.transpose(tvecs[0]) # 填充平移部分

# 取得棋盘格坐标上一点
X,Y,Z = objp[1]
print("棋盘格坐标系的点坐标:")
print(X,Y,Z)
point_homog = np.dot(homog_mat, np.array([X, Y, Z, 1]))  # 应用 4x4 变换,三维空间的齐次坐标[X, Y, Z, 1]
x_C = point_homog[0] #/ point_homog[2]  # 透视除法得到 x'
y_C = point_homog[1] #/ point_homog[2]  # 透视除法得到 y'
z_C = point_homog[2]
print(x_C,y_C,z_C)# 相机坐标系,使用物理空间坐标

point1_2d =np.array([x_C,y_C,z_C])#将用来计算空间距离
#****************由计算相机坐标计算像平面坐标****************************************
point_pixel = np.dot(mtx,np.array([x_C/z_C,y_C/z_C,1])) # 投影
print("由棋盘坐标计算来的像素坐标:")
print(point_pixel) # 像平面,使用像素坐标
# 打印 objp[1]在原图像的对应的像素坐标

print(imgpoints[0][1])

#****************棋盘格坐标计算相机坐标****************************************
# 取得棋盘格坐标上一点
X,Y,Z = objp[0]
print("棋盘格坐标系的点坐标:")
print(X,Y,Z)
point_homog = np.dot(homog_mat, np.array([X, Y, Z, 1]))  # 应用 4x4 变换,三维空间的齐次坐标[X, Y, Z, 1]
x_C = point_homog[0] #/ point_homog[2]  # 透视除法得到 x'
y_C = point_homog[1] #/ point_homog[2]  # 透视除法得到 y'
z_C = point_homog[2]
print(x_C,y_C,z_C)# 相机坐标系,使用物理空间坐标

point2_2d =np.array([x_C,y_C,z_C])#将用来计算空间距离
#****************由计算相机坐标计算像平面坐标****************************************
point_pixel = np.dot(mtx,np.array([x_C/z_C,y_C/z_C,1])) # 投影
print(point_pixel) # 像平面,使用像素坐标
#point2_2d = point_pixel
print("由棋盘坐标计算来的像素坐标:")
print(imgpoints[0][0])
#****************计算距离,是否符合****************************************
# 计算两点之间的欧几里得距离
distance_2d = np.linalg.norm(point1_2d - point2_2d)
print(distance_2d)

# ***************求矩阵的逆,由像素求在相机坐标系中,Z=1平面的X,Y坐标***********
def inv_matrix(matrix):
    try:
        # 使用 numpy.linalg.inv 计算逆矩阵
        inv_mat = np.linalg.inv(matrix)
    except np.linalg.LinAlgError:
        print("Matrix is singular and cannot be inverted.")
        return None
    else:
        return inv_mat
print("相机内矩阵的逆:")
print(inv_matrix(mtx))
#
print("在相机坐标系内Z=1平面的交点的XYZ坐标:")
point_c= np.dot(inv_matrix(mtx),np.array(point_pixel )) # 投影
print(point_c)
#
#
# ***************使用相机内参数,校正图像***********
# 创建目录,存放零件模板
if not os.path.exists("calibration_pic"):
    os.makedirs("calibration_pic")

for original_filename in images:
    img = cv2.imread(original_filename)
    #  注意:如果img是彩色图像,你需要将每个通道分别校正,但OpenCV通常会自动处理这一点
    undistorted_img = cv2.undistort(img, mtx, dist)
    #更改文件类型
    # 分离文件路径和文件名
    #import os
    path, filename = os.path.split(original_filename)
    # 去除原始扩展名
    name, ext = os.path.splitext(filename)
    # 添加新的扩展名
    new_filename = os.path.join(path, name + '.jpg')
    # 保存校正后的图像(可选)
    cv2.imwrite(new_filename, undistorted_img)

#***************对矫正图像,寻找棋盘格的角点**********************
fname = images[0]
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 找到棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

    # 如果找到角点,则添加对象点和图像点
if ret:
        # If found, add object points, image points (after refining them)
        #objpoints.append(objp)
        # 亚像素
    cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        #imgpoints.append(corners)
    print("校正后的图像点")
    print(corners[0])
# 猜想:#校正后的图像特定点,经过 相机内参数 mtx方阵的逆 变换 就可以得到 在Z=1平面上的坐标 x,y,1。如何知道此像素点的实物与相机的距离,就可以算出此物的在相机坐标系的空间位###置。

  #比较难,可以实现

复制代码

 

确定空间一物体在相机坐标系的位置

1)长方体木块各面异色,已经知道长,宽,高。即知道物体自身的坐标系。

2)利用识别算法,可以识别出图像中的长方体木块,进而找出一个面的四个角点及像素坐标

3)建立像素坐标和物体坐标的点对

4)利用点对估算物体相对相机坐标系的位姿。

 

复制代码
# 已经知道物体自身的特征点坐标
# 已经知相机的内参数
# 求取 物体对相机坐标系的姿态

import cv2
import numpy as np
import glob

# json
import json
import os

# 棋盘格的大小 (内部角点的数量)  长对应X,宽对应Y
chessboard_size = (11, 8)

# 准备对象点 (例如 (0,0,0), (1,0,0), (2,0,0), ....,(7,5,0))
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)

square_size = 15  # 每个格子的大小,单位可以是毫米或米等
objp  = objp*square_size
# 存储所有图像的对象点和图像点
objpoints = []  # 3d 点在真实世界空间
imgpoints = []  # 2d 点在图像平面

# 读取所有棋盘格图像
images = glob.glob('D:\Python_OpenCV\SC_vantage\SingleCamera\calibrationSingleCamera\*.png')
#images = glob.glob('D:/Python_OpenCV/vantage1080/stero_calibration_imgs_2\left\*.png')

# 假设相机内参数矩阵和畸变系数已经通过标定获得
camera_matrix = np.array([ [311.28794266,   0.0       ,322.43776776],
                         [  0.0       , 311.80630946  ,224.42678219],
                         [  0.0       , 0.0           ,1.0         ] ], dtype=np.float32)
#畸变系数 k1,k2,p1,p2,k3
dist_coeffs = np.array([ 0.02349178, -0.1442722,  0.00367356, 0.00248951, 0.08352093], dtype=np.float32)  # 畸变系数

# 亚像素 termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
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:
        # If found, add object points, image points (after refining them)
        objpoints.append(objp)
        # 亚像素
        cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        imgpoints.append(corners)



        # 使用 cv2.solvePnP 计算外参数矩阵(旋转矩阵和平移向量)
        # 注意:我们只需要一幅图像的点对来计算外参数矩阵,但使用多幅图像可以提高精度
        ret, rvec, tvec = cv2.solvePnP(objp, corners, camera_matrix, dist_coeffs)

        # 将旋转向量转换为旋转矩阵
        rmat, _ = cv2.Rodrigues(rvec)

        # 输出旋转矩阵和平移向量
        print("Rotation Matrix:")
        print(rmat)
        print("Translation Vector:")
        print(tvec)
复制代码

 

posted @   辛河  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示