(九)相机内参、外参、反透视变换python opencv

这篇具有很好参考价值的文章主要介绍了(九)相机内参、外参、反透视变换python opencv。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. 背景知识

任务需求:将相机上的一个点投影到真实世界平面上去。

原则上单目相机是不可以的,因为只记录了二维信息,真实世界是三维的,双目相机可以通过视差,或者单目+IMU组合,但是由于特征点在地面上的先验知识,因此可以进行反透视变换。方法有很多种那个,这里采用计算相机的内参和外参的方法。基础知识理论在视觉slam14讲中有详细说明,但是其代码是c++这里采用python opencv实现。

相机内参外参标定:https://blog.csdn.net/qq_29931565/article/details/119395353

逆投影,roadmap:https://blog.csdn.net/qq_53086461/article/details/128028199

需要安装提前安装好numpy、glob、cv2

pip install numpy
pip install opencv-python
pip install glob

2、内参标定

2.1打印一张棋盘格

(九)相机内参、外参、反透视变换python  opencv

2.2拍摄50张照片

import cv2
camera = cv2.VideoCapture(0)
i = 1
while i < 50:
    _, frame = camera.read()
    cv2.imwrite("E:/images/"+str(i)+'.png', frame, [int(cv2.IMWRITE_PNG_COMPRESSION), 0]) 
    cv2.imshow('frame', frame)
    i += 1
    if cv2.waitKey(200) & 0xFF == 27: # 按ESC键退出
        break
cv2.destroyAllWindows()
(九)相机内参、外参、反透视变换python  opencv

2.3 计算内参、畸变

需要更改的地方

第4行:调整为实际的棋盘格数量,格子数-1(格子内部交点数量)

第5行:调整为实际的棋盘格数量

第6行:调整为实际的棋盘格大小

第9行:上一步的存储位置

import cv2
import numpy as np
import glob
objp = np.zeros((6 * 9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2)  # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
objp = 2.6 * objp  # 打印棋盘格一格的边长为2.6cm
obj_points = []  # 存储3D点
img_points = []  # 存储2D点
images = glob.glob("images/*.png")  # 黑白棋盘的图片路径

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    size = gray.shape[::-1]
    ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
    if ret:
        obj_points.append(objp)
        corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1),
                                    (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001))
        if [corners2]:
            img_points.append(corners2)
        else:
            img_points.append(corners)
        cv2.drawChessboardCorners(img, (9, 6), corners, ret)  # 记住,OpenCV的绘制函数一般无返回值
        cv2.waitKey(1)
_, mtx, dist, _, _ = cv2.calibrateCamera(obj_points, img_points, size, None, None)

# 内参数矩阵
Camera_intrinsic = {"mtx": mtx, "dist": dist, }
print("内参")
print(mtx)
print("畸变")
print(dist)

2.4 计算外参

固定相机!!!(之后就不要动了)

因为笔记本自带了一个相机,所以这里是用第二个,根据自己情况修改数字

camera = cv2.VideoCapture(1)

(九)相机内参、外参、反透视变换python  opencv

查看参考资料1:原作者代码显示俯仰角距离等信息,打开line 65、66 ,注释掉 line 67、68即可,效果如下。

(九)相机内参、外参、反透视变换python  opencv

外参包含两部分,旋转矩阵rvec_matrix(公式中的(九)相机内参、外参、反透视变换python  opencv)和平移向量tvec(公式中的(九)相机内参、外参、反透视变换python  opencv),我们通过参考资料2的方式反透视变化,(九)相机内参、外参、反透视变换python  opencv为相机的投影模型,简化为内参矩阵。

(九)相机内参、外参、反透视变换python  opencv的标定我们在图中选取特定的点,进行运算。

代码中为#####部分,uv是相机图像中的坐标值的点,xy1是结算出来对应真实世界的点。

(九)相机内参、外参、反透视变换python  opencv
(九)相机内参、外参、反透视变换python  opencv

以该图片举例:

红色是七个点在图像中的位置,坐标原点在左上角

绿色是七个点在真实世界中的位置,坐标原点在右下角0号点位置

输入:点在图片中的位置,输出点在真实坐标系的位置

