(5)图像处理
1、从matlab2022b读取 单目相机标定参数
import numpy as np import cv2 """此文件单独运行,用来导入Matlab2022b标定单目相机参数""" ############## matlab的标定值################## # 1103.04069810984 0 952.894900237209 # 0 1104.11471051557 492.954822481377 # 0 0 1 # 径向 0.0303940709745075 -0.0635669030050014 # 切向 4.61519930830675e-05 -0.000416382854898642 # 假设从MATLAB导出的参数如下 (替换为你的实际值) fx = 1103.04069810984 # 焦距x fy = 1104.11471051557 # 焦距y cx = 952.894900237209 # 主点x cy = 492.954822481377 # 主点y s = 0.0 # 倾斜系数 (通常为0) # MATLAB标定的畸变系数 (2径向 + 2切向) k1, k2, k3= 0.0303940709745075, -0.0635669030050014, 0 # 径向畸变系数 p1, p2 = 4.61519930830675e-05, -0.000416382854898642 # 切向畸变系数 image_size = (1920, 1080) # 图像宽高 # 转换为OpenCV相机矩阵 camera_matrix = np.array([ [fx, s, cx], [0, fy, cy], [0, 0, 1] ], dtype=np.float32) # 转换为OpenCV畸变系数 (k3补0) dist_coefficients = np.array([k1, k2, p1, p2, k3], dtype=np.float32) #为了方便使用,可以将参数保存为文件 # 保存参数 np.savez('camera_params.npz', camera_matrix=camera_matrix, dist_coeffs=dist_coefficients, image_size=image_size) # 加载参数 params = np.load('camera_params.npz') camera_matrix = params['camera_matrix'] dist_coefficients = params['dist_coeffs'] image_size = tuple(params['image_size'])
2、img.py 识别圆点


