import cv2
import numpy as np
import json
import os
from scipy.spatial import distance
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def load_camera_params(config_path):
"""从配置文件加载相机参数"""
with open(config_path, 'r') as f:
config = json.load(f)
return config
def project_3d_to_2d(point_3d, camera_matrix, dist_coeffs=None, rvec=None, tvec=None):
"""
将3D坐标投影到2D图像上
:param point_3d: 3D坐标 (X, Y, Z)
:param camera_matrix: 相机内参矩阵 [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]
:param dist_coeffs: 畸变系数 (k1, k2, p1, p2, k3)
:param rvec: 旋转向量
:param tvec: 平移向量
:return: 2D图像坐标 (u, v)
"""
if dist_coeffs is None:
dist_coeffs = np.zeros(5, dtype=np.float32)
if rvec is None:
rvec = np.zeros(3, dtype=np.float32)
if tvec is None:
tvec = np.zeros(3, dtype=np.float32)
# 将3D点转换为numpy数组
point = np.array([point_3d], dtype=np.float32)
# 投影3D点到2D
projected, _ = cv2.projectPoints(
point, rvec, tvec, camera_matrix, dist_coeffs
)
return tuple(map(int, projected[0][0]))
def draw_3d_bbox(image, center_3d, size, camera_matrix, dist_coeffs=None, rvec=None, tvec=None, color=(0, 255, 0), thickness=2):
"""
在图像上绘制车辆的3D边界框
:param center_3d: 车辆中心3D坐标
:param size: 车辆尺寸 (长, 宽, 高)
:param color: 框线颜色 (B, G, R)
:param thickness: 线宽
"""
l, w, h = size
half_l, half_w, half_h = l/2, w/2, h/2
# 生成3D边界框的8个角点
corners_3d = [
[center_3d[0] - half_l, center_3d[1] - half_w, center_3d[2] - half_h],
[center_3d[0] + half_l, center_3d[1] - half_w, center_3d[2] - half_h],
[center_3d[0] + half_l, center_3d[1] + half_w, center_3d[2] - half_h],
[center_3d[0] - half_l, center_3d[1] + half_w, center_3d[2] - half_h],
[center_3d[0] - half_l, center_3d[1] - half_w, center_3d[2] + half_h],
[center_3d[0] + half_l, center_3d[1] - half_w, center_3d[2] + half_h],
[center_3d[0] + half_l, center_3d[1] + half_w, center_3d[2] + half_h],
[center_3d[0] - half_l, center_3d[1] + half_w, center_3d[2] + half_h]
]
# 投影角点到2D
corners_2d = [project_3d_to_2d(pt, camera_matrix, dist_coeffs, rvec, tvec) for pt in corners_3d]
# 绘制底部矩形
for i in range(4):
cv2.line(image, corners_2d[i], corners_2d[(i+1)%4], color, thickness)
# 绘制顶部矩形
for i in range(4):
cv2.line(image, corners_2d[i+4], corners_2d[4+(i+1)%4], color, thickness)
# 绘制垂直线
for i in range(4):
cv2.line(image, corners_2d[i], corners_2d[i+4], color, thickness)
# 绘制中心点
center_2d = project_3d_to_2d(center_3d, camera_matrix, dist_coeffs, rvec, tvec)
cv2.circle(image, center_2d, 5, (0, 0, 255), -1)
return center_2d
def draw_distance(image, point1, point2, distance_3d, color=(0, 255, 255), thickness=2):
"""
在图像上绘制两点之间的距离
:param point1: 第一个点的2D坐标
:param point2: 第二个点的2D坐标
:param distance_3d: 3D空间中的实际距离
:param color: 线条颜色
"""
# 绘制连接线
cv2.line(image, point1, point2, color, thickness)
# 计算中点位置
mid_point = ((point1[0] + point2[0]) // 2, (point1[1] + point2[1]) // 2)
# 绘制距离标签
distance_text = f"{distance_3d:.2f}m"
cv2.putText(image, distance_text, mid_point,
cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
# 绘制端点标记
cv2.circle(image, point1, 8, color, -1)
cv2.circle(image, point2, 8, color, -1)
def visualize_3d_scene(vehicles_3d, camera_position, connections=None):
"""
可视化3D场景
:param vehicles_3d: 车辆3D坐标列表
:param camera_position: 相机位置
:param connections: 需要连接的车辆对
"""
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 绘制车辆位置
for i, (x, y, z) in enumerate(vehicles_3d):
ax.scatter(x, y, z, c='b', s=100)
ax.text(x, y, z, f'V{i+1}', size=12)
# 绘制相机位置
cx, cy, cz = camera_position
ax.scatter(cx, cy, cz, c='r', s=150, marker='s')
ax.text(cx, cy, cz, 'Camera', size=12)
# 绘制连接线
if connections:
for (i, j) in connections:
x1, y1, z1 = vehicles_3d[i]
x2, y2, z2 = vehicles_3d[j]
ax.plot([x1, x2], [y1, y2], [z1, z2], 'g-', linewidth=2)
# 设置坐标轴标签
ax.set_xlabel('X (m)')
ax.set_ylabel('Y (m)')
ax.set_zlabel('Z (m)')
ax.set_title('3D Vehicle Positions')
plt.tight_layout()
plt.show()
def main(image_path, vehicles, camera_params, output_path=None):
"""
主处理函数
:param image_path: 输入图像路径
:param vehicles: 车辆列表,每个元素为字典 {'id': int, 'coords': [x, y, z]}
:param camera_params: 相机参数字典
:param output_path: 输出图像路径
"""
# 加载图像
image = cv2.imread(image_path)
if image is None:
print(f"错误: 无法加载图像 {image_path}")
return
# 提取相机参数
camera_matrix = np.array(camera_params['intrinsic'])
dist_coeffs = np.array(camera_params['distortion']) if 'distortion' in camera_params else None
rvec = np.array(camera_params['rotation']) if 'rotation' in camera_params else None
tvec = np.array(camera_params['translation']) if 'translation' in camera_params else None
# 标准车辆尺寸 (长, 宽, 高) 单位:米
vehicle_size = [4.5, 1.8, 1.5]
# 获取车辆3D坐标
vehicle_coords = [v['coords'] for v in vehicles]
vehicle_ids = [v['id'] for v in vehicles]
# 绘制每辆车的3D边界框
centers_2d = []
for coords in vehicle_coords:
center_2d = draw_3d_bbox(image, coords, vehicle_size, camera_matrix, dist_coeffs, rvec, tvec)
centers_2d.append(center_2d)
# 计算并标注车辆之间的距离
connections = []
for i in range(len(vehicle_coords)):
for j in range(i+1, len(vehicle_coords)):
# 计算3D欧几里得距离
dist = distance.euclidean(vehicle_coords[i], vehicle_coords[j])
# 绘制距离标注 (只绘制近距离的车辆对)
if dist < 20: # 只标注20米内的车辆
draw_distance(image, centers_2d[i], centers_2d[j], dist)
connections.append((i, j))
# 添加图像标题
cv2.putText(image, "Vehicle Distance Visualization", (20, 40),
cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3)
# 显示或保存结果
if output_path:
cv2.imwrite(output_path, image)
print(f"结果已保存至: {output_path}")
# 显示结果图像
cv2.imshow("Vehicle Projection and Distance", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 可视化3D场景
camera_position = tvec if tvec is not None else [0, 0, 0]
visualize_3d_scene(vehicle_coords, camera_position, connections)
def process_from_json(json_file):
"""
从JSON文件加载数据并处理
:param json_file: JSON文件路径
"""
with open(json_file, 'r') as f:
data = json.load(f)
# 提取必要数据
image_path = data['image_path']
vehicles = data['vehicles']
# 相机参数 (这里使用示例参数,实际应从配置文件或元数据获取)
camera_params = {
'intrinsic': [[1000, 0, 640], [0, 1000, 360], [0, 0, 1]], # 内参矩阵
'distortion': [0.1, -0.05, 0, 0, 0], # 畸变系数
'rotation': [0, 0, 0], # 旋转向量
'translation': [0, 0, 0] # 平移向量
}
# 输出路径
output_dir = os.path.dirname(image_path)
output_name = os.path.splitext(os.path.basename(image_path))[0] + '_annotated.jpg'
output_path = os.path.join(output_dir, output_name)
# 处理图像
main(image_path, vehicles, camera_params, output_path)
# 使用示例
if __name__ == "__main__":
# 示例1: 直接使用参数调用
image_path = "path/to/your/image.jpg"
# 车辆3D坐标 (相机坐标系)
vehicles = [
{"id": 1, "coords": [8.2, -1.2, 15.5]},
{"id": 2, "coords": [6.5, 1.8, 12.3]},
{"id": 3, "coords": [10.5, -2.1, 18.7]}
]
# 相机参数 (需要根据实际相机校准)
camera_params = {
'intrinsic': [[1200, 0, 960], [0, 1200, 540], [0, 0, 1]], # 内参矩阵
'distortion': [0.1, -0.05, 0, 0, 0], # 畸变系数
'rotation': [0, 0, 0], # 旋转向量
'translation': [0, 0, 0] # 平移向量
}
# 处理图像
main(image_path, vehicles, camera_params, "output_image.jpg")
# 示例2: 从JSON文件处理
# process_from_json("image_data.json")
最新发布