import cv2
import numpy as np
import glob
import math
objp = np.zeros((6 * 9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2)  # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
objp = 2.5 * objp  # 打印棋盘格一格的边长为2.6cm
obj_points = []  # 存储3D点
img_points = []  # 存储2D点
images = glob.glob("images/*.png")  # 黑白棋盘的图片路径


mtx=[[444.26980017,0.0,330.00914635], [  0.0,442.49395109,239.56771487], [  0.0,0.0,1.0]]
mtx=np.array(mtx)

dist = [[ 1.47649702e-01,-2.72487592e-01 ,7.01492649e-05 ,2.74167922e-03,   1.84670647e-01]]
dist=np.array(dist)
# 内参数矩阵
Camera_intrinsic = {"mtx": mtx, "dist": dist, }
print("内参")
print(mtx)
print("畸变")
print(dist)



obj_points = objp  # 存储3D点
img_points = []  # 存储2D点

# 从摄像头获取视频图像
camera = cv2.VideoCapture(1)

while True:
    _, frame = camera.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    size = gray.shape[::-1]
    ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
    if ret:  # 画面中有棋盘格
        img_points = np.array(corners)
        cv2.drawChessboardCorners(frame, (9, 6), corners, ret)
        # rvec: 旋转向量 tvec: 平移向量
        _, rvec, tvec = cv2.solvePnP(obj_points, img_points, Camera_intrinsic["mtx"], Camera_intrinsic["dist"])  # 解算位姿
        distance = math.sqrt(tvec[0] ** 2 + tvec[1] ** 2 + tvec[2] ** 2)  # 计算距离
        rvec_matrix = cv2.Rodrigues(rvec)[0]  # 旋转向量->旋转矩阵
        print("旋转矩阵")
        print(rvec_matrix)
        print("tvec")
        print(tvec)
        #####################
        rt =np.array([[rvec_matrix[0][0],rvec_matrix[0][1],tvec[0]],
                    [rvec_matrix[1][0],rvec_matrix[1][1],tvec[1]],
                    [rvec_matrix[2][0],rvec_matrix[2][1],tvec[2]]], dtype=np.float)
        rt_i=np.linalg.inv(rt)
        pi_i = np.linalg.inv(Camera_intrinsic["mtx"])
        uv = np.array([[434,396,362,331,338,376,480],
                       [349,331,315,300,347,368,371],
                       [1,1,1,1,1,1,1]])
        xy1 = 0.340909*rt_i.dot(pi_i.dot(uv))
        print("xy1")
        print(xy1)
        #####################
        proj_matrix = np.hstack((rvec_matrix, tvec))  # hstack: 水平合并
        eulerAngles = cv2.decomposeProjectionMatrix(proj_matrix)[6]  # 欧拉角
        pitch, yaw, roll = eulerAngles[0], eulerAngles[1], eulerAngles[2]
        #cv2.putText(frame, "dist: %.2fcm, yaw: %.2f, pitch: %.2f, roll: %.2f" % (distance, yaw, pitch, roll),
                    #(10, frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
        cv2.putText(frame, "dist: %.2fcm,x : %.2f,y : %.2f, z: %.2f" % (distance, tvec[0], tvec[1], tvec[2]),
        (10, frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)

        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == 27:  # 按ESC键退出
            break
    else:  # 画面中没有棋盘格
        cv2.putText(frame, "Unable to Detect Chessboard", (20, frame.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 1.3,
                    (0, 0, 255), 3)
        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == 27:  # 按ESC键退出
            break
cv2.destroyAllWindows()
(九)相机内参、外参、反透视变换python  opencv

这里是结果,可以看到还是有一定误差,系数(九)相机内参、外参、反透视变换python  opencv是根据4号点算的。

3、yolo中实现

这里我们更改了yolov5 detect.py中的Add bbox to image部分,让结果加在识别框上。思路是提取下边缘中点进行反透视投影。

                    if save_img or save_crop or view_img:  # Add bbox to image
                        c = int(cls)  # integer class
                        label = None if hide_labels else (names[c] if hide_conf else f' {names[c]}{conf:.2f}')
                        #########

                        mtx = [[444.26980017, 0.0, 330.00914635], [0.0, 442.49395109, 239.56771487], [0.0, 0.0, 1.0]]
                        mtx = np.array(mtx)
                        dist = [[1.47649702e-01, -2.72487592e-01, 7.01492649e-05, 2.74167922e-03, 1.84670647e-01]]
                        dist = np.array(dist)
                        Camera_intrinsic = {"mtx": mtx, "dist": dist, }
                        rvec_matrix= np.array([[-0.99611232,-0.05914762,-0.06528244],
                                                 [-0.04042639,-0.35150008,0.9353146 ],
                                                 [-0.07826841,0.93431753,0.34774244]])
                        tvec=np.array([[8.0553314],
                                         [8.50746768],
                                         [34.52738218]])

                        rt = np.array([[rvec_matrix[0][0], rvec_matrix[0][1], tvec[0]],
                                       [rvec_matrix[1][0], rvec_matrix[1][1], tvec[1]],
                                       [rvec_matrix[2][0], rvec_matrix[2][1], tvec[2]]], dtype=np.float)
                        rt_i = np.linalg.inv(rt)
                        pi_i = np.linalg.inv(Camera_intrinsic["mtx"])
                        x1, y1, x2, y2 = xyxy
                        xx = (x1 + x2) / 2
                        yy = max(y1, y2);
                        uv = np.array([[xx.cpu()],
                                       [yy.cpu()],
                                       [1]])
                        xy1 =rt_i.dot(pi_i.dot(uv))
                        xy1[0]=xy1[0]*0.43
                        xy1[1]=xy1[1]*0.43
                        xxx="%.3f" % xy1[0]
                        yyy="%.2f" % xy1[1]
                        label =xxx+" "+yyy
                        #########
                        annotator.box_label(xyxy, label, color=colors(c, True))
(九)相机内参、外参、反透视变换python  opencv

可以看到,误差似乎比单点更大了一点,怀疑是投影模型没有考虑畸变,更改如下:

before=np.array([[[xx.cpu(),yy.cpu()]]])
                        end = cv.undistortPoints(before,mtx, dist, P=mtx)
                        xx = end[0][0][0]
                        yy = end[0][0][1]
                        uv = np.array([[xx],
                                       [yy],
                                       [1]])

但是,后来发现差不多,测试了下去畸变函数,可以发现并没有什么区别。

import cv2 as cv
import numpy as np
mtx = [[444.26980017, 0.0, 330.00914635], [0.0, 442.49395109, 239.56771487], [0.0, 0.0, 1.0]]
mtx = np.array(mtx)
dist = [[1.47649702e-01, -2.72487592e-01, 7.01492649e-05, 2.74167922e-03, 1.84670647e-01]]
dist = np.array(dist)
img = cv.imread('imagess/1.png')
img_undistored = cv.undistort(img, mtx, dist)
cv.imwrite('imagess/11.png', img_undistored)
(九)相机内参、外参、反透视变换python  opencv

4、总结

  1. 原理上可行,根据棋盘格算距离也比较准确。但最终精度不高,可能是yolo本身下边缘也不准。

  1. 这种方法本来精度就有限,尤其上车后,还存在俯仰角度变化等,所以后续还要优化。

  1. 计算内参时候发现,选优不同数量的图片和角度时,结果有时候差距也比较大,可能是影响结果来源之一。另外一个打印的纸张后来发现不是严格的2.5cm,实际要大一点点,因此量格子的时候十个一量算平均数可能结果更加精确。

4、留坑:后续可能和(八)做联动文章来源地址https://www.toymoban.com/news/detail-462602.html

到了这里,关于(九)相机内参、外参、反透视变换python opencv的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一文理清相机内参和外参矩阵的来龙去脉 -- review向

    Auther:SeaHIRobot date: 2023-05-03 最近在做一门课程的项目,在pybullet中复现GGCNN的机械臂视觉抓取的深度学习网络。在搭建仿真环境时,又回到了绕不开的相机内参和外参。借此机会对相机内参外参这一套进行一个review,对这个问题进行一个逻辑梳理。 因此本文不建议纯小白阅读

    2024年02月04日
    浏览(37)
  • 使用ROS功能包camera_calibration进行单目相机和双目相机的内参和外参标定

    本文总结使用ROS标定单目和双目相机的过程,同时提供生成棋盘格文件的方法。 参考链接: [1]使用ros标定相机的内参和外参 [2]ROS下采用camera_calibration进行双目相机标定 棋盘格可以自己买一个,或者打印一个粘在板子上,棋盘格电子版生成可以参考博客《使用kalibr标定工具进

    2024年02月11日
    浏览(32)
  • python opencv实现相机内参标定

    使用python opencv 标定相机内参。 (1)从网络上下载一张棋盘格图片,粘贴到word文档上,设定尺寸大小为合适值,作为标定板。 (2)在不同距离,不同角度下用手机相机拍摄棋盘图片。 (3)调用 opencv findChessboardCorners 和 cornerSubPix 函数提取棋盘的角点。 (4)调用 opencv cal

    2024年02月13日
    浏览(37)
  • 【相机标定】opencv python 标定相机内参时不计算 k3 畸变参数

    畸变参数 k3 通常用于描述径向畸变的更高阶效应,即在需要高精度的应用中可以用到,一般的应用中 k1, k2 足矣。 常见的应用中, orbslam3 中是否传入 k3 是可选的,而 kalibr 标定中则只需要传入 k1, k2 。但计算 k3 时的 k1, k2 不等于不计算 k3 时的 k1, k2 ,因此需要学会两种场景下

    2024年02月09日
    浏览(28)
  • Python中OpenCV透视变换恢复扭曲图像

    在处理图像问题时,经常会遇到将要处理的目标的位置是斜的,需要使用透视变换进行矫正。如下图,该图片中左边的目标是扭曲倾斜拍摄的,那么任务就是将其矫正过来,如下图右图所示。 前提1:这里假设我已经知道四个点坐标(可用深度学习方法检测/分割)和目标宽高

    2024年01月20日
    浏览(38)
  • 【Python】【OpenCV】OCR识别(二)——透视变换

    对于OCR技术在处理有角度有偏差的图像时是比较困难的,而水平的图像使用OCR识别准确度会高很多,因为文本通常是水平排列的,而OCR算法一般会假设文本是水平的。 针对上述情况,所以我们在处理有角度的图象时,需要将图像“摆正”,将使用到getPerspectiveTransform方法和

    2024年02月03日
    浏览(34)
  • 【Python图像处理篇】opencv中的仿射变换和透视变换

    仿射变换可以将矩形图片映射为平行四边形, 透视变换可以将矩形图片映射为任意四边形。 opencv提供了两个变换函数,cv2.warpAffine和cv2.warpPerspective, 使用这两个函数可以实现所有类型的变换。 cv2.warpAffine 接收的参数2x3的变换矩阵; 而 cv2.warpPerspective 接收的3x3的变换矩阵。

    2024年01月24日
    浏览(44)
  • OpenCV(图像处理)-基于Python-图像的基本变换-平移-翻转-仿射变换-透视变换

    为了方便开发人员的操作,OpenCV还提供了一些图像变换的API,本篇文章讲简单介绍各种API的使用,并附上一些样例。 图像缩放函数,用于把图像按指定的尺寸放大或缩小。 dst = cv2.resize(src, dsize, fx, fy, interpolation) dst = 生成的目的图像 src:需要变换的原图像 disize:(x, y)需要

    2024年02月08日
    浏览(42)
  • 相机标定(三)—— 正交投影和透视投影变换

    平面投影分为平行投影和透视投影两种类型,平行投影是具有矩形观察体的投影方式(透视投影则是视锥观察体),它不会根据物体离视点的远近缩放物体(透视投影则会)。平行投影可以分为侧投影和正交投影两种类型。

    2023年04月08日
    浏览(31)
  • Opencv 相机内参标定及使用

    目录 一、功能描述 二、标定板制作 三、图像采集 四、标定内参 方法一:Matlab标定  方法二:C++程序标定 五、使用内参 1.本文用于记录通过 Opencv 进行相机内参标定和对内参的使用来进行图像畸变矫正。         1)相机矩阵:包括焦距(fx,fy),光学中心(Cx,Cy),完

    2024年01月24日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包