# 文件名:img.py # 20250504 import cv2 import numpy as np #导入自己的类 import log logger = log.logger_init() ######################################################################### # 获取 含有疑似椭圆的灰度图 def get_object_mask(gray,img_show=True): # 初始化MSER检测器(使用正确参数名) mser = cv2.MSER_create( delta=5, min_area=100, max_area=800, max_variation=0.25, min_diversity=0.5 ) # 检测区域 regions, _ = mser.detectRegions(gray) # 过滤 一部分不需要的objects contours = [] for region in regions: if len(region) >= 20: # 至少5个点才能拟合椭圆 ellipse = cv2.fitEllipse(np.array(region)) # 椭圆有效性验证 _, (a, b), angle = ellipse if min(a, b) <= 0 : # 过滤过于扁平的椭圆 continue if max(a,b)/min(a,b)>=1.2: continue # area = cv2.contourArea(region) # if 3.14 * a * b / 4 > 1.2 * area: # 轮廓包围面积过小 # continue contours.append(region) if img_show: # 可视化结果 vis = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR) cv2.drawContours(vis,contours,-1,color=(0,255,0),thickness=2) cv2.imshow('MSER Detection', vis) cv2.waitKey(0) cv2.destroyAllWindows() # 创建模板 rows, cols = gray.shape mask = np.zeros((rows, cols), dtype=np.uint8) cv2.drawContours(mask, contours, -1, 255, -1) # 使用模板 # roi_gray = cv2.bitwise_and(gray, mask) # roi_gray = cv2.bitwise_or(roi_gray, mask) if img_show: cv2.imshow('mask',mask) cv2.waitKey(0) cv2.destroyAllWindows() return mask def find_edges_in_gray(gray): """在灰度图中通过 高斯滤波 ->canny->形态闭运算->找到外轮廓""" # 使用直方图均衡化 对低对比度图像 # equalized = cv2.equalizeHist(gray) # 使用高斯模糊降噪(可选,推荐) blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Canny边缘检测 low_threshold = 100 high_threshold = 200 edges = cv2.Canny(blurred, low_threshold, high_threshold) # 形态学操作 连接断裂边缘: kernel = np.ones((7, 7), np.uint8) closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel) edges = closed # 查找轮廓 contours, hierarchy = cv2.findContours( edges, cv2.RETR_EXTERNAL, # 只检测外部轮廓 cv2.CHAIN_APPROX_SIMPLE # 压缩水平、垂直和对角线段,只保留端点 ) return contours def find_ellipse_in_edges(contours): """在边界曲线内找到接近圆的椭圆的圆心、轮廓线""" min_contour_area = 400 # 根据实际调整 ellipse_contours = [] ellipses = [] for cnt in contours: # 过滤过小轮廓(根据实际调整) if len(cnt) < 10: # 至少需要5个点拟合椭圆(实际可能需要更多) continue # 椭圆拟合 if len(cnt) >= 10: ellipse = cv2.fitEllipse(cnt) (x, y), (major, minor), angle = ellipse # 筛选有效椭圆(根据实际需求调整条件) if max(major, minor) / min(major, minor) < 1.2: # 按照近似为圆的特点筛选 area = cv2.contourArea(cnt) # print(area) if area*1.4 < minor*minor*3.14/4: # 面积特张筛选 continue # 跳过小轮廓 ellipses.append(ellipse) ellipse_contours.append(cnt) return ellipses, ellipse_contours ######################################################################### # 获取 矩形的四个顶点 def get_centroid(points): """计算多边形顶点的质心(几何中心)""" x = [p[0] for p in points] y = [p[1] for p in points] centroid_x = sum(x) / len(points) centroid_y = sum(y) / len(points) return centroid_x, centroid_y def sort_vertice_indices_clockwise(points): """按顺时针顺序排序顶点(支持凸/凹多边形)""" centroid = get_centroid(points) points = np.array(points) # 计算每个点相对于质心的极角(角度) angles = np.arctan2(points[:, 1] - centroid[1],# 1,2象限 【0 180】 3,4象限 【-180 0】 points[:, 0] - centroid[0]) # 极角计算时,建议使用np.arctan2而非np.arctan,避免除零错误 # 按极角排序(顺时针) sorted_indices = np.argsort(angles) #sorted_points = points[sorted_indices] return sorted_indices def point_to_line_distance(p, p1, p2): """ 计算点p到直线(p1, p2)的距离 p1=[x1,y1],作为判断是否在直线上的依据 """ if np.allclose(p1, p2, atol=1e-6): return np.linalg.norm(p - p1) # 如果p1和p2重合,退化为点到点距离 return np.abs(np.cross(p2 - p1, p - p1)) / np.linalg.norm(p2 - p1) def points_classifier_with_line(ellipses,max_ellipse_area =300): """ (x, y), (major, minor), angle = ellipse 将点按照是否近似在同一条直线上为依据,分类为集合 """ # 搜索三点共线的点 from itertools import combinations lines = [] n = len(ellipses) if n < 3: return lines line_point_indexes_list= list() # 遍历所有两点组合 for i, j in combinations(range(n), 2): # 取椭圆圆心作为确定直线的两个点 ellipse1, ellipse2 = ellipses[i], ellipses[j] p1 = np.array(ellipse1[0],dtype=np.float32) p2 = np.array(ellipse2[0],dtype=np.float32) ab1 =ellipse1[1] ab2 =ellipse2[1] if ab1[0]*ab1[1]*3.14/4 > max_ellipse_area or ab2[0]*ab2[1]*3.14/4 > max_ellipse_area: continue # 跳过重合点 thresh = min(ellipse1[1][0],ellipse1[1][1],ellipse2[1][0],ellipse2[1][1] )#阈值 if np.allclose(p1, p2, atol=1e-2): continue # 找到当前直线上的所有点 line_points = [p1, p2] line_index_set =[i,j] for k in range(n): if k == i or k == j:#去重复 continue p3 = np.array([ellipses[k][0][0],ellipses[k][0][1]],dtype=np.float32) # 计算点到直线的距离 epsilon = min(thresh,ellipses[k][1][0],ellipses[k][1][1])*0.1 if point_to_line_distance(p3, p1, p2) < epsilon: line_points.append(p3) line_index_set.append(k) #print(p1,p2,p3) # 如果当前直线有至少3个点,添加记录 if len(line_index_set) >= 3: line_point_indexes_list.append(set(line_index_set)) # 去除重复的线 for i in range(len(line_point_indexes_list)): set_i = line_point_indexes_list[i] if len(set_i)==0: continue for j in range(len(line_point_indexes_list)): if i == j: continue set_j = line_point_indexes_list[j] if len(set_j) == 0: continue set_ij = set_i.intersection(set_j) if len(set_ij)>=3: set_i = set_i.union(set_j) line_point_indexes_list[i] = set_i line_point_indexes_list[j] =set() line_point_indexes_list_ret = list() for each in line_point_indexes_list: if len(each)>=3: line_point_indexes_list_ret.append(sorted(list(each))) # 按直线上点的数量降序排序 lines_sorted = sorted(line_point_indexes_list_ret, key=lambda x: len(x), reverse=True) return lines_sorted def fit_line_opencv(points): """# 方法2:OpenCV的fitLine(可处理垂直线),含垂直线 points1 = np.array([[x1, y1], [x2, y2], ..., [xn, yn]], dtype=np.int32) points2 = np.array([[x1, y1], [x2, y2], ..., [xm, ym]], dtype=np.int32) """ vx, vy, x0, y0 = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) #(vx, vy) 是单位方向向量 (x0, y0) 是直线上的一点 # 计算直线参数 return float(vx), float(vy), float(x0), float(y0) def fit_line_opencv_intersection(line1, line2): """ 计算两条直线 (p1-p2 和 p3-p4) 的交点 :return: 交点坐标 (x, y),若平行则返回 None """ vx1, vy1, x1, y1 = line1 vx2, vy2, x2, y2 = line2 # 解方程组求t1和t2 A = np.array([[vx1, -vx2], [vy1, -vy2]]) b = np.array([[x2 - x1], [y2 - y1]]) try: t1, t2 = np.linalg.solve(A, b) intersection_point = (float(x1 + t1 * vx1),float(y1 + t1 * vy1)) return intersection_point except np.linalg.LinAlgError: # 直线平行或重合,无交点或无穷多交点 return None def fit_line_opencv_angle(line1, line2): vx1, vy1, _, _ = line1 vx2, vy2, _, _ = line2 # 计算方向向量的点积 dot_product = vx1 * vx2 + vy1 * vy2 # 计算向量的模 magnitude1 = np.sqrt(vx1**2 + vy1**2) magnitude2 = np.sqrt(vx2**2 + vy2**2) # 计算夹角的余弦值 cos_theta = dot_product / (magnitude1 * magnitude2) # 防止浮点误差导致arccos参数超出[-1,1]范围 cos_theta = np.clip(cos_theta, -1.0, 1.0) # 计算夹角(弧度) angle_rad = np.arccos(cos_theta) # 转换为角度 angle_deg = np.degrees(angle_rad) # 返回锐角 return min(angle_deg, 180 - angle_deg) def point_in_rotated_rectangle(point, vertices): """ 判断点是否在任意方向的矩形内(向量叉积法) :param point: 待检测的点 (x, y) :param vertices: 矩形的四个顶点 [(x1, y1), (x2, y2), (x3, y3), (x4, y4)],按顺序排列(ccw 或cw) :return: True 如果在矩形内或边上,否则 False """ x, y = point n = len(vertices) inside = True sign_now = True sign_last = None for i in range(n): x1, y1 = vertices[i] x2, y2 = vertices[(i + 1) % n] # 计算叉积 (P - P1) × (P2 - P1) cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1) # 如果叉积符号不一致,说明点在外部 sign_now = True if cross < 0: sign_now = False if sign_last is None or (sign_now == sign_last): sign_last = sign_now continue else: inside = False break return inside def find_k_closest_product_points(points, k): """ 找出 k 个点,使得它们的 x*y 最接近 :param points: 点列表 [(x1, y1), (x2, y2), ...] :param k: 需要选取的点的数量 :return: 最接近的 k 个点及其极差 """ if len(points) < k: return None, None # 不足 k 个点,无法选取 # 计算每个点的 x*y,并存储 (x*y, (x, y)) products = [(x * y, (x, y)) for x, y in points] products.sort() # 按 x*y 排序 min_range = float('inf') best_window = [] # 滑动窗口法,寻找极差最小的 k 个连续点 for i in range(len(products) - k + 1): current_window = products[i:i + k] current_range = current_window[-1][0] - current_window[0][0] if current_range < min_range: min_range = current_range best_window = current_window # 提取点的坐标 closest_points = [point for (prod, point) in best_window] # print(f"最接近的 {k} 个点: {closest_points}") # print(f"它们的 x*y 极差: {min_diff}") return closest_points, min_range def app_get_rectangle_corner(ellipses,img_show=False): """ :param ellipses, ellipse_contours: find_ellipse_in_edges()的返回值 :return: corners 模板的四个点对 obj_points and img_points,顺时针排序 """ # 按照是否近似在一条直线上做分类 line_indices = points_classifier_with_line(ellipses) #根据分类结果拟合直线方程 line_equation_parameters = list() lines_points = list() for i in range(0, len(line_indices)): line = line_indices[i] if len(line) <= 3: continue line_points = list() for index in line: x, y = ellipses[index][0] line_points.append([x, y]) points = np.array(line_points, dtype=np.float32) vx, vy, x0, y0 = fit_line_opencv(points) line_equation_parameters.append([vx, vy, x0, y0]) # 遍历所有两直线组合,求取他们的交点和夹角,保留在矩形范围内的键值对 two_line_intersection_angle = dict() from itertools import combinations for i, j in combinations(range(len(line_equation_parameters)), 2): line1, line2 = line_equation_parameters[i], line_equation_parameters[j] intersection = fit_line_opencv_intersection(line1, line2) # if not point_in_rotated_rectangle(intersection, [(100, 100), (1000, 100), (1000, 800), (100, 800)]): # continue angle = fit_line_opencv_angle(line1, line2) two_line_intersection_angle[f'{i}_{j}'] = (intersection, angle) # 直角点 del_keys =list() for key in two_line_intersection_angle.keys(): value = two_line_intersection_angle[key] if value[1]<88:# 交叉角小于88度,认为不是矩形的顶点 del_keys.append(key) for key in del_keys: del two_line_intersection_angle[key] # 顺时针排序 points = [] for value in two_line_intersection_angle.values(): pt = value[0] points.append(pt) if len(points)<4: logger.error("图像内信息不足。或有遮挡,去除遮挡重拍") return None centroid = get_centroid(points) # 多点的中心 sorted_items = sorted(two_line_intersection_angle.items(), key=lambda item: np.arctan2(item[1][0][1] -centroid[1],item[1][0][0] -centroid[0]) ) # 转换为有序字典(可选) from collections import OrderedDict sorted_dict = OrderedDict(sorted_items) if img_show: # 输出结果 for key, value in sorted_items: logger.info(f"\n{key}: {value}") if len(sorted_dict)<4: logger.error("图像内信息不足。或有遮挡,去除遮挡重拍") return None # 取点坐标 points = list() for value in sorted_dict.values(): points.append(value[0]) return points def app_get_triangle_hole(ellipses,vertices,img_show=False): """ 查找零件的三个安装孔 :param ellipses: 椭圆集 :param vertices: 查找范围的包围点 :param img_show: 是否显示调试信息 :return: 三个点坐标 顺时针排列 """ # 缩小范围 centroid = get_centroid(vertices) # 多点的中心 vertices_shrink=list() for point in vertices: x,y = point x,y = (x-centroid[0])*0.9 + centroid[0], (y - centroid[1]) * 0.9 + centroid[1] vertices_shrink.append([x,y]) # 待选 椭圆 candidate = list() for each in ellipses: point = each[0] if not point_in_rotated_rectangle(point, vertices_shrink): continue candidate.append(each) # 排序 candidate_sorted = sorted(candidate, key= lambda each: each[1][0]*each[1][1],reverse=True) #print(candidate_sorted,len(candidate_sorted)) if len(candidate_sorted)<3: logger.error("图像内信息不足。或有遮挡,去除遮挡重拍") return None k = 3 product_list = list() for each in candidate_sorted: product_list.append(each[1][0]*each[1][1]) score =1e2 start = 0 for i in range(len(product_list)-3+1): product =0 data =product_list[i:i+3] # 样本均值 mean = np.mean(data) #print("样本均值:", mean) # 输出: 6.0 # 样本标准差 std_dev = np.std(data, ddof=1)# ddof=1 表示使用 n-1 校正 #print("样本标准差:", std_dev) # 输出: 3.162 if mean <=0: mean = -mean mean = mean + 1e-2 if std_dev/mean < score:#记录移动起点 score = std_dev/mean start = i if img_show: logger.info(candidate_sorted) logger.info(candidate_sorted[start:start+3] ) #排序 顺时针 hole_ellipses = candidate_sorted[start:start + 3] points = list() for each in hole_ellipses: points.append(each[0]) cx,cy = get_centroid(points) # 多点的中心 hole_points = sorted(points,key=lambda point: np.arctan2(point[1] - cy, point[0] - cx)) pt1, pt2, pt3 = np.array(hole_points[0], dtype=np.float32), np.array(hole_points[1], dtype=np.float32), np.array( hole_points[2], dtype=np.float32) """计算两点间欧几里得距离""" dist_1_2 = cv2.norm(pt1, pt2, cv2.NORM_L2) dist_2_3 = cv2.norm(pt2, pt3, cv2.NORM_L2) dist_3_1 = cv2.norm(pt1, pt3, cv2.NORM_L2) score = [0,0,0] # 判别等腰三角形顶点 score[0] = (dist_1_2 - dist_3_1) ** 2 score[1] = (dist_1_2 - dist_2_3) ** 2 score[2] = (dist_2_3 - dist_3_1) ** 2 id = min(range(len(score)), key=lambda i: score[i])# 顶点的下标 sorted_hole_points= list() len_ls = len(hole_points) for i in range(len_ls): #第一点 是 等腰三角形的顶点 j = i + id j = j %(len_ls) point = hole_points[j] sorted_hole_points.append(point) return sorted_hole_points if __name__ == "__main__": # 读取图像 image= cv2.imread('part04.jpg') cv2.imshow('灰度图', image) # 转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imwrite("result.jpg",gray) # 近似椭圆的object的灰度图 gray = get_object_mask(gray=gray,img_show=False) cv2.imshow('灰度图', gray) cv2.waitKey(0) cv2.destroyAllWindows() # 边界轮廓线 edges = find_edges_in_gray(gray) # 椭圆及轮廓线 ellipses, ellipse_contours = find_ellipse_in_edges(edges) #椭圆集所在矩形的顶点,顺时针排序 vertices = app_get_rectangle_corner(ellipses,img_show=False) # 零件的三个安装孔 第一个点是等腰三角形的顶点 hole_points = app_get_triangle_hole(ellipses,vertices,img_show=False) print(hole_points) line_indices = points_classifier_with_line(ellipses) line_contours = list() for i in range(0,len(line_indices)): line = line_indices[i] if len(line)<=3: continue line_points =list() for index in line: x,y = ellipses[index][0] line_points.append([[int(x),int(y)]]) line_contours.append( np.array(line_points, dtype=np.int32)) vis = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR) # 椭圆 cv2.drawContours(vis,ellipse_contours,-1,color=(0,0,255),thickness=2) # 线段 cv2.polylines(vis, line_contours, isClosed=False, color=(0, 255, 0), thickness=2) # 定位孔 cv2.polylines(vis, [np.array(hole_points,dtype=np.int32)], isClosed=True, color=(255, 0, 0), thickness=2) cv2.imshow('ellipse_contours', vis) cv2.waitKey(0) cv2.destroyAllWindows()
3) 根据像素坐标、物体自身坐标 计算它的相机坐标
# 文件名 calibration_robot.py import cv2 import numpy as np #导入自己的类 import log logger = log.logger_init() def get_chessboard_object_points(chessboard_size = (11, 8),square_size=1,translation_x =0,translation_y =0,translation_z =0): """ 使用棋盘格标定 机器人坐标和相机坐标 :param chessboard_size: (8, 6) 棋盘格的大小 (内部角点的数量) :param square_size: 棋盘格边长 mm :param translation_x: 在机器人坐标系下,棋盘格起点坐标 :param translation_y: 在机器人坐标系下,棋盘格起点坐标 :param translation_z: 在机器人坐标系下,棋盘格起点坐标 :return: """ # 准备对象点 (例如 (0,0,0), (1,0,0), (2,0,0), ....,(7,5,0)) objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32) #print(objp) #objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2) objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2) objp = objp * square_size+[translation_x,translation_y,translation_z] # 使用列表推导式交换每个子列表的第0和第1元素 #objp = [[7-sublist[1], sublist[0], *sublist[2:]] for sublist in objp] objp = [[sublist[1], sublist[0], *sublist[2:]] for sublist in objp] return objp def get_chessboard_image_points(image=None,chessboard_size=(8, 6)): """ 一张图中找到棋盘格的角点 :param image: 图像文件路径 :param chessboard_size: 棋盘格的大小 (内部角点的数量) :return: """ img = image gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 找到棋盘格角点 ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None) # 如果找到角点,则添加对象点和图像点 if ret: # 3. 优化角点精度(亚像素级) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) return corners else: return None def detect_and_label_checkerboard(image_path, pattern_size=(11, 8),image_show=True): """ 识别棋盘格并标注序号 :param image_path: 图像路径 :param pattern_size: 棋盘格内角点数量 (rows, cols) """ # 1. 读取图像 img = cv2.imread(image_path) if img is None: print("Error: 图像无法加载!") return gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 2. 检测棋盘格角点 ret, corners = cv2.findChessboardCorners(gray, pattern_size, None) if not ret: print("Error: 未检测到棋盘格!") return # 3. 优化角点精度(亚像素级) criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) if image_show: # 4. 绘制角点并标注序号 img_draw = img.copy() cv2.drawChessboardCorners(img_draw, pattern_size, corners, ret) # 标注序号 (0,0), (0,1), ..., (n,m) for i, corner in enumerate(corners): x, y = corner.ravel().astype(int) row = i // pattern_size[1] # 行号 col = i % pattern_size[1] # 列号 cv2.putText(img_draw, f"({row},{col})", (x + 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) # 5. 显示结果 cv2.imshow("Checkerboard with Labels", img_draw) cv2.waitKey(0) cv2.destroyAllWindows() # 可选:保存结果 #cv2.imwrite("labeled_checkerboard.jpg", img_draw) return corners #代码 class LocationPart: """在相机坐标系下, 零件的定位 零件的角度 模板的定位 """ def __init__(self): # 加载相机标定参数 params = np.load('camera_params.npz') self.camera_matrix = params['camera_matrix'] self.dist_coefficients = params['dist_coeffs'] self.image_size = tuple(params['image_size']) def get_rotation_transform_vectors(self,obj_points,img_points): """物体的坐标为平面三个点或以上,三点不共线 返回 ret, rotation_vec, transform_vec """ # 只有3个点时 # obj_points = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]], dtype=np.float32) # img_points = np.array([[1000, 600], [1200, 580], [980, 400]], dtype=np.float32) # 方法A:使用SQPNP算法(需要OpenCV 3.4+) 平面三个点及以上 ret, rotation_vec, transform_vec = cv2.solvePnP(obj_points, img_points, self.camera_matrix, self.dist_coefficients, flags=cv2.SOLVEPNP_SQPNP) # 返回结果 return ret, rotation_vec, transform_vec @staticmethod def get_camera_coordinates(obj_points,rotation_vec, transform_vec): """ 由物体的世界点坐标,计算相机坐标系下的点坐标 :param obj_points: # 平面物体上的三个点 (Z=0) obj_points = np.array([[0, 0, 0], # 原点 [1, 0, 0], # X轴1单位 [0, 1, 0]], # Y轴1单位 dtype=np.float32) :param rotation_vec: :param transform_vec: :return: """ # 1. 转换旋转向量为矩阵 R, _ = cv2.Rodrigues(rotation_vec) # 2. 计算相机坐标系坐标 camera_points = [] for pw in obj_points: pc = R @ pw.reshape(3, 1) + transform_vec # 旋转,然后平移 camera_points.append(pc.flatten()) camera_points = np.array(camera_points) return camera_points @staticmethod def get_triangle_obj_points(img_points): """ 已抓取中心为原点,抓取方向定坐标轴 根据安装孔位置成等腰三角形,已知位置尺寸,已计算它的坐标 [(0, 77,0), (52.5, -50,0), (-52.5, -50,0)] 这里匹配 世界坐标和像素坐标 :param img_points: :return: obj_points mm """ pt1,pt2,pt3 = np.array(img_points[0],dtype=np.float32),np.array(img_points[1],dtype=np.float32),np.array(img_points[2],dtype=np.float32) """计算两点间欧几里得距离""" dist_1_2 = cv2.norm(pt1, pt2, cv2.NORM_L2) dist_2_3 = cv2.norm(pt2, pt3, cv2.NORM_L2) dist_3_1 = cv2.norm(pt1, pt3, cv2.NORM_L2) score =list() # 判别等腰三角形顶点 score[0] = (dist_1_2 - dist_3_1) ** 2 score[1] = (dist_1_2 - dist_2_3) ** 2 score[2] = (dist_2_3 - dist_3_1) ** 2 id = min(range(len(score)), key=lambda i: score[i]) if id ==0: return [(0, 77,0), (52.5, -50,0), (-52.5, -50,0)] if id ==1: return [ (52.5, -50, 0), (0, 77, 0),(-52.5, -50, 0)] if id ==2: return [(52.5, -50, 0), (-52.5, -50, 0), (0, 77, 0)] @staticmethod def get_rectangle_obj_points(img_points): """ 已知矩形的长宽分别是:240*280 mm :param img_points: :return:obj_points 根据像素坐标,匹配物体为参照的世界坐标值 (0,0),(240,0),(240,280),(0,280) (0,0),(280,0),(280,240),(0,240) """ if len(img_points) !=4: return None pt1,pt2,pt3 = np.array(img_points[0],dtype=np.float32),np.array(img_points[1],dtype=np.float32),np.array(img_points[2],dtype=np.float32) """计算两点间欧几里得距离""" dist_1_2 = cv2.norm(pt1, pt2, cv2.NORM_L2) dist_2_3 = cv2.norm(pt2, pt3, cv2.NORM_L2) if dist_1_2> dist_2_3: return (0,0,0),(280,0,0),(280,240,0),(0,240,0) else: return (0,0,0),(240,0,0),(240,280,0),(0,280,0) if __name__ == "__main__": #读取图像 image = cv2.imread('chessboard.jpg') if image is None: raise FileNotFoundError("未找到图像文件,请检查路径是否正确") obj_points=get_chessboard_object_points(square_size=1,translation_x =0,translation_y =0,translation_z =0) img_points = get_chessboard_image_points(image=image,chessboard_size=(11,8)) # 示例调用 img_points = detect_and_label_checkerboard("chessboard.jpg", pattern_size=(11, 8)) for i in range(len(img_points)): print(img_points[i],obj_points[i])

浙公网安备 33010602011771号