lidar_camera_cablition/api_alg_pnp.py
2024-12-16 12:30:52 +08:00

333 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Import modules
import os
from unicodedata import mirrored
import cv2
import numpy as np
import math
import time
import json
import sys
# import transforms3d as tfs
# 1、src 放标准图还是待检测图
# 2、问题一两张图的尺寸要一致
# 3、问题二相机的z轴 是手臂的y轴
# 4、所有参数应该是 第二个参数到第一个参数的差值::标准图必须做目标图像
# 5、if m.distance < 0.72*n.distance: 这个阈值是个关键参数,越小定位约精准
# 6、assert(isRotationMatrix(R)) 报错是因为截图或者拉伸的原因,使得角度旋转无解
# 7、截图比例会影响旋转矩阵甚至会使得旋转矩阵无解。一定要4:3
# 8、原图中目标图像的像素数量与目标图像的像素数量【缩放比】越接近M的值越准确
# 9、缩放比需要打印
# 10、会改变M的参数变量包括
# img1 = cv2.resize
# interpolation=cv2.INTER_AREA
# cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 1.0) 1.0
# if m.distance < 0.85 * n.distance: 0.85
# 11、编写自动化选择参数脚本自动找到最优解
# 12、可优化参数
# cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# cv2.resize
########################################################################
#Import pictures
homo = "./image/2_origin_centor.jpg"
homo_train = "./image/10_bigorigin_rz_x.jpg"
homo = "./scp_file//o.jpg"
homo_train = "./scp_file/6_-0.59803_0.264184_0.588617_-1.556_0.343_1.072.jpg"
homo = "./error1009/grayscale1.jpg"
homo_train = "./error1009/9983823d/GRAY_SCALE/WIDE/1/IMG_20221009_105934.jpg"
# homo = "./error0926/c24.jpg"
# homo = "./error0928/1g.jpg"
# homo_train = "./error0926/IMG_20220926_165931.jpg"
homo = "./error1010/sfrplus.jpg"
homo_train = "./error1010/IMG_20221010_162855.jpg"
# homo_train = "./060701/IMG_20230604_130953.jpg"
# homo = "./error06042/C24.jpg"
# homo_train = "./060701/IMG_20230603_185437.jpg"
# homo_train = "./060701/IMG_20230603_185512.jpg"
# homo_train = "./060701/IMG_20230603_185518.jpg"
# homo_train = "./error0604/IMG_20230604_175620.jpg"
# homo_train = "./error0604/IMG_20230604_175620.jpg"
# homo_train = "./error06042/IMG_20230604_182406.jpg"
homo = "./error0605/grayscale.jpg"
homo_train = "./error0605/IMG_20230605_121151.jpg"
# homo = "./error0605/sfrplus.jpg"
# homo_train = "./error0605/IMG_20230605_132952.jpg"
# homo = "./error0605 near/sfrplus.jpg"
# homo_train = "./error0605 near/IMG_20230605_174504.jpg"
homo = "./error0605gray/grayscale.jpg"
homo_train = "./error0605gray/IMG_20230605_194543.jpg"
def Pnp(objpos, imgpos, M, cameraMatrix, coff_dis):
dst_imgpos = cv2.perspectiveTransform(imgpos, M)
# imgp.append(dst_imgpos)
# pnp 求解
_, rvec, tvec = cv2.solvePnP(objpos, dst_imgpos, cameraMatrix, None, flags=cv2.SOLVEPNP_UPNP)
# print(cv2.Rodrigues(rvec))
rotM = cv2.Rodrigues(rvec)[0]
# camera_postion = -np.mat(rotM).T * np.mat(tvec)
# Ca = camera_postion.T
# 计算相机坐标系的三轴旋转欧拉角旋转后可以转出世界坐标系。旋转顺序z,y,x
thetaZ = math.atan2(rotM[1, 0], rotM[0, 0]) * 180.0 / math.pi
thetaY = math.atan2(-1.0 * rotM[2, 0], math.sqrt(rotM[2, 1] ** 2 + rotM[2, 2] ** 2)) * 180.0 / math.pi
thetaX = math.atan2(rotM[2, 1], rotM[2, 2]) * 180.0 / math.pi
# 相机坐标系下值
x = tvec[0]
y = tvec[1]
z = tvec[2]
# 进行三次旋转
def RotateByZ(Cx, Cy, thetaZ):
rz = thetaZ * math.pi / 180.0
outX = math.cos(rz) * Cx - math.sin(rz) * Cy
outY = math.sin(rz) * Cx + math.cos(rz) * Cy
return outX, outY
def RotateByY(Cx, Cz, thetaY):
ry = thetaY * math.pi / 180.0
outZ = math.cos(ry) * Cz - math.sin(ry) * Cx
outX = math.sin(ry) * Cz + math.cos(ry) * Cx
return outX, outZ
def RotateByX(Cy, Cz, thetaX):
rx = thetaX * math.pi / 180.0
outY = math.cos(rx) * Cy - math.sin(rx) * Cz
outZ = math.sin(rx) * Cy + math.cos(rx) * Cz
return outY, outZ
(x, y) = RotateByZ(x, y, -1.0 * thetaZ)
(x, z) = RotateByY(x, z, -1.0 * thetaY)
(y, z) = RotateByX(y, z, -1.0 * thetaX)
Cx = x * -1
Cy = y * -1
Cz = z * -1
# 输出相机位置
camera_Location = [Cx, Cy, Cz]
# print("相机位姿",camera_Location)
# 输出相机旋转角
# print("相机旋转角度:",'[', thetaX, thetaY, -thetaZ, ']')
angles = [thetaX, thetaY, thetaZ]
#result = [camera_Location, "旋转角度", angles]
return angles, camera_Location
# 欧拉角->旋转矩阵
def eulerAnglesToRotationMatrix(theta) :
R_x = np.array([[1, 0, 0 ],
[0, math.cos(theta[0]), -math.sin(theta[0]) ],
[0, math.sin(theta[0]), math.cos(theta[0]) ]
])
R_y = np.array([[math.cos(theta[1]), 0, math.sin(theta[1]) ],
[0, 1, 0 ],
[-math.sin(theta[1]), 0, math.cos(theta[1]) ]
])
R_z = np.array([[math.cos(theta[2]), -math.sin(theta[2]), 0],
[math.sin(theta[2]), math.cos(theta[2]), 0],
[0, 0, 1]
])
R = np.dot(R_z, np.dot( R_y, R_x ))
return R
def _angel_zoom(rx, ry, rz, angel_zoom=1):
ax, angle = tfs.euler.euler2axangle(rx, ry, rz, "sxyz")
eulers = tfs.euler.axangle2euler(ax * angel_zoom, angle * angel_zoom, "sxyz")
return eulers[0], eulers[1], eulers[2]
# 固定 resolution = 1 sigma = 1.9
def cal_homo(homo_train, homo, mirror = False, resolution = 1, angle_zoom=1, sigma = 1.6):
# 默认初始化参数
resolution = 1
angle_zoom = 1
# SIFT:该值越大被选中的特征点越多计算时间越长推荐10 同时适用于广角和后置
edgeThreshold = 10
# SIFT:该值越大,精度越高,计算时间越长
sigma = 4.5
# if os.path.basename(homo) == "grayscale.jpg": sigma = 1.9
ransacReprojThreshold = 3.0 # Homography: 该值越小,精度越高,对时间没有影像
# imgWidth、imgHeight 越大,精度越高,计算时间越长
imgWidth = 1000
imgHeight = 750
# if os.path.basename(homo) == "grayscale.jpg":
# imgWidth = 1600
# imgHeight = 1200
# 算法开始执行计时
start = time.time()
# 读取图像与标准图像进行对比
img1 = cv2.imread(homo_train, cv2.IMREAD_GRAYSCALE)
if mirror: img1 = cv2.flip(img1, 1)
img1 = cv2.resize(img1, (imgWidth, imgHeight), interpolation=cv2.INTER_AREA) # 使用INTER_AREA可以提升特征点识别率
img2 = cv2.imread(homo, cv2.IMREAD_GRAYSCALE)
img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]), interpolation=cv2.INTER_AREA) # 使用INTER_AREA可以提升特征点识别率两张照片必须同样尺寸
# 1、使用 SIFT 进行特征提取
MIN_MATCH_COUNT = 20
MAX_MATCH_COUNT = 100
# 从优选择特征点限定800个
if resolution == 1: sift = cv2.xfeatures2d.SIFT_create(nfeatures=1500, nOctaveLayers=10, contrastThreshold=0.02, edgeThreshold=edgeThreshold, sigma=sigma)
else: sift = cv2.xfeatures2d.SIFT_create(nfeatures=1500)
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 2、使用 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 = []
for m,n in matches:
if m.distance < 0.85 * n.distance:
good.append(m)
# 从优选择特征点限定MAX个特征
# good = sorted(good, key=lambda x: x.distance)
# if len(good) >= MAX_MATCH_COUNT: good = good[:100]
# 如果特征匹配点的数量不低于 MIN_MATCH_COUNT 设置的数量
if len(good) > MIN_MATCH_COUNT:
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
# 3、查找单应矩阵
# 修改第三个参数 1.0 -> 5.0
# 利用基于RANSAC的鲁棒算法选择最优的四组配对点再计算转换矩阵H(3*3)并返回,以便于反向投影错误率达到最小
# ransacReprojThreshold 设置为 1.0,精度最高
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, ransacReprojThreshold, maxIters=1500, confidence=0.998) ## 修改 1.0 -> 5.0
# print("api_alg 单应性矩阵 M:\n", M)
matchesMask = mask.ravel().tolist()
# 用于测试显示,使用时注释
import matplotlib.pyplot as plt
plt.ion()
draw_params = dict(matchColor = (0,255,0),
singlePointColor = None,
matchesMask = matchesMask,
flags = 2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)
plt.imshow(img3, 'gray'),plt.show()
# 4、采用PNP进行相机的姿态估计
h, w = img1.shape
# 模拟实际物体世界坐标参数
if resolution == 1:
objpos = np.float32([[0.001, 0.001, 0], [0.001, 1.200, 0], [1.600, 1.200, 0], [1.600, 0.001, 0]]).reshape(-1, 1, 3)
else:
objpos = np.float32([[0.0008, 0.0008, 0], [0.0008, 0.5994, 0], [0.7992, 0.5994, 0], [0.7992, 0.0008, 0]]).reshape(-1, 1, 3)
imgpos = np.float32([[0,0], [0,h-1], [w-1,h-1], [w-1,0]]).reshape(-1, 1, 2)
# 模拟相机参数
focal_length = img1.shape[1] # 使用摄像头的宽度(像素)代表焦距
center = (img1.shape[1] / 2, img1.shape[0] / 2) # 使用图像的中心代替光学中心
K = np.array([[focal_length, 0, center[0]],
[0, focal_length, center[1]],
[0, 0, 1]])
angles, T = Pnp(objpos, imgpos, M, K, None)
rand = [angles[0] / 180 * math.pi, angles[1] / 180 * math.pi, angles[2] / 180 * math.pi]
R = eulerAnglesToRotationMatrix(angles)
# print("api_alg 图像计算旋转矩阵 R: \n", R)
# print("api_alg 图像计算角度偏移 a: ", angles)
# print("api_alg 图像计算弧度偏移 r: ", rand)
# print("api_alg 图像计算平移偏移 t: ", [[T[0][0]], [T[1][0]], [T[2][0]]])
# print("mmatches count: ", len(matches), " good count: ", len(good), " matchesMask count: ", len(matchesMask))
# print(rand[0], rand[1], rand[2], M[0][0], M[1][1], M[0][2], M[1][2])
print("mmatches count: ", len(matches), " good count: ", len(good), " matchesMask count: ", len(matchesMask))
print(rand[0], rand[1], rand[2], M[0][0], M[1][1], M[0][2], M[1][2])
end = time.time()
print("api_alg 程序的运行时间为:{}".format(end-start))
# print(rand[0], rand[1], rand[2])
if angle_zoom != 1:
rand[0], rand[1], rand[2] = _angel_zoom(rand[0], rand[1], rand[2], angle_zoom)
return rand[0], rand[1], rand[2], M[0][0], M[1][1], M[0][2], M[1][2], R, T
# img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
# print ("api_alg : Not enough matches are found - %d/%d") % (len(good), MIN_MATCH_COUNT)
matchesMask = None
# print("good features is too few!", "mmatches count: ", len(matches), " good count: ", len(good))
if __name__ == '__main__':
cal_homo(homo_train, homo)
# resp = {'code': 0, 'message': 'success'}
# if len(sys.argv) < 3:
# resp['code'] = -1
# resp['message'] = '参数错误!'
# else:
# img1 = sys.argv[1]
# img2 = sys.argv[2]
# mirror = False
# resolution = 0
# angle_zoom = 1
# sigma = 1.3
# if len(sys.argv) >= 4:
# mirror = sys.argv[3].lower() == 'true'
# if len(sys.argv) >= 5:
# resolution = int(sys.argv[4])
# if len(sys.argv) >= 6:
# angle_zoom = float(sys.argv[5])
# if len(sys.argv) >= 7:
# sigma = float(sys.argv[6])
# result = cal_homo(img1, img2, mirror, resolution, angle_zoom, sigma)
# if result is not None:
# resp['result'] = {
# 'drx': result[0],
# 'dry': result[1],
# 'drz': result[2],
# 'hs': result[3],
# 'ws': result[4],
# 'xf': result[5],
# 'yf': result[6],
# 'dx': result[8][0][0],
# 'dy': result[8][1][0],
# 'dz': result[8][2][0],
# }
# else:
# resp['code'] = -1
# resp['message'] = '解析错误!'
# print(json.dumps(resp, indent = 2, ensure_ascii = False))