unit_test.py 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154
  1. import cv2
  2. import os
  3. from tqdm import tqdm
  4. import numpy as np
  5. import scipy.io as sio
  6. import matplotlib.pyplot as plt
  7. from scipy.interpolate import griddata
  8. import glob
  9. from scipy import stats
  10. from scipy.optimize import minimize
  11. from collections import defaultdict
  12. from scipy.signal import savgol_filter
  13. from scipy.ndimage import uniform_filter, gaussian_filter, median_filter
  14. import json
  15. import pickle
  16. import shutil
  17. import pandas as pd
  18. from sklearn.linear_model import LinearRegression
  19. from scipy.interpolate import Rbf
  20. from numpy.linalg import lstsq
  21. from scipy.optimize import curve_fit
  22. from aprilgrid import Detector
  23. from src.utils import *
  24. from scipy.optimize import least_squares
  25. import random
  26. from scipy.io import savemat, loadmat
  27. from src.pcl_postproc import smooth_pcl, align2ref
  28. from src.calibration import *
  29. import math
  30. from src.calibration import calibrate_world, calibrate_screen_chessboard, calibrate_screen_aprilgrid, map_screen_to_world
  31. import numpy as np
  32. import matplotlib.pyplot as plt
  33. from scipy.signal import find_peaks
  34. from skimage.measure import find_contours
  35. import numpy as np
  36. import matplotlib.pyplot as plt
  37. from scipy.ndimage import gaussian_filter
  38. from skimage.restoration import unwrap_phase
  39. from aprilgrid import Detector
  40. from src.calibration.calibrate import generate_3d_points, show_detect_result
  41. def pixel_to_world(points_2d, K, R, T, depth):
  42. """
  43. 将像素坐标转换为世界坐标系中的 3D 坐标。
  44. Parameters:
  45. points_2d: np.ndarray
  46. 图像中的2D像素坐标 (N, 2)。
  47. K: np.ndarray
  48. 相机内参矩阵 (3x3)。
  49. R: np.ndarray
  50. 相机旋转矩阵 (3x3)。
  51. T: np.ndarray
  52. 相机平移向量 (3, 1)。
  53. depth: float or np.ndarray
  54. 点的深度(单位:毫米或其他实际单位)。
  55. Returns:
  56. points_3d_world: np.ndarray
  57. 世界坐标系中的 3D 点 (N, 3)。
  58. """
  59. # 将像素坐标转换为齐次坐标 (u, v, 1)
  60. image_points_3d = np.hstack((points_2d, np.ones((points_2d.shape[0], 1))))
  61. norm_points = np.linalg.inv(K).dot(image_points_3d.reshape(-1, 3).T).T
  62. # 逆投影到3D空间
  63. object_points = []
  64. for point in norm_points:
  65. # 将归一化坐标转换为3D点
  66. point_3d = np.zeros((3,), dtype=np.float32)
  67. point_3d[:2] = point[:2] / point[2] # 归一化坐标转换为3D坐标
  68. point_3d[2] = 1
  69. # 使用旋转和平移向量进行逆投影
  70. # 首先需要将旋转向量转换为旋转矩阵
  71. #R, _ = cv2.Rodrigues(R.reshape(3, 3))
  72. object_point = np.dot(R.T, point_3d - T.reshape(3, 1)).flatten()
  73. object_points.append(object_point)
  74. points_3d_world = np.array(object_points)
  75. print("Object Points:")
  76. print(points_3d_world)
  77. return points_3d_world
  78. def project_to_world(points_2d, K, R, T, dist_coeffs, depth=1.0):
  79. """
  80. 将相机检测到的2D点转换为世界坐标系下的3D点。
  81. Parameters:
  82. points_2d: np.ndarray
  83. 图像中的2D点 (N, 2)。
  84. K: np.ndarray
  85. 相机内参矩阵。
  86. R: np.ndarray
  87. 相机旋转矩阵 (3x3)。
  88. T: np.ndarray
  89. 相机平移向量 (3, 1)。
  90. dist_coeffs: np.ndarray
  91. 相机的畸变系数。
  92. depth: float or np.ndarray
  93. 点的深度(可以是标量或与点的数量匹配的数组)。
  94. Returns:
  95. points_3d_world: np.ndarray
  96. 世界坐标系下的3D点 (N, 3)。
  97. """
  98. # 去畸变并将2D点转换为相机归一化坐标
  99. #points_undistorted = cv2.undistortPoints(points_2d, K, dist_coeffs)
  100. #print('points_undistorted = ', points_undistorted)
  101. # 将2D点扩展为3D点,相机归一化坐标 (x, y, 1)
  102. points_3d_camera = np.hstack((points_2d.squeeze(), np.ones((points_2d.shape[0], 1))))
  103. # 乘以深度,得到实际的相机坐标系中的 3D 点
  104. points_3d_camera *= depth
  105. # 将相机坐标系中的点转换为世界坐标系中的点
  106. points_3d_world = np.dot(R.T, (points_3d_camera.T - T)).T
  107. return points_3d_world
  108. def plot_3d_points(points_3d, label, color, ax):
  109. """
  110. 绘制3D点云。
  111. Parameters:
  112. points_3d: np.ndarray
  113. 3D世界坐标系中的点 (N, 3)。
  114. label: str
  115. 相机的标签名称。
  116. color: str
  117. 点的颜色。
  118. ax: Axes3D
  119. 3D图形对象。
  120. """
  121. ax.scatter(points_3d[:, 0], points_3d[:, 1], points_3d[:, 2], label=label, color=color)
  122. # 定义投影函数
  123. def project_points(points_3d, rvec, tvec, K, dist_coeffs):
  124. """
  125. 将 3D 世界坐标点投影到 2D 图像平面。
  126. Parameters:
  127. points_3d: numpy.ndarray
  128. 3D 点的坐标 (N, 3)。
  129. rvec: numpy.ndarray
  130. 旋转向量 (3,)。
  131. tvec: numpy.ndarray
  132. 平移向量 (3,)。
  133. K: numpy.ndarray
  134. 相机内参矩阵 (3, 3)。
  135. dist_coeffs: numpy.ndarray
  136. 畸变系数。
  137. Returns:
  138. image_points: numpy.ndarray
  139. 投影到图像平面的 2D 点 (N, 2)。
  140. """
  141. image_points, _ = cv2.projectPoints(points_3d, rvec, tvec, K, dist_coeffs)
  142. return np.squeeze(image_points)
  143. # def residuals(params, n_cameras, n_points, camera_indices, point_indices, points_2d, K_matrices, dist_coeffs):
  144. # """
  145. # 计算捆绑调整中的残差,即重投影误差。
  146. # Parameters:
  147. # params: np.ndarray
  148. # 优化的参数(包括所有相机的外参和所有3D点的坐标)。
  149. # n_cameras: int
  150. # 相机的数量。
  151. # n_points: int
  152. # 3D 点的数量。
  153. # camera_indices: np.ndarray
  154. # 每个观测对应的相机索引。
  155. # point_indices: np.ndarray
  156. # 每个观测对应的 3D 点索引。
  157. # points_2d: np.ndarray
  158. # 实际观测到的 2D 图像点。
  159. # K_matrices: list of np.ndarray
  160. # 每个相机的内参矩阵。
  161. # dist_coeffs: list of np.ndarray
  162. # 每个相机的畸变系数。
  163. # Returns:
  164. # residuals: np.ndarray
  165. # 重投影误差。
  166. # """
  167. # # 提取旋转和平移向量
  168. # rvecs = params[:n_cameras * 3].reshape((n_cameras, 3)) # 旋转向量
  169. # tvecs = params[n_cameras * 3:n_cameras * 6].reshape((n_cameras, 3)) # 平移向量
  170. # # 提取3D点
  171. # points_3d = params[n_cameras * 6:].reshape((n_points, 3)) # 3D 点
  172. # residuals = []
  173. # # 计算每个观测的重投影误差
  174. # for i in range(len(camera_indices)):
  175. # cam_idx = camera_indices[i] # 当前观测的相机
  176. # point_idx = point_indices[i] # 当前观测的 3D 点
  177. # K = K_matrices[cam_idx] # 相机的内参矩阵
  178. # dist = dist_coeffs[cam_idx] # 相机的畸变系数
  179. # # 投影3D点到相机平面,使用OpenCV的projectPoints函数
  180. # projected_points, _ = cv2.projectPoints(
  181. # points_3d[point_idx].reshape(1, -1), # 当前的3D点
  182. # rvecs[cam_idx].reshape(-1, 1), # 当前相机的旋转向量 (Rodrigues形式)
  183. # tvecs[cam_idx].reshape(-1, 1), # 当前相机的平移向量
  184. # K, # 内参矩阵
  185. # dist # 畸变系数
  186. # )
  187. # # 将 projected_points 变成 (N, 2) 的形状
  188. # projected_points = projected_points.squeeze()
  189. # # 计算残差(重投影误差)
  190. # residuals.append(projected_points - points_2d[i])
  191. # return np.concatenate(residuals)
  192. def calculate_normal(points):
  193. # points 是一个 (4, 3) 的数组,表示 4 个 3D 点
  194. vec1 = points[1] - points[0]
  195. vec2 = points[2] - points[0]
  196. normal = np.cross(vec1, vec2) # 计算法向量
  197. return normal / np.linalg.norm(normal) # 单位化
  198. def point_to_plane_distance(point, plane_normal, plane_point):
  199. return np.dot(plane_normal, point - plane_point)
  200. def residuals(params, n_cameras, n_points, camera_indices, point_indices, points_2d, K_matrices, dist_coeffs, plane_constraints):
  201. rvecs = params[:n_cameras * 3].reshape((n_cameras, 3))
  202. tvecs = params[n_cameras * 3:n_cameras * 6].reshape((n_cameras, 3))
  203. points_3d = params[n_cameras * 6:].reshape((n_points, 3))
  204. residuals = []
  205. # 计算重投影误差
  206. for i in range(len(camera_indices)):
  207. cam_idx = camera_indices[i]
  208. point_idx = point_indices[i]
  209. K = K_matrices[cam_idx]
  210. dist = dist_coeffs[cam_idx]
  211. projected_points, _ = cv2.projectPoints(
  212. points_3d[point_idx].reshape(1, -1),
  213. rvecs[cam_idx].reshape(3, 1),
  214. tvecs[cam_idx].reshape(3, 1),
  215. K, dist
  216. )
  217. projected_points = projected_points.squeeze()
  218. residual = projected_points - points_2d[i]
  219. residuals.extend(residual.tolist()) # 将重投影误差的两个分量添加到 residuals
  220. # 计算平面约束残差
  221. for constraint in plane_constraints:
  222. points_idx = constraint
  223. plane_points = points_3d[points_idx[:3]] # 取前三个点计算平面
  224. plane_normal = calculate_normal(plane_points)
  225. plane_point = plane_points[0]
  226. for idx in points_idx:
  227. distance = point_to_plane_distance(points_3d[idx], plane_normal, plane_point)
  228. residuals.append(distance) # 添加标量距离值
  229. return np.array(residuals)
  230. def extract_params_from_result(result, n_cameras, n_points):
  231. """
  232. 从优化结果中提取旋转向量、平移向量和3D点数据。
  233. Parameters:
  234. result: OptimizeResult
  235. scipy.optimize.least_squares 返回的结果。
  236. n_cameras: int
  237. 相机的数量。
  238. n_points: int
  239. 3D 点的数量。
  240. Returns:
  241. rvecs: list of np.ndarray
  242. 优化后的旋转向量。
  243. tvecs: list of np.ndarray
  244. 优化后的平移向量。
  245. points_3d: np.ndarray
  246. 优化后的3D点坐标。
  247. """
  248. optimized_params = result.x
  249. # 提取旋转和平移向量
  250. rvecs = optimized_params[:n_cameras * 3].reshape((n_cameras, 3)) # 旋转向量
  251. tvecs = optimized_params[n_cameras * 3:n_cameras * 6].reshape((n_cameras, 3)) # 平移向量
  252. # 提取3D点
  253. #points_3d = optimized_params[n_cameras * 6:].reshape((n_points, 3)) # 3D点
  254. return rvecs, tvecs
  255. def compare_params(optimized_rvecs, optimized_tvecs, original_cam_params):
  256. """
  257. 对比优化后的相机外参和原始标定结果。
  258. Parameters:
  259. optimized_rvecs: np.ndarray
  260. 优化后的旋转向量。
  261. optimized_tvecs: np.ndarray
  262. 优化后的平移向量。
  263. original_cam_params: list of dict
  264. 原始标定结果(包含旋转矩阵和平移向量)。
  265. Returns:
  266. None
  267. """
  268. for cam_idx, original_params in enumerate(original_cam_params):
  269. # 原始旋转矩阵和转换为旋转向量
  270. original_rvec = cv2.Rodrigues(original_params['rotation_matrix'])[0].flatten()
  271. original_tvec = original_params['translation_vector'].flatten()
  272. # 优化后的旋转向量和平移向量
  273. optimized_rvec = optimized_rvecs[cam_idx]
  274. optimized_tvec = optimized_tvecs[cam_idx]
  275. # 计算旋转向量和平移向量的差异
  276. rotation_diff = np.linalg.norm(original_rvec - optimized_rvec)
  277. translation_diff = np.linalg.norm(original_tvec - optimized_tvec)
  278. print(f"相机 {cam_idx} 差异:")
  279. print(f"旋转向量差异: {rotation_diff}")
  280. print(f"平移向量差异: {translation_diff}")
  281. def calculate_reprojection_error(rvecs, tvecs, points_3d, points_2d, K_matrices, dist_coeffs):
  282. """
  283. 计算重投影误差。
  284. Parameters:
  285. rvecs: np.ndarray
  286. 相机的旋转向量。
  287. tvecs: np.ndarray
  288. 相机的平移向量。
  289. points_3d: np.ndarray
  290. 3D点坐标。
  291. points_2d: np.ndarray
  292. 2D图像中的对应点。
  293. K_matrices: list of np.ndarray
  294. 相机内参矩阵。
  295. dist_coeffs: list of np.ndarray
  296. 畸变系数。
  297. Returns:
  298. float
  299. 平均重投影误差。
  300. """
  301. total_error = 0
  302. total_points = 0
  303. for cam_idx in range(len(rvecs)):
  304. projected_points, _ = cv2.projectPoints(points_3d, rvecs[cam_idx], tvecs[cam_idx], K_matrices[cam_idx], dist_coeffs[cam_idx])
  305. error = np.linalg.norm(points_2d[cam_idx] - projected_points.squeeze(), axis=1).sum()
  306. total_error += error
  307. total_points += len(points_2d[cam_idx])
  308. return total_error / total_points
  309. def bundle_adjustment(all_object_points, all_corners, cam_params, plane_constraints):
  310. """
  311. 使用捆绑调整优化相机参数和3D点坐标。
  312. Parameters:
  313. all_object_points: list of list of np.ndarray
  314. 每个相机中每个图像中的 3D 物体点。
  315. all_corners: list of list of np.ndarray
  316. 每个相机中的多个图像中的 2D 图像点(每台相机拍摄了多张图片)。
  317. cam_params: list of dict
  318. 每个相机的内参和外参。
  319. Returns:
  320. result: scipy.optimize.OptimizeResult
  321. 优化后的参数结果。
  322. """
  323. n_cameras = len(cam_params) # 相机的数量
  324. # 展平所有 3D 物体点
  325. all_object_points_flat = []
  326. for object_points_cam in all_object_points:
  327. all_object_points_flat.extend(object_points_cam) # 将每个相机的所有 3D 物体点展平
  328. all_object_points_flat = np.concatenate(all_object_points_flat)
  329. n_points = all_object_points_flat.shape[0]
  330. # 收集相机内参矩阵和畸变系数
  331. K_matrices = [params['camera_matrix'] for params in cam_params]
  332. dist_coeffs = [params['distortion_coefficients'] for params in cam_params]
  333. # 创建相机索引和点索引,用于优化
  334. camera_indices = []
  335. point_indices = []
  336. points_2d = []
  337. # 记录全局 3D 点的偏移量,用于正确生成 point_indices
  338. point_offset = 0
  339. # 遍历每台相机的所有图像
  340. for cam_idx, (object_points_cam, corners_cam) in enumerate(zip(all_object_points, all_corners)):
  341. for img_idx, points_2d_list in enumerate(corners_cam): # 遍历每个相机的每张图像中的2D点
  342. n_points_in_image = points_2d_list.shape[0]
  343. # 给每个2D点分配当前相机的相机索引(相机编号 cam_idx)
  344. camera_indices.extend([cam_idx] * n_points_in_image)
  345. # 给每个2D点分配全局3D点索引
  346. point_indices.extend(range(point_offset, point_offset + n_points_in_image))
  347. # 增加偏移量
  348. point_offset += n_points_in_image
  349. # 收集2D图像点
  350. points_2d.extend(points_2d_list)
  351. camera_indices = np.array(camera_indices)
  352. point_indices = np.array(point_indices)
  353. points_2d = np.array(points_2d)
  354. # 初始化外参(旋转和平移向量)和 3D 点
  355. rvecs = [cv2.Rodrigues(params['rotation_matrix'])[0].flatten() for params in cam_params]
  356. tvecs = [params['translation_vector'].flatten() for params in cam_params]
  357. # 将外参和3D点展平为一维数组
  358. initial_params = np.hstack([
  359. np.concatenate(rvecs), # 所有相机的旋转向量
  360. np.concatenate(tvecs), # 所有相机的平移向量
  361. all_object_points_flat.ravel() # 所有3D点展平
  362. ])
  363. # 执行捆绑调整,使用 'trf' 或 'dogbox' 方法
  364. # result = least_squares(
  365. # residuals, initial_params, verbose=2, x_scale='jac', ftol=1e-4, method='trf', # 使用 'trf' 方法
  366. # args=(n_cameras, n_points, camera_indices, point_indices, points_2d, K_matrices, dist_coeffs)
  367. # )
  368. result = least_squares(
  369. residuals, initial_params, verbose=2, x_scale='jac', ftol=1e-4, method='trf',
  370. args=(n_cameras, n_points, camera_indices, point_indices, points_2d, K_matrices, dist_coeffs, plane_constraints)
  371. )
  372. # 优化前的重投影误差
  373. original_rvecs = [cv2.Rodrigues(params['rotation_matrix'])[0] for params in cam_params]
  374. original_tvecs = [params['translation_vector'] for params in cam_params]
  375. # original_error = calculate_reprojection_error(
  376. # original_rvecs, original_tvecs, original_points_3d, original_points_2d, K_matrices, dist_coeffs
  377. # )
  378. # # 优化后的重投影误差
  379. # optimized_error = calculate_reprojection_error(
  380. # optimized_rvecs, optimized_tvecs, points_3d, points_2d, K_matrices, dist_coeffs
  381. # )
  382. #print(f"优化前的重投影误差: {original_error}")
  383. #print(f"优化后的重投影误差: {optimized_error}")
  384. return result
  385. def visualize_projection(image, points_2d, color='r', title='Projected Points'):
  386. """
  387. 在图像上绘制投影点。
  388. Parameters:
  389. image: numpy.ndarray
  390. 图像数据。
  391. points_2d: numpy.ndarray
  392. 要绘制的 2D 点。
  393. color: str
  394. 绘制点的颜色。
  395. title: str
  396. 图像标题。
  397. """
  398. plt.figure(figsize=(8, 6))
  399. plt.imshow(image, cmap='gray')
  400. plt.scatter(points_2d[:, 0], points_2d[:, 1], c=color, s=50, label='Projected Points')
  401. plt.legend()
  402. plt.title(title)
  403. plt.show()
  404. def compute_reprojection_error(projected_points, observed_points):
  405. """
  406. 计算投影点与实际观测点之间的误差。
  407. Parameters:
  408. projected_points: numpy.ndarray
  409. 投影得到的2D点,形状为 (N, 2)。
  410. observed_points: numpy.ndarray
  411. 实际观测到的2D点,形状为 (N, 2)。
  412. Returns:
  413. float
  414. 平均重投影误差。
  415. """
  416. error = np.linalg.norm(projected_points - observed_points, axis=1)
  417. return np.mean(error)
  418. # 将 3D 点转换到相机坐标系
  419. def transform_points_to_camera_frame(points, R, T):
  420. return (R @ points.T + T).T
  421. def detect_april_tag_points(detector, img_file, object_point_single):
  422. """
  423. 检测单张图像中的 AprilTags,并返回图像中的 2D 角点和相应的 3D 物体点。
  424. Parameters:
  425. detector: AprilTag 检测器对象
  426. img_file: 图像文件路径
  427. object_point_single: 标定板的 3D 物体点
  428. Returns:
  429. all_corners: np.ndarray
  430. 图像中的 2D 角点 (N, 2)
  431. all_object_points: np.ndarray
  432. 对应的 3D 物体点 (N, 3)
  433. """
  434. #print('Processing image file:', img_file)
  435. # 读取并转换为灰度图像
  436. src = cv2.imread(img_file)
  437. gray = cv2.imread(img_file, 0)
  438. if gray is None:
  439. print(f"Failed to load image: {img_file}")
  440. return np.array([]), np.array([])
  441. # 检测 AprilTags
  442. detections = detector.detect(gray)
  443. all_corners = []
  444. all_object_points = []
  445. tag_id_lst = []
  446. #show_detect_result(src, detections)
  447. if detections:
  448. for detection in detections:
  449. corners = detection.corners # 检测到的角点 (4, 2)
  450. tag_id = detection.tag_id # 标签 ID
  451. tag_id_lst.append(int(tag_id))
  452. # 将检测到的角点存储为 np.float32 格式
  453. all_corners.extend(corners.astype(np.float32))
  454. # 根据标签 ID 提取相应的 3D 坐标
  455. if tag_id < len(object_point_single):
  456. all_object_points.extend(object_point_single[tag_id])
  457. else:
  458. print(f"Warning: Tag ID {tag_id} is out of range.")
  459. if all_object_points and all_corners:
  460. return np.array(all_corners, dtype=np.float32).reshape(-1, 2), \
  461. np.array(all_object_points, dtype=np.float32).reshape(-1, 3), \
  462. tag_id_lst, detections
  463. else:
  464. print("No valid detections in this image.")
  465. return np.array([]), np.array([]), [], []
  466. def single_camera_calibrate_vis():
  467. config_path = 'D:/code/pmdrecons-python-xtt/config/cfg_3freq_wafer.json'
  468. para_path = 'D:\\code\\pmdrecons-python-xtt\\config\\world_params.mat'
  469. world_params = loadmat(para_path)
  470. camera_matrix = np.array(world_params['camera_matrix'])
  471. dist_coeffs = np.array(world_params['distortion_coefficients'])
  472. img_dir = 'D:\\data\\one_cam\\pmd_benchmarks\\set4-20240906\\set4-20240906\\calibration\\'
  473. img_paths = glob.glob(os.path.join(img_dir, 'world_calibration/*.bmp'))
  474. debug = 1
  475. cfg = json.load(open(config_path, 'r'))
  476. chessboard_size = cfg['world_chessboard_size']
  477. square_size = cfg['world_square_size']
  478. # 准备棋盘格的世界坐标
  479. objp = np.zeros((chessboard_size[0]*chessboard_size[1], 3), np.float32)
  480. objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
  481. objp *= square_size
  482. # 存储每个棋盘格在相机坐标系中的位置
  483. chessboard_positions = []
  484. for fname in img_paths:
  485. print(fname)
  486. img = cv2.imread(fname)
  487. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  488. # 寻找棋盘格角点
  489. ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
  490. if ret:
  491. # 提高角点精度
  492. criteria = (cv2.TermCriteria_EPS + cv2.TermCriteria_MAX_ITER, 30, 0.001)
  493. corners_subpix = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
  494. # 计算棋盘格的姿态(相对于相机)
  495. success, rvec, tvec = cv2.solvePnP(objp, corners_subpix, camera_matrix, dist_coeffs)
  496. if success:
  497. # 计算旋转矩阵
  498. R, _ = cv2.Rodrigues(rvec)
  499. # 将棋盘格角点转换到相机坐标系
  500. # objp_in_camera = R * objp^T + tvec
  501. objp_in_camera = (R @ objp.T + tvec).T # 转置以获得 N x 3 的矩阵
  502. # 存储转换后的棋盘格角点
  503. chessboard_positions.append(objp_in_camera)
  504. else:
  505. print("姿态估计失败:", fname)
  506. else:
  507. print("无法找到棋盘格角点:", fname)
  508. cv2.destroyAllWindows()
  509. # 绘制多个棋盘格在相机坐标系中的位置
  510. if len(chessboard_positions) > 0:
  511. # 创建3D绘图
  512. fig = plt.figure()
  513. ax = fig.add_subplot(111, projection='3d')
  514. # 绘制每个棋盘格
  515. for idx, chessboard in enumerate(chessboard_positions):
  516. x = chessboard[:, 0]
  517. y = chessboard[:, 1]
  518. z = chessboard[:, 2]
  519. ax.scatter(x, y, z, label=f'Chessboard {idx+1}')
  520. plt.pause(3)
  521. # 绘制相机位置(原点)
  522. ax.scatter(0, 0, 0, c='r', marker='^', s=100, label='Camera Position')
  523. # 设置标签和标题
  524. ax.set_xlabel('X axis')
  525. ax.set_ylabel('Y axis')
  526. ax.set_zlabel('Z axis')
  527. ax.set_title('Multiple Chessboard Positions Relative to Fixed Camera')
  528. # 设置等比例显示
  529. all_x = np.hstack([chessboard[:, 0] for chessboard in chessboard_positions])
  530. all_y = np.hstack([chessboard[:, 1] for chessboard in chessboard_positions])
  531. all_z = np.hstack([chessboard[:, 2] for chessboard in chessboard_positions])
  532. ax.set_box_aspect([np.ptp(a) for a in [all_x, all_y, all_z]])
  533. # 显示图例
  534. ax.legend()
  535. plt.show()
  536. else:
  537. print("未能计算出有效的棋盘格位置。")
  538. return
  539. def draw(img, corner, imgpts):
  540. corner = tuple(corner.ravel().astype(int))
  541. imgpts = imgpts.astype(int)
  542. # 绘制坐标轴
  543. img = cv2.line(img, corner, tuple(imgpts[1].ravel()), (0, 0, 255), 5) # X轴(红色)
  544. img = cv2.line(img, corner, tuple(imgpts[2].ravel()), (0, 255, 0), 5) # Y轴(绿色)
  545. img = cv2.line(img, corner, tuple(imgpts[3].ravel()), (255, 0, 0), 5) # Z轴(蓝色)
  546. return img
  547. def verify_coplanarity(points_3d):
  548. # 使用SVD拟合平面
  549. centroid = np.mean(points_3d, axis=0)
  550. centered_points = points_3d - centroid
  551. U, S, Vt = np.linalg.svd(centered_points)
  552. normal_vector = Vt[-1, :]
  553. # 计算每个点到平面的距离
  554. distances = np.abs(centered_points @ normal_vector)
  555. mean_distance = np.mean(distances)
  556. max_distance = np.max(distances)
  557. print(f"Mean distance to plane: {mean_distance}")
  558. print(f"Max distance to plane: {max_distance}")
  559. # 设置一个阈值判断是否共面
  560. threshold = 1e-3 # 根据需求调整
  561. if max_distance < threshold:
  562. print("All points are approximately coplanar.")
  563. else:
  564. print("Points are not coplanar.")
  565. def fit_plane(points):
  566. """拟合一个平面并返回法向量和质心."""
  567. centroid = np.mean(points, axis=0) # 计算质心
  568. centered_points = points - centroid # 将点移到质心
  569. # 使用 SVD 分解拟合平面法向量
  570. _, _, vh = np.linalg.svd(centered_points)
  571. normal_vector = vh[-1] # 法向量是 SVD 的最后一列
  572. return normal_vector, centroid
  573. def rotation_matrix_from_vectors(v1, v2):
  574. """计算将向量 v1 旋转到 v2 的旋转矩阵."""
  575. v1 = v1 / np.linalg.norm(v1)
  576. v2 = v2 / np.linalg.norm(v2)
  577. axis = np.cross(v1, v2) # 旋转轴
  578. angle = np.arccos(np.dot(v1, v2)) # 旋转角度
  579. # 构建旋转矩阵(Rodrigues 公式)
  580. K = np.array([
  581. [0, -axis[2], axis[1]],
  582. [axis[2], 0, -axis[0]],
  583. [-axis[1], axis[0], 0]
  584. ])
  585. rotation_matrix = np.eye(3) + np.sin(angle) * K + (1 - np.cos(angle)) * (K @ K)
  586. return rotation_matrix
  587. def align_points_to_horizontal(points):
  588. """将给定的 3D 点集旋转到水平面."""
  589. normal_vector, centroid = fit_plane(points) # 拟合平面
  590. print('normal_vector = ', normal_vector)
  591. rotation_to_horizontal = rotation_matrix_from_vectors(normal_vector, np.array([0, 0, 1]))
  592. #print('rotation_to_horizontal = ', rotation_to_horizontal)
  593. # 将所有点旋转到水平面,并返回结果
  594. centered_points = points - centroid # 平移到质心
  595. rotated_points = (rotation_to_horizontal @ centered_points.T).T + centroid # 旋转并平移回去
  596. return rotated_points, rotation_to_horizontal
  597. def camera_calibrate_vis():
  598. # 加载配置和相机参数
  599. config_path = 'D:\\code\\pmd-python\\config\\cfg_3freq_wafer.json'
  600. detector = Detector('t36h11')
  601. cfg = json.load(open(config_path, 'r'))
  602. with open(os.path.join('D:\\code\\pmd-python\\config\\', 'cam_params1023.pkl'), 'rb') as pkl_file:
  603. cam_params = pickle.load(pkl_file)
  604. # 生成 3D 物体点
  605. world_chessboard_size, world_square_size, world_square_spacing = [10, 6], 35.2, 10.56
  606. object_point_single = generate_3d_points(world_chessboard_size, world_square_size, world_square_spacing)
  607. # 保存所有相机的观测数据
  608. all_points_3d_world = []
  609. all_image_points = []
  610. all_camera_centers = []
  611. K_matrices = []
  612. dist_coeffs = []
  613. all_corners = []
  614. all_object_points = []
  615. optimized_cam_params = []
  616. all_tag_lst = []
  617. all_detections = []
  618. # 定义四个相机的标定图像路径
  619. calibration_images_path = [
  620. 'D:\\data\\four_cam\\calibrate\\single_test\\cam0\\',
  621. 'D:\\data\\four_cam\\calibrate\\single_test\\cam1\\',
  622. 'D:\\data\\four_cam\\calibrate\\single_test\\cam2\\',
  623. 'D:\\data\\four_cam\\calibrate\\single_test\\cam3\\'
  624. ]
  625. # 遍历每个相机
  626. for cam_id, params in enumerate(cam_params):
  627. K_matrices.append(params['camera_matrix'])
  628. dist_coeffs.append(params['distortion_coefficients'])
  629. camera_corners = [] # 用于当前相机的2D图像点
  630. camera_object_points = [] # 用于当前相机的3D物体点
  631. # 对每个相机的标定图像提取2D角点
  632. for img_file in os.listdir(calibration_images_path[cam_id]):
  633. # 读取图像并检测AprilTags,获取2D角点和对应的3D物体点
  634. image_points, object_points, tag_id_lst, detections = detect_april_tag_points(
  635. detector, calibration_images_path[cam_id] + img_file, object_point_single
  636. )
  637. # print('image_points = ', image_points)
  638. # print('object_points =', object_points)
  639. print('tag_id_lst = ', tag_id_lst)
  640. all_tag_lst.append(tag_id_lst)
  641. all_corners.append(image_points)
  642. all_object_points.append(object_points)
  643. all_detections.append(detections)
  644. undistorted_points = cv2.undistortPoints(image_points, params['camera_matrix'], params['distortion_coefficients'])
  645. success, rotation_vector, translation_vector = cv2.solvePnP(object_points, image_points, params['camera_matrix'],
  646. params['distortion_coefficients'],
  647. params['rotation_matrix'],
  648. params['translation_vector']
  649. )
  650. rotation_matrix, _ = cv2.Rodrigues(rotation_vector)
  651. # 假设的深度值 (s)
  652. # 你需要根据场景给出一个合理的深度值
  653. # 相机的中心
  654. camera_center = -rotation_matrix.T @ translation_vector
  655. #print('camera_center = ', camera_center)
  656. depth = camera_center[-1]
  657. print('undistorted_points shape = ', undistorted_points.shape)
  658. print('image_points shape = ', image_points.shape)
  659. print('depth = ', depth)
  660. # 将 2D 图像点转换为齐次坐标
  661. #print(image_points.shape)
  662. image_points_homogeneous = np.hstack([image_points, np.ones((image_points.shape[0], 1), dtype=np.float32)])
  663. # 批量计算相机坐标系中的 3D 点 (N x 3 矩阵)
  664. camera_coordinates = (np.linalg.inv(params['camera_matrix']) @ image_points_homogeneous.T).T * depth
  665. # 将相机坐标转换为世界坐标 (N x 3 矩阵)
  666. world_coordinates = (np.linalg.inv(rotation_matrix) @ (camera_coordinates - translation_vector.T).T).T
  667. all_points_3d_world.append(world_coordinates)
  668. print('len 3d = ', len(all_points_3d_world))
  669. # adjusted_cam_params = []
  670. # for i, params in enumerate(cam_params):
  671. # world_coordinates = all_points_3d_world[i] # 获取该相机的 3D 点
  672. # #print('world_coordinates = ', world_coordinates)
  673. # # 将点集旋转到水平面,并获取旋转矩阵
  674. # #aligned_points, rotation_to_horizontal = align_points_to_horizontal(world_coordinates)
  675. # aligned_points, rotation_to_horizontal = align2ref(world_coordinates)
  676. # print('aligned_points = ', rotation_matrix)
  677. # print('rotation_to_horizontal = ', rotation_to_horizontal)
  678. # # 更新相机的外参(旋转矩阵)
  679. # params['rotation_matrix'] = rotation_to_horizontal @ params['rotation_matrix']
  680. # adjusted_cam_params.append(params)
  681. # # 更新校正后的 3D 点
  682. # all_points_3d_world[i] = aligned_points
  683. # fig = plt.figure()
  684. # ax = fig.add_subplot(111, projection='3d')
  685. # for i, points in enumerate(all_points_3d_world):
  686. # ax.scatter(points[:, 0], points[:, 1], points[:, 2], label=f'Cam {i}')
  687. # ax.set_xlabel('X')
  688. # ax.set_ylabel('Y')
  689. # ax.set_zlabel('Z')
  690. # plt.legend()
  691. # plt.show()
  692. # # 3. 保存调整后的相机参数,以备后续使用
  693. # with open(os.path.join('D:\\code\\pmd-python\\config\\', 'cam_params1023.pkl'), 'wb') as f:
  694. # pickle.dump(adjusted_cam_params, f)
  695. fig = plt.figure()
  696. ax = fig.add_subplot(111, projection='3d')
  697. colors = ['r', 'g', 'b', 'c'] # 颜色列表
  698. for i in range(len(all_points_3d_world)):
  699. plot_3d_points(all_points_3d_world[i], label=f'Cam {i}', color=[random.random() for _ in range(3)], ax=ax)
  700. # 显示图例和更新当前图像
  701. ax.legend(loc='upper right')
  702. plt.pause(2) # 暂停 1 秒,逐个显示效果
  703. # for cam_idx, (params, corners) in enumerate(zip(cam_params, all_corners)):
  704. # #print('all_points_3d_world[cam_idx] = ', all_points_3d_world[cam_idx])
  705. # plot_3d_points(all_points_3d_world[cam_idx], label=f'Cam {cam_idx}', color=colors[cam_idx], ax=ax)
  706. ax.set_xlabel('X')
  707. ax.set_ylabel('Y')
  708. ax.set_zlabel('Z')
  709. # ax.set_zlim([0, 500])
  710. plt.legend()
  711. plt.show()
  712. #cam_params[3]['rotation_matrix'] = np.eye(3)
  713. #cam_params[3]['translation_vector'] = np.zeros((3, 1))
  714. # stereo_calibration_data_0_3 = stereo_calibrate(all_detections, 0, 3, object_point_single, cam_params)
  715. # stereo_calibration_data_1_3 = stereo_calibrate(all_detections, 1, 3, object_point_single, cam_params)
  716. # stereo_calibration_data_2_3 = stereo_calibrate(all_detections, 2, 3, object_point_single, cam_params)
  717. # stereo_calib_data_list = []
  718. # stereo_calib_data_list.append(stereo_calibration_data_0_3)
  719. # stereo_calib_data_list.append(stereo_calibration_data_1_3)
  720. # stereo_calib_data_list.append(stereo_calibration_data_2_3)
  721. # # 设置相机 3 的外参不变
  722. # R_3 = cam_params[3]['rotation_matrix']
  723. # T_3 = cam_params[3]['translation_vector']
  724. # for i in [0, 1, 2]:
  725. # # 获取相机 i 的初始外参
  726. # R_i = cam_params[i]['rotation_matrix']
  727. # T_i = cam_params[i]['translation_vector']
  728. # # 计算初始相对位姿
  729. # R_i3_init = R_3 @ R_i.T
  730. # T_i3_init = -R_3 @ R_i.T @ T_i + T_3
  731. # # 获取双目标定结果
  732. # stereo_calib_data = stereo_calib_data_list[i]
  733. # R_i3_stereo = stereo_calib_data['rotation_matrix']
  734. # T_i3_stereo = stereo_calib_data['translation_vector']
  735. # # 计算位姿差异
  736. # delta_R = R_i3_stereo @ R_i3_init.T
  737. # delta_T = T_i3_stereo - delta_R @ T_i3_init
  738. # # 更新相机外参
  739. # R_i_new = delta_R @ R_i
  740. # T_i_new = delta_R @ T_i + delta_T
  741. # # 更新 cam_params
  742. # cam_params[i]['rotation_matrix'] = R_i_new
  743. # cam_params[i]['translation_vector'] = T_i_new
  744. # with open('D:\\code\\pmd-python\\config\\stereo_calib2.pkl', 'wb') as pkl_file:
  745. # pickle.dump(cam_params, pkl_file)
  746. def stereo_calibrate(all_detections, cam_id1, cam_id2, object_point_single, cam_params):
  747. detections1 = all_detections[cam_id1]
  748. detections2 = all_detections[cam_id2]
  749. detections_dict1 = {d.tag_id: d for d in detections1}
  750. detections_dict2 = {d.tag_id: d for d in detections2}
  751. # 用于保存所有图像的检测到的角点和 ID
  752. objpoints = [] # 3D 点
  753. imgpoints1 = [] # 相机1的 2D 点
  754. imgpoints2 = [] # 相机2的 2D 点
  755. image_size = None
  756. # 找到两个相机共同检测到的标签 ID
  757. common_tags = set(detections_dict1.keys()) & set(detections_dict2.keys())
  758. print('common_tags = ', common_tags)
  759. if common_tags:
  760. image_points1 = []
  761. image_points2 = []
  762. object_points = []
  763. for tag_id in common_tags:
  764. # 获取相机1的角点
  765. corners1 = detections_dict1[tag_id].corners.astype(np.float32)
  766. # 获取相机2的角点
  767. corners2 = detections_dict2[tag_id].corners.astype(np.float32)
  768. # 获取对应的 3D 点
  769. if tag_id < len(object_point_single):
  770. obj_pts = object_point_single[tag_id]
  771. else:
  772. print(f"Warning: Tag ID {tag_id} is out of range.")
  773. continue
  774. object_points.append(obj_pts)
  775. image_points1.append(corners1)
  776. image_points2.append(corners2)
  777. if object_points and image_points1 and image_points2:
  778. objpoints.append(np.array(object_points, dtype=np.float32).reshape(-1, 3))
  779. imgpoints1.append(np.array(image_points1, dtype=np.float32).reshape(-1, 2))
  780. imgpoints2.append(np.array(image_points2, dtype=np.float32).reshape(-1, 2))
  781. else:
  782. print("No valid common detections in this image pair.")
  783. else:
  784. print("No common tags detected in this image pair.")
  785. K1 = cam_params[cam_id1]['camera_matrix']
  786. dist1 = cam_params[cam_id1]['distortion_coefficients']
  787. K2 = cam_params[cam_id2]['camera_matrix']
  788. dist2 = cam_params[cam_id2]['distortion_coefficients']
  789. # 立体标定
  790. flags = 0
  791. flags |= cv2.CALIB_FIX_INTRINSIC # 固定单目内参
  792. criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 100, 1e-5)
  793. ret, _, _, _, _, R, T, E, F = cv2.stereoCalibrate(
  794. objpoints, # 3D 点
  795. imgpoints1, # 相机1的 2D 点
  796. imgpoints2, # 相机2的 2D 点
  797. K1, # 相机1的内参
  798. dist1, # 相机1的畸变系数
  799. K2, # 相机2的内参
  800. dist2, # 相机2的畸变系数
  801. image_size, # 图像尺寸
  802. criteria=criteria,
  803. flags=flags
  804. )
  805. print(f"Stereo Calibration between Camera {cam_id1} and Camera {cam_id2} completed.")
  806. print(f"Rotation Matrix R:\n{R}")
  807. print(f"Translation Vector T:\n{T}")
  808. # 将结果保存到字典中
  809. stereo_calibration_data = {
  810. 'camera_matrix1': K1,
  811. 'distortion_coefficients1': dist1,
  812. 'camera_matrix2': K2,
  813. 'distortion_coefficients2': dist2,
  814. 'rotation_matrix': R,
  815. 'translation_vector': T,
  816. 'essential_matrix': E,
  817. 'fundamental_matrix': F,
  818. 'reprojection_error': ret
  819. }
  820. # 返回标定结果
  821. print('stereo_calibration_data = ', stereo_calibration_data)
  822. return stereo_calibration_data
  823. # ######################## bundle_adjustment
  824. # # 遍历每个相机
  825. # for cam_id, params in enumerate(cam_params):
  826. # K_matrices.append(params['camera_matrix'])
  827. # dist_coeffs.append(params['distortion_coefficients'])
  828. # camera_corners = [] # 用于当前相机的2D图像点
  829. # camera_object_points = [] # 用于当前相机的3D物体点
  830. # # 对每个相机的标定图像提取2D角点
  831. # for img_file in os.listdir(calibration_images_path[cam_id]):
  832. # # 读取图像并检测AprilTags,获取2D角点和对应的3D物体点
  833. # image_points, object_points, tag_lst = detect_april_tag_points(
  834. # detector, calibration_images_path[cam_id] + img_file, object_point_single
  835. # )
  836. # # 将检测到的2D图像点和3D物体点保存到当前相机的列表
  837. # if image_points is not None and object_points is not None:
  838. # camera_corners.append(image_points)
  839. # camera_object_points.append(object_points)
  840. # # 保存当前相机的所有图像点到全局列表
  841. # all_corners.append(camera_corners)
  842. # all_object_points.append(camera_object_points)
  843. # # 调用捆绑调整函数,优化所有相机的内参、外参和3D点
  844. # plane_constraints = [
  845. # [0, 1, 2, 3], # 第一个平面的点索引
  846. # [4, 5, 6, 7] # 第二个平面的点索引
  847. # ]
  848. # result = bundle_adjustment(all_object_points, all_corners, cam_params, plane_constraints)
  849. # print(result)
  850. # optimized_rvecs, optimized_tvecs = extract_params_from_result(result, 4, len(all_object_points))
  851. # original_rvecs = [cv2.Rodrigues(params['rotation_matrix'])[0] for params in cam_params]
  852. # original_tvecs = [params['translation_vector'] for params in cam_params]
  853. # print('optimized_rvecs = ',optimized_rvecs)
  854. # for i in range(4):
  855. # optimized_rotation_matrices = cv2.Rodrigues(optimized_rvecs[i])[0]
  856. # #print(original_tvecs[i], optimized_tvecs[i].reshape(-1, 1))
  857. # calibration_data = {
  858. # 'camera_matrix': cam_params[i]['camera_matrix'],
  859. # 'distortion_coefficients': cam_params[i]['distortion_coefficients'],
  860. # 'rotation_matrix': optimized_rotation_matrices,
  861. # 'translation_vector': optimized_tvecs[i].reshape(-1, 1)
  862. # }
  863. # optimized_cam_params.append(calibration_data)
  864. # with open('D:\\code\\pmd-python\\config\\optimized_param1021.pkl', 'wb') as pkl_file:
  865. # pickle.dump(optimized_cam_params, pkl_file)
  866. # # 打印优化后的结果
  867. # print("Bundle Adjustment 完成!")
  868. # #绘制相机
  869. # fig = plt.figure()
  870. # ax = fig.add_subplot(111, projection='3d')
  871. # # 绘制每个相机
  872. # for ii in range(4):
  873. # plot_camera(ax, cam_params[ii]['rotation_matrix'], cam_params[ii]['translation_vector'], ii, scale=40, color='r')
  874. # # 绘制物体 (假设是一个简单的平面物体,位于 z = 0 平面上)
  875. # plane_x, plane_y = np.meshgrid(np.linspace(-500, 500, 10), np.linspace(-500, 500, 10))
  876. # plane_z = np.zeros_like(plane_x) # z = 0 的平面
  877. # ax.plot_surface(plane_x, plane_y, plane_z, color='blue', alpha=0.3)
  878. # # 设置轴的范围和标签
  879. # ax.set_xlabel('X axis')
  880. # ax.set_ylabel('Y axis')
  881. # ax.set_zlabel('Z axis')
  882. # ax.set_xlim([-50, 300])
  883. # ax.set_ylim([-50, 300])
  884. # ax.set_zlim([0, 500])
  885. # plt.show()
  886. def plt_gaussion():
  887. # 1. Create a Gaussian surface
  888. X = np.linspace(-3, 3, 100)
  889. Y = np.linspace(-3, 3, 100)
  890. X, Y = np.meshgrid(X, Y)
  891. Z = np.exp(-(X**2 + Y**2))
  892. # 2. Compute gradients
  893. Zx, Zy = np.gradient(Z)
  894. # 3. Plotting the Gaussian surface
  895. fig = plt.figure(figsize=(14, 6))
  896. # Plot 1: Gaussian Surface
  897. ax1 = fig.add_subplot(121, projection='3d')
  898. ax1.plot_surface(X, Y, Z, cmap='viridis', edgecolor='none')
  899. ax1.set_title('Gaussian Surface')
  900. ax1.set_xlabel('X')
  901. ax1.set_ylabel('Y')
  902. ax1.set_zlabel('Z')
  903. # Plot 2: Gradient Field Visualization
  904. ax2 = fig.add_subplot(122)
  905. ax2.quiver(X, Y, Zx, Zy, color='r')
  906. ax2.set_title('Gradient Field of Gaussian Surface')
  907. ax2.set_xlabel('X')
  908. ax2.set_ylabel('Y')
  909. plt.tight_layout()
  910. plt.show()
  911. def test_aprilgrid():
  912. dll_path = "C:\\Users\\zhang\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages\\pupil_apriltags\\lib\\apriltag.dll"
  913. if not os.path.exists(dll_path):
  914. raise FileNotFoundError(f"{dll_path} 不存在")
  915. ctypes.WinDLL(dll_path, winmode=0)
  916. detector = apriltag.Detector(
  917. families="tag36h11", # 确保与标签类型一致
  918. nthreads=4, # 多线程加速
  919. quad_decimate=0.5, # 降采样因子,调小提高检测精度
  920. quad_sigma=0.0, # 高斯模糊,处理噪声图像
  921. refine_edges=True # 边缘细化
  922. )
  923. #detector = Detector('t36h11')
  924. #os.add_dll_directory(dll_path)
  925. #img_file = 'D:\\data\\one_cam\\calibration-1029\\screen\\Image_20241029170810995.bmp'
  926. #img_file = 'C:\\Users\\zhang\\Desktop\\Image_20241029190420495.bmp'
  927. img_dir = 'D:\\data\\1030\\'
  928. for img_name in os.listdir(img_dir):
  929. img_file = os.path.join(img_dir, img_name)
  930. # 从文件读取并解码图像
  931. image_data = np.fromfile(img_file, dtype=np.uint8)
  932. img = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
  933. image_size = None
  934. if image_size is None:
  935. image_size = (img.shape[1], img.shape[0]) # 保存图像尺寸
  936. # 转换为灰度图像
  937. img = cv2.flip(img, 0)
  938. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  939. # 检测 AprilTags
  940. detections = detector.detect(gray)
  941. #print(detections)
  942. # for detection in detections:
  943. # corners = detection.corners
  944. # tag_id = detection.tag_id
  945. # if tag_id == 0 or tag_id == 1:
  946. # print(tag_id, corners)
  947. if 1:
  948. # from calibration.get_camera_params import show_detect_result
  949. show_detect_result_new(img, detections)
  950. #show_detect_result(img, detections)
  951. def stack_apriltag(rows, cols):
  952. # 设置图片路径和目标大图的输出路径
  953. input_folder = 'C:\\Users\\zhang\\Desktop\\apriltag' # 替换为你的图片文件夹路径
  954. output_image_path = 'D:\\data\\0output_image.png'
  955. output_image_path1 = 'D:\\data\\0output_image_' + str(rows) + 'x' + str(cols) + '.png'
  956. # 获取并按顺序生成每张图片的路径
  957. images = [
  958. os.path.join(input_folder, f'tag36h11_{i}.png')
  959. for i in range(rows * cols)
  960. ]
  961. # 检查是否找到足够数量的图片
  962. missing_files = [img for img in images if not os.path.exists(img)]
  963. if missing_files:
  964. raise FileNotFoundError(f"以下图片未找到: {missing_files}")
  965. # 打开第一张图片来确定每张小图的大小
  966. with Image.open(images[0]) as img:
  967. width, height = img.size
  968. # 创建大图,大小为 (cols * width) x (rows * height)
  969. output_image = Image.new('RGB', (cols * width, rows * height))
  970. # 拼接图片到大图中
  971. for index, image_path in enumerate(images):
  972. with Image.open(image_path) as img:
  973. row = index // cols
  974. col = index % cols
  975. output_image.paste(img, (col * width, row * height))
  976. # 保存拼接好的大图
  977. output_image.save(output_image_path)
  978. print(f"拼接完成!大图已保存为 {output_image_path}")
  979. # 计算等比例缩放后的尺寸,使其尽可能适应 1920x1080,但不改变比例
  980. aspect_ratio = output_image.width / output_image.height
  981. target_width, target_height = 1920, 1080
  982. if aspect_ratio > (target_width / target_height):
  983. # 图像更宽,按照宽度进行缩放
  984. new_width = target_width
  985. new_height = int(target_width / aspect_ratio)
  986. else:
  987. # 图像更高,按照高度进行缩放
  988. new_height = target_height
  989. new_width = int(target_height * aspect_ratio)
  990. resized_image = output_image.resize((new_width, new_height), Image.LANCZOS)
  991. # 创建 1920x1080 的黑色背景图
  992. final_image = Image.new('RGB', (target_width, target_height), (0, 0, 0))
  993. # 计算粘贴位置,使图像居中
  994. paste_x = (target_width - new_width) // 2
  995. paste_y = (target_height - new_height) // 2
  996. final_image.paste(resized_image, (paste_x, paste_y))
  997. # 保存最终的大图
  998. final_image.save(output_image_path1)
  999. print(f"缩放并保存完成!小图已保存为 {output_image_path1}")
  1000. def stack_apriltag2(rows, cols):
  1001. # 设置图片路径和目标大图的输出路径
  1002. input_folder = 'C:\\Users\\zhang\\Desktop\\apriltag'
  1003. output_image_path = 'D:\\data\\2output_image_pad.png'
  1004. output_image_path1 = 'D:\\data\\2output_image_pad_' + str(rows) + 'x' + str(cols) + '.bmp'
  1005. #target_width, target_height = 1920, 1080
  1006. target_width, target_height = 2360, 1640
  1007. # 获取并按顺序生成每张图片的路径
  1008. images = [
  1009. os.path.join(input_folder, f'tag36h11_{i}.png')
  1010. for i in range(rows * cols)
  1011. ]
  1012. # 检查是否找到足够数量的图片
  1013. missing_files = [img for img in images if not os.path.exists(img)]
  1014. if missing_files:
  1015. raise FileNotFoundError(f"以下图片未找到: {missing_files}")
  1016. # 打开第一张图片来确定每张小图的大小
  1017. first_image = cv2.imread(images[0])
  1018. height, width, _ = first_image.shape
  1019. # 创建大图,大小为 (cols * width) x (rows * height)
  1020. output_image = np.zeros((rows * height, cols * width, 3), dtype=np.uint8)
  1021. # 拼接图片到大图中
  1022. for index, image_path in enumerate(images):
  1023. img = cv2.imread(image_path)
  1024. img = cv2.flip(img, 0) # 垂直翻转图片
  1025. row = index // cols
  1026. col = index % cols
  1027. output_image[row*height:(row+1)*height, col*width:(col+1)*width] = img
  1028. # 保存拼接好的大图
  1029. cv2.imwrite(output_image_path, output_image)
  1030. print(f"拼接完成!大图已保存为 {output_image_path}")
  1031. # 计算等比例缩放后的尺寸,使其尽可能适应 1920x1080,但不改变比例
  1032. aspect_ratio = output_image.shape[1] / output_image.shape[0]
  1033. if aspect_ratio > (target_width / target_height):
  1034. # 图像更宽,按照宽度进行缩放
  1035. new_width = target_width
  1036. new_height = int(target_width / aspect_ratio)
  1037. else:
  1038. # 图像更高,按照高度进行缩放
  1039. new_height = target_height
  1040. new_width = int(target_height * aspect_ratio)
  1041. resized_image = cv2.resize(output_image, (new_width, new_height), interpolation=cv2.INTER_LANCZOS4)
  1042. # 创建 1920x1080 的黑色背景图
  1043. final_image = np.zeros((target_height, target_width, 3), dtype=np.uint8) # 注意这里大小的变化
  1044. # 计算粘贴位置,使图像居中
  1045. paste_x = (target_width - new_width) // 2
  1046. paste_y = (target_height - new_height) // 2
  1047. final_image[paste_y:paste_y+new_height, paste_x:paste_x+new_width] = resized_image
  1048. # 保存最终的大图
  1049. cv2.imwrite(output_image_path1, final_image)
  1050. print(f"缩放并保存完成!小图已保存为 {output_image_path1}")
  1051. def stack_apriltag1(rows, cols):
  1052. # 设置图片路径和目标大图的输出路径
  1053. input_folder = 'C:\\Users\\zhang\\Desktop\\apriltag' # 替换为你的图片文件夹路径
  1054. output_image_path = 'D:\\data\\output_image.png'
  1055. output_image_path1 = 'D:\\data\\output_image_' + str(rows) + 'x' + str(cols) + '.bmp'
  1056. # 获取并按顺序生成每张图片的路径
  1057. images = [
  1058. os.path.join(input_folder, f'tag36h11_{i}.png')
  1059. for i in range(rows * cols)
  1060. ]
  1061. # 检查是否找到足够数量的图片
  1062. missing_files = [img for img in images if not os.path.exists(img)]
  1063. if missing_files:
  1064. raise FileNotFoundError(f"以下图片未找到: {missing_files}")
  1065. # 打开第一张图片来确定每张小图的大小
  1066. first_image = cv2.imread(images[0])
  1067. height, width, _ = first_image.shape
  1068. # 创建大图,大小为 (cols * width) x (rows * height)
  1069. output_image = np.zeros((rows * height, cols * width, 3), dtype=np.uint8)
  1070. # 拼接图片到大图中
  1071. for index, image_path in enumerate(images):
  1072. img = cv2.imread(image_path)
  1073. img = cv2.flip(img, 1) # 水平翻转图片
  1074. row = index // cols
  1075. col = index % cols
  1076. output_image[(rows-row-1)*height:(rows-row)*height, (cols-col-1)*width:(cols-col)*width] = img
  1077. # 保存拼接好的大图
  1078. cv2.imwrite(output_image_path, output_image)
  1079. print(f"拼接完成!大图已保存为 {output_image_path}")
  1080. # 计算等比例缩放后的尺寸,使其尽可能适应 1920x1080,但不改变比例
  1081. target_width, target_height = 1920, 1080
  1082. aspect_ratio = output_image.shape[1] / output_image.shape[0]
  1083. if aspect_ratio > (target_width / target_height):
  1084. # 图像更宽,按照宽度进行缩放
  1085. new_width = target_width
  1086. new_height = int(target_width / aspect_ratio)
  1087. else:
  1088. # 图像更高,按照高度进行缩放
  1089. new_height = target_height
  1090. new_width = int(target_height * aspect_ratio)
  1091. resized_image = cv2.resize(output_image, (new_width, new_height), interpolation=cv2.INTER_LANCZOS4)
  1092. # 创建 1920x1080 的黑色背景图
  1093. final_image = np.zeros((target_height, target_width, 3), dtype=np.uint8) # 注意这里大小的变化
  1094. # 计算粘贴位置,使图像居中
  1095. paste_x = (target_width - new_width) // 2
  1096. paste_y = (target_height - new_height) // 2
  1097. final_image[paste_y:paste_y+new_height, paste_x:paste_x+new_width] = resized_image
  1098. # 保存最终的大图
  1099. cv2.imwrite(output_image_path1, final_image)
  1100. print(f"缩放并保存完成!小图已保存为 {output_image_path1}")
  1101. def cal_pad_pixel():
  1102. # Given values
  1103. diagonal_inch = 10.86 # diagonal length in inches
  1104. width_pixels = 2360
  1105. height_pixels = 1640
  1106. # Calculate the aspect ratio constant k using the Pythagorean theorem
  1107. k = diagonal_inch / math.sqrt(width_pixels**2 + height_pixels**2)
  1108. # Calculate width and height in inches
  1109. width_inch = k * width_pixels
  1110. height_inch = k * height_pixels
  1111. # Convert the width and height to millimeters (1 inch = 25.4 mm)
  1112. width_mm = width_inch * 25.4
  1113. height_mm = height_inch * 25.4
  1114. # Calculate the size of each pixel in millimeters
  1115. pixel_width_mm = width_mm / width_pixels
  1116. pixel_height_mm = height_mm / height_pixels
  1117. print(pixel_width_mm, pixel_height_mm)
  1118. def cvt_image():
  1119. input_dir = 'D:\\code\\code of PMD\\code of PMD\\hud1\\24\\'
  1120. output_dir = 'D:\\code\\code of PMD\\code of PMD\\hud1\\8\\'
  1121. for img in os.listdir(input_dir):
  1122. image = cv2.imread(input_dir + img, 0)
  1123. height, width = image.shape
  1124. # 计算中心四分之一区域的边界
  1125. x1 = width // 4
  1126. x2 = 3 * width // 4
  1127. y1 = height // 4
  1128. y2 = 3 * height // 4
  1129. # 再次减半以保留中心八分之一
  1130. x1 = x1 + (x2 - x1) // 4
  1131. x2 = x2 - (x2 - x1) // 4
  1132. y1 = y1 + (y2 - y1) // 4
  1133. y2 = y2 - (y2 - y1) // 4
  1134. # x1 = int(width/2) - 200
  1135. # x2 = int(width/2) + 200
  1136. # y1 = 280
  1137. # 创建一个黑色图像
  1138. black_image = np.zeros_like(image)
  1139. # 将中心四分之一区域复制到黑色图像上
  1140. black_image[y1:y2, x1:x2] = image[y1:y2, x1:x2]
  1141. cv2.imwrite(output_dir + img, image)
  1142. cv2.namedWindow('src', 0)
  1143. cv2.imshow('src', image)
  1144. cv2.waitKey(10)
  1145. def pixel_to_world(pixel_coords, depth, K, R, t):
  1146. """
  1147. 将像素坐标转换为世界坐标
  1148. :param pixel_coords: 像素坐标 (u, v)
  1149. :param depth: 深度值
  1150. :param K: 内参矩阵 (3x3)
  1151. :param R: 旋转矩阵 (3x3)
  1152. :param t: 平移向量 (3x1)
  1153. :return: 世界坐标 (X, Y, Z)
  1154. """
  1155. # 将像素坐标 (u, v) 转为齐次坐标 (u, v, 1)
  1156. uv1 = np.array([pixel_coords[0], pixel_coords[1], 1])
  1157. # 反投影到相机坐标系
  1158. K_inv = np.linalg.inv(K)
  1159. cam_coords = depth * (K_inv @ uv1)
  1160. # 将相机坐标转换到世界坐标
  1161. R_inv = np.linalg.inv(R)
  1162. world_coords = R_inv @ (cam_coords - t)
  1163. return world_coords
  1164. def calculate_distance(pixel1, pixel2, depth1, depth2, K, R, t):
  1165. """
  1166. 计算两像素点在世界坐标系中的距离
  1167. :param pixel1: 第一个像素点坐标 (u1, v1)
  1168. :param pixel2: 第二个像素点坐标 (u2, v2)
  1169. :param depth1: 第一个点的深度值
  1170. :param depth2: 第二个点的深度值
  1171. :param K: 内参矩阵
  1172. :param R: 外参旋转矩阵
  1173. :param t: 外参平移向量
  1174. :return: 两点之间的实际距离
  1175. """
  1176. # 转换两点到世界坐标
  1177. world_point1 = pixel_to_world(pixel1, depth1, K, R, t)
  1178. world_point2 = pixel_to_world(pixel2, depth2, K, R, t)
  1179. print(world_point1, world_point2)
  1180. # 计算欧几里得距离
  1181. distance = np.linalg.norm(world_point1 - world_point2)
  1182. return distance
  1183. def pixel_to_world_on_plane(u, v, K, r_vector, t, h):
  1184. """
  1185. 将像素点 (u, v) 转换到世界坐标系中,假设物体位于 z=h 的平面上
  1186. :param u: 像素点横坐标
  1187. :param v: 像素点纵坐标
  1188. :param K: 相机内参矩阵 (3x3)
  1189. :param R: 外参旋转矩阵 (3x3)
  1190. :param t: 外参平移向量 (3x1)
  1191. :param h: 平面高度(z=h)
  1192. :return: 世界坐标 (X_w, Y_w, h) 和深度值(点到相机光心的距离)
  1193. """
  1194. # 计算归一化相机坐标 (3x1)
  1195. R = cv2.Rodrigues(np.array(r_vector).reshape(3,1))[0]
  1196. K_inv = np.linalg.inv(K)
  1197. pixel_coords = np.array([u, v, 1]) # 3x1
  1198. normalized_coords = K_inv @ pixel_coords # (3,)
  1199. # 调整为列向量 (3, 1)
  1200. normalized_coords = normalized_coords[:, np.newaxis]
  1201. # 相机光心在世界坐标系中的位置 (3x1)
  1202. camera_center = -np.linalg.inv(R) @ t # (3, 1)
  1203. #print('camera_center = ', camera_center)
  1204. # 光线方向 (3, 1)
  1205. ray_dir = np.linalg.inv(R) @ normalized_coords # (3, 1)
  1206. # 求解射线与 z=h 平面的交点 (标量 lambda)
  1207. lambda_ = (h - camera_center[2]) / ray_dir[2]
  1208. # 世界坐标 (3x1)
  1209. world_coords = camera_center + lambda_ * ray_dir
  1210. #print('world_coords = ', world_coords)
  1211. #world_coords[2] = h # 强制设定 z=h
  1212. # 返回为一维数组 (3,) 或者列向量 (3, 1)
  1213. return world_coords.flatten()
  1214. def get_world_point(K, R, T, contour_points, d):
  1215. A = K
  1216. Rw2c = R
  1217. Rc2w = np.linalg.inv(Rw2c)
  1218. Tw2c = T
  1219. Tc2w = -np.dot(np.linalg.inv(Rw2c), Tw2c)
  1220. world_points_contours = affine_img2world(contour_points, A, Rc2w, Tc2w, d)
  1221. return world_points_contours
  1222. def camera2world_vis():
  1223. # cam_param_path = 'D:\\code\\pmd-python\\config\\cam_params_1125.pkl'
  1224. img_path = 'D:\\data\\one_cam\\1226image\\calibration\\cam\\Image_20241226135705692.bmp'
  1225. #img_path = 'D:\\data\\one_cam\\pad-test-1125\\test3\\calibration\\screen\\Image_20241128190133634.bmp'
  1226. # with open(cam_param_path, 'rb') as pkl_file:
  1227. # cam_params = pickle.load(pkl_file)
  1228. matlab_calib_json_path = ('D:\\code\\code of PMD\\code of PMD\\cameraParams_1_cam0103.json')
  1229. #matlab_calib_json_path = ('D:\\code\\code of PMD\\code of PMD\\cameraParams_1227cam3.json')
  1230. camera_pkl_path = 'D:\\code\\pmd-python\\config\\camera_params_1227cam3.pkl'
  1231. screen_pkl_path = 'D:\\code\\pmd-python\\config\\screen_params_1226_1.pkl'
  1232. screen = 0
  1233. circle = 0
  1234. if screen:
  1235. with open(camera_pkl_path, 'rb') as pkl_file:
  1236. cam_params = pickle.load(pkl_file)
  1237. with open(matlab_calib_json_path, 'r', encoding='utf-8') as json_file:
  1238. screen_params = json.load(json_file)
  1239. if not screen:
  1240. with open(matlab_calib_json_path, 'r', encoding='utf-8') as json_file:
  1241. cam_params = json.load(json_file)
  1242. #x_scale, y_scale = 0.98829, 0.988425
  1243. #x_scale, y_scale = 0.987255, 0.987355
  1244. #x_scale, y_scale = 0.98699, 0.98670
  1245. x_scale, y_scale = 0.993665, 0.9934
  1246. x_scale, y_scale = 1.0023, 0.998
  1247. x_scale, y_scale = 1, 1 #cam0
  1248. cam_params['camera_matrix'][0][0]= cam_params['camera_matrix'][0][0] * x_scale
  1249. cam_params['camera_matrix'][1][1]= cam_params['camera_matrix'][1][1] * y_scale
  1250. dist_rdist = cam_params['dist_rdist']
  1251. dist_tdist = cam_params['dist_tdist']
  1252. r_vector = cam_params['rotation_vectors'][-1]
  1253. K = np.array(cam_params['camera_matrix'])
  1254. R = cv2.Rodrigues(np.array(r_vector).reshape(3,1))[0]
  1255. T = np.array(cam_params['translation_vectors'][-1]).reshape(3,1)
  1256. print('dist_rdist = ', dist_rdist)
  1257. print('dist_tdist = ', dist_tdist)
  1258. print(dist_rdist[0])
  1259. dist_coeffs = np.array([dist_rdist[0] , dist_rdist[1] , dist_tdist[0], dist_tdist[1], 0])
  1260. print('K = ',K)
  1261. print('rotation_matrix = ', R)
  1262. print('r_vector = ', r_vector)
  1263. print('t = ', T)
  1264. print('dist_rdist = ', dist_rdist)
  1265. print('dist_tdist = ', dist_tdist)
  1266. with open(camera_pkl_path, 'wb') as pkl_file:
  1267. cam_params_pkl = {
  1268. 'camera_matrix': K,
  1269. 'distortion_coefficients': dist_coeffs,
  1270. 'rotation_matrix': R,
  1271. 'rotation_vector': r_vector,
  1272. 'translation_vector': T,
  1273. }
  1274. print('cam_params = ', cam_params_pkl)
  1275. cam_data = []
  1276. cam_data.append(cam_params_pkl)
  1277. pickle.dump(cam_data, pkl_file)
  1278. if screen:
  1279. with open(screen_pkl_path, 'wb') as pkl_file:
  1280. r_vector = screen_params['rotation_vectors'][-1]
  1281. screen_distortion = cam_params[0]['distortion_coefficients']
  1282. K = cam_params[0]['camera_matrix'] #same matrix for same camera
  1283. R = cv2.Rodrigues(np.array(r_vector).reshape(3,1))[0]
  1284. T = np.array(screen_params['translation_vectors'][-1]).reshape(3,1)
  1285. dist_coeffs = cam_params[0]['distortion_coefficients']
  1286. print('K = ', K)
  1287. print('R = ', R)
  1288. print('T = ', T)
  1289. T[2] = T[2] + 9
  1290. print('T = ', T)
  1291. screen_params = {
  1292. 'screen_matrix': K,
  1293. 'screen_distortion': screen_distortion,
  1294. 'screen_rotation_matrix': R,
  1295. 'screen_rotation_vector': r_vector,
  1296. 'screen_translation_vector': T
  1297. }
  1298. pickle.dump(screen_params, pkl_file)
  1299. img = cv2.imread(img_path)
  1300. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  1301. chessboard_size = [8, 8]
  1302. img_size = img.shape
  1303. print('raw size = ',img_size)
  1304. h, w = img.shape[:2]
  1305. new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(K, dist_coeffs, (w, h), 1, (w, h))
  1306. undistorted_image = cv2.undistort(gray, K, dist_coeffs, None, new_camera_matrix)
  1307. undistorted_show = cv2.undistort(img, K, dist_coeffs, None, new_camera_matrix)
  1308. print('new_camera_matrix = ', new_camera_matrix)
  1309. print('K = ', K)
  1310. if circle:
  1311. circle_w, circle_h = 8, 6
  1312. chessboard_size = [8, 6]
  1313. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  1314. params = cv2.SimpleBlobDetector_Params()
  1315. params.maxArea = 10e4
  1316. params.minArea = 10
  1317. params.minDistBetweenBlobs = 5
  1318. blobDetector = cv2.SimpleBlobDetector_create(params)
  1319. ret, corners = cv2.findCirclesGrid(undistorted_image, (circle_w, circle_h), cv2.CALIB_CB_SYMMETRIC_GRID, blobDetector, None)
  1320. print('ret = ', ret)
  1321. if ret:
  1322. corners_refined = cv2.cornerSubPix(undistorted_image, corners, (circle_w, circle_h), (-1, -1), criteria)
  1323. cv2.drawChessboardCorners(undistorted_show, (circle_w, circle_h), corners_refined, ret)
  1324. cv2.namedWindow('Detected Circles', 0)
  1325. cv2.imshow("Detected Circles", undistorted_show)
  1326. cv2.waitKey(0)
  1327. cv2.destroyAllWindows()
  1328. # json_path = 'D:\\code\\code of PMD\\code of PMD\\circle_imagePoints.json'
  1329. # json_data = json.load(open(json_path, 'r'))
  1330. # imagePoints = np.array(json_data['imagePoints'])
  1331. # last_image_points = imagePoints[:, :, -1]
  1332. # print(last_image_points)
  1333. # for pt in last_image_points:
  1334. # cv2.circle(img, (int(pt[0]), int(pt[1])), 2, (255,0,255), 2)
  1335. # corners_refined = last_image_points
  1336. # cv2.namedWindow('matlab', 0)
  1337. # cv2.imshow('matlab', img)
  1338. # #cv2.imwrite('vis_chessboard.png', undistorted_show)
  1339. # cv2.waitKey(0)
  1340. else:
  1341. # x, y, w, h = roi
  1342. # print(x, y, w, h)
  1343. # undistorted_image = undistorted_image[y:y+h, x:x+w]
  1344. # print(undistorted_image.shape)
  1345. # 寻找棋盘格角点
  1346. #ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
  1347. flag = cv2.CALIB_CB_EXHAUSTIVE | cv2.CALIB_CB_ACCURACY
  1348. ret, corners = cv2.findChessboardCorners(undistorted_image, chessboard_size, flag)
  1349. print('ret = ', ret)
  1350. # 如果找到足够的角点,添加到列表中
  1351. if ret:
  1352. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  1353. corners_refined = cv2.cornerSubPix(undistorted_image, corners, (11, 11), (-1, -1), criteria)
  1354. img = cv2.drawChessboardCorners(undistorted_image, chessboard_size, corners_refined, ret)
  1355. world_coords = [
  1356. np.array(pixel_to_world_on_plane(u, v, K, r_vector, T, 0)) for u, v in corners_refined.squeeze()
  1357. ]
  1358. corners_refined = corners_refined.squeeze()
  1359. # print('corners_refined = ', corners_refined.shape)
  1360. # 计算水平和垂直方向相邻角点的距离
  1361. horizontal_distances = []
  1362. vertical_distances = []
  1363. cols, rows = chessboard_size
  1364. font_size = 0.9
  1365. font_thickness = 1
  1366. for i in range(chessboard_size[1]): # 遍历每一行
  1367. for j in range(chessboard_size[0] - 1): # 水平相邻点
  1368. idx1 = i * chessboard_size[0] + j
  1369. idx2 = idx1 + 1
  1370. distance = np.linalg.norm(world_coords[idx1] - world_coords[idx2])
  1371. horizontal_distances.append(distance)
  1372. x, y =int(corners_refined[idx1][0]), int(corners_refined[idx1][1])
  1373. distance_text = f"{distance:.4f}"
  1374. cv2.putText(img, distance_text, (x - 30, y ), cv2.FONT_HERSHEY_SIMPLEX, font_size, (0, 255, 0), font_thickness)
  1375. for i in range(chessboard_size[1] - 1): # 垂直相邻点
  1376. for j in range(chessboard_size[0]): # 遍历每一列
  1377. idx1 = i * chessboard_size[0] + j
  1378. idx2 = idx1 + chessboard_size[0]
  1379. distance = np.linalg.norm(world_coords[idx1] - world_coords[idx2])
  1380. vertical_distances.append(distance)
  1381. x, y = int(corners_refined[idx1][0]), int(corners_refined[idx1][1])
  1382. distance_text = f"{distance:.4f}"
  1383. cv2.putText(img, distance_text, (x, y - 30), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 0, 255), font_thickness)
  1384. print("水平方向相邻角点距离:", np.mean(horizontal_distances))
  1385. print("垂直方向相邻角点距离:", np.mean(vertical_distances))
  1386. cv2.namedWindow('src', 0)
  1387. cv2.imshow('src', img)
  1388. cv2.imwrite('vis_chessboard.png', img)
  1389. cv2.waitKey(0)
  1390. # 每四个元素为一组计算相邻点的xy的欧式距离,包括第四个点和第一个点
  1391. def calculate_distances(coords):
  1392. distances = []
  1393. for i in range(0, len(coords), 4):
  1394. group = coords[i:i+4]
  1395. if len(group) < 4:
  1396. break
  1397. d1 = np.linalg.norm(group[0][:2] - group[1][:2])
  1398. d2 = np.linalg.norm(group[1][:2] - group[2][:2])
  1399. d3 = np.linalg.norm(group[2][:2] - group[3][:2])
  1400. d4 = np.linalg.norm(group[3][:2] - group[0][:2])
  1401. distances.append([d1, d2, d3, d4])
  1402. return distances
  1403. def camera2world_aprilgrid_vis():
  1404. # cam_param_path = 'D:\\code\\pmd-python\\config\\cam_params_1125.pkl'
  1405. img_path = 'D:\\data\\four_cam\\calibrate\\calibrate-1016\\cam3\\Image_20241016171139414.bmp'
  1406. #img_path = 'D:\\data\\one_cam\\pad-test-1125\\test3\\calibration\\screen\\Image_20241128190133634.bmp'
  1407. # with open(cam_param_path, 'rb') as pkl_file:
  1408. # cam_params = pickle.load(pkl_file)
  1409. # matlab_calib_json_path = ('D:\\code\\code of PMD\\code of PMD\\camera_calibration2-cam-1129.json')
  1410. # matlab_calib_json_path = ('D:\\code\\code of PMD\\code of PMD\\cameraParams_cam_screen_1216_4-1.json')
  1411. camera_pkl_path = 'D:\\code\\pmd-python\\config\\cam_params_4.pkl'
  1412. camera_pkl_path = 'D:\\code\\pmd-python\\config\\camera_params_1216_4_1.pkl'
  1413. screen_pkl_path = None
  1414. screen = 0
  1415. circle = 0
  1416. chessboard = 0
  1417. aprilgrid = True
  1418. # if screen:
  1419. # with open(camera_pkl_path, 'rb') as pkl_file:
  1420. # cam_params = pickle.load(pkl_file)
  1421. # with open(matlab_calib_json_path, 'r', encoding='utf-8') as json_file:
  1422. # screen_params = json.load(json_file)
  1423. if not screen:
  1424. with open(camera_pkl_path, 'rb') as pkl_file:
  1425. cam_params = pickle.load(pkl_file)
  1426. print('cam_params = ', cam_params)
  1427. #return 0
  1428. x_scale, y_scale = 0.995291, 0.995291 #cam0
  1429. x_scale, y_scale = 0.993975, 0.993975 #cam1
  1430. x_scale, y_scale = 0.993085, 0.993085 #cam2
  1431. x_scale, y_scale = 0.994463, 0.994463 #cam3
  1432. print('cam_params = ', cam_params)
  1433. cam_params['camera_matrix'][0][0]= cam_params['camera_matrix'][0][0] * x_scale
  1434. cam_params['camera_matrix'][1][1]= cam_params['camera_matrix'][1][1] * y_scale
  1435. K = np.array(cam_params['camera_matrix'])
  1436. R = np.array(cam_params['rotation_matrix'])
  1437. T = np.array(cam_params['translation_vector'])
  1438. dist_coeffs = np.array(cam_params['distortion_coefficients'])
  1439. print('K = ',K)
  1440. print('rotation_matrix = ', R)
  1441. print('t = ', T)
  1442. print('dist_coeffs = ', dist_coeffs)
  1443. img = cv2.imread(img_path)
  1444. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  1445. chessboard_size = [6, 8]
  1446. img_size = img.shape
  1447. print('raw size = ',img_size)
  1448. h, w = img.shape[:2]
  1449. new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(K, dist_coeffs, (w, h), 1, (w, h))
  1450. undistorted_image = cv2.undistort(gray, K, dist_coeffs, None, new_camera_matrix)
  1451. undistorted_show = cv2.undistort(img, K, dist_coeffs, None, new_camera_matrix)
  1452. if circle:
  1453. circle_w, circle_h = 6, 8
  1454. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  1455. params = cv2.SimpleBlobDetector_Params()
  1456. params.maxArea = 10e4
  1457. params.minArea = 10
  1458. params.minDistBetweenBlobs = 5
  1459. blobDetector = cv2.SimpleBlobDetector_create(params)
  1460. ret, corners = cv2.findCirclesGrid(undistorted_image, (circle_w, circle_h), cv2.CALIB_CB_SYMMETRIC_GRID, blobDetector, None)
  1461. if ret:
  1462. corners_refined = cv2.cornerSubPix(undistorted_image, corners, (circle_w, circle_h), (-1, -1), criteria)
  1463. cv2.drawChessboardCorners(undistorted_show, (circle_w, circle_h), corners_refined, ret)
  1464. cv2.namedWindow('Detected Circles', 0)
  1465. cv2.imshow("Detected Circles", undistorted_show)
  1466. cv2.waitKey(0)
  1467. cv2.destroyAllWindows()
  1468. # json_path = 'D:\\code\\code of PMD\\code of PMD\\circle_imagePoints.json'
  1469. # json_data = json.load(open(json_path, 'r'))
  1470. # imagePoints = np.array(json_data['imagePoints'])
  1471. # last_image_points = imagePoints[:, :, -1]
  1472. # print(last_image_points)
  1473. # for pt in last_image_points:
  1474. # cv2.circle(img, (int(pt[0]), int(pt[1])), 2, (255,0,255), 2)
  1475. # corners_refined = last_image_points
  1476. # cv2.namedWindow('matlab', 0)
  1477. # cv2.imshow('matlab', img)
  1478. # #cv2.imwrite('vis_chessboard.png', undistorted_show)
  1479. # cv2.waitKey(0)
  1480. elif chessboard:
  1481. # x, y, w, h = roi
  1482. # print(x, y, w, h)
  1483. # undistorted_image = undistorted_image[y:y+h, x:x+w]
  1484. # print(undistorted_image.shape)
  1485. # 寻找棋盘格角点
  1486. #ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
  1487. flag = cv2.CALIB_CB_EXHAUSTIVE | cv2.CALIB_CB_ACCURACY
  1488. ret, corners = cv2.findChessboardCorners(undistorted_image, chessboard_size, flag)
  1489. print('ret = ', ret)
  1490. # 如果找到足够的角点,添加到列表中
  1491. if ret:
  1492. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  1493. corners_refined = cv2.cornerSubPix(undistorted_image, corners, (11, 11), (-1, -1), criteria)
  1494. img = cv2.drawChessboardCorners(undistorted_image, chessboard_size, corners_refined, ret)
  1495. elif aprilgrid:
  1496. detector = Detector('t36h11')
  1497. detections = detector.detect(gray)
  1498. corners_refined = []
  1499. # 遍历每个检测到的标签
  1500. for detection in detections:
  1501. corners = detection.corners # 检测到的角点 (4, 2)
  1502. tag_id = detection.tag_id # 标签 ID
  1503. #print(tag_id, corners)
  1504. corners_refined.append(corners)
  1505. corners_refined = np.array(corners_refined).reshape(len(corners_refined) * 4, 2)
  1506. print('corners_refined = ', corners_refined.shape)
  1507. world_coords = [
  1508. np.array(pixel_to_world_on_plane(u, v, K, R, T, 0)) for u, v in corners_refined
  1509. ]
  1510. corners_refined = corners_refined.squeeze()
  1511. for pt in corners_refined:
  1512. cv2.circle(img, (int(pt[0]), int(pt[1])), 3, (255,0,255), 2)
  1513. distances = calculate_distances(world_coords)
  1514. print(np.mean(distances))
  1515. # cv2.namedWindow('src', 0)
  1516. # cv2.imshow('src', img)
  1517. # cv2.waitKey(0)
  1518. # print('corners_refined = ', corners_refined.shape)
  1519. # 计算水平和垂直方向相邻角点的距离
  1520. # horizontal_distances = []
  1521. # vertical_distances = []
  1522. # cols, rows = chessboard_size
  1523. # font_size = 1.9
  1524. # font_thickness = 2
  1525. # for i in range(chessboard_size[1]): # 遍历每一行
  1526. # for j in range(chessboard_size[0] - 1): # 水平相邻点
  1527. # idx1 = i * chessboard_size[0] + j
  1528. # idx2 = idx1 + 1
  1529. # distance = np.linalg.norm(world_coords[idx1] - world_coords[idx2])
  1530. # horizontal_distances.append(distance)
  1531. # x, y =int(corners_refined[idx1][0]), int(corners_refined[idx1][1])
  1532. # distance_text = f"{distance:.4f}"
  1533. # #cv2.putText(img, distance_text, (x - 30, y ), cv2.FONT_HERSHEY_SIMPLEX, font_size, (0, 255, 0), font_thickness)
  1534. # for i in range(chessboard_size[1] - 1): # 垂直相邻点
  1535. # for j in range(chessboard_size[0]): # 遍历每一列
  1536. # idx1 = i * chessboard_size[0] + j
  1537. # idx2 = idx1 + chessboard_size[0]
  1538. # distance = np.linalg.norm(world_coords[idx1] - world_coords[idx2])
  1539. # vertical_distances.append(distance)
  1540. # x, y = int(corners_refined[idx1][0]), int(corners_refined[idx1][1])
  1541. # distance_text = f"{distance:.4f}"
  1542. # #cv2.putText(img, distance_text, (x, y - 30), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 0, 255), font_thickness)
  1543. # print("水平方向相邻角点距离:", np.mean(horizontal_distances))
  1544. # print("垂直方向相邻角点距离:", np.mean(vertical_distances))
  1545. def generate_fringe_patterns(width, height, shifts, frequency=1, orientation='horizontal'):
  1546. patterns = []
  1547. for i in range(shifts):
  1548. shift = (i * 2 * np.pi) / shifts
  1549. if orientation == 'horizontal':
  1550. x = np.linspace(0, 2 * np.pi * frequency, width) + shift
  1551. pattern = np.sin(x)
  1552. pattern = np.tile(pattern, (height, 1))
  1553. # cv2.namedWindow('hpattern', 0)
  1554. # cv2.imshow('hpattern', pattern)
  1555. # cv2.waitKey(0)
  1556. elif orientation == 'vertical':
  1557. y = np.linspace(0, 2 * np.pi * frequency, height) + shift
  1558. pattern = np.sin(y)
  1559. pattern = np.tile(pattern.reshape(-1, 1), (1, width))
  1560. # cv2.namedWindow('vpattern', 0)
  1561. # cv2.imshow('vpattern', pattern)
  1562. # cv2.waitKey(0)
  1563. patterns.append(pattern)
  1564. return patterns
  1565. def calculate_phase(images):
  1566. shifts = len(images)
  1567. sin_sum = np.zeros_like(images[0])
  1568. cos_sum = np.zeros_like(images[0])
  1569. for i in range(shifts):
  1570. sin_sum += images[i] * np.sin(2 * np.pi * i / shifts)
  1571. cos_sum += images[i] * np.cos(2 * np.pi * i / shifts)
  1572. phase = np.arctan2(sin_sum, cos_sum)
  1573. unwrapped_phase = unwrap_phase(phase)
  1574. return unwrapped_phase
  1575. def extract_cut_lines(phase_map, threshold):
  1576. horizontal_lines = np.argwhere(np.abs(np.diff(np.sign(phase_map - threshold), axis=1)) > 0)
  1577. vertical_lines = np.argwhere(np.abs(np.diff(np.sign(phase_map - threshold), axis=0)) > 0)
  1578. return horizontal_lines, vertical_lines
  1579. def find_intersections(horizontal_lines, vertical_lines, tolerance=2):
  1580. intersections = []
  1581. vertical_set = set(map(tuple, vertical_lines))
  1582. for h_line in horizontal_lines:
  1583. y, x = h_line
  1584. for dy in range(-tolerance, tolerance + 1):
  1585. for dx in range(-tolerance, tolerance + 1):
  1586. if (y + dy, x + dx) in vertical_set:
  1587. intersections.append([y, x])
  1588. break
  1589. return np.array(intersections)
  1590. def visualize_results(phase_map, intersections):
  1591. plt.imshow(phase_map, cmap='jet')
  1592. plt.scatter( intersections[:, 0], color='red', s=5, label='Intersections')
  1593. #plt.scatter(intersections[:, 1], intersections[:, 0], color='red', s=5, label='Intersections')
  1594. plt.colorbar()
  1595. plt.legend()
  1596. plt.title("Phase Cut Lines Intersections")
  1597. plt.show()
  1598. def extract_features_from_patterns(patterns, threshold=np.pi/2):
  1599. phase_map = calculate_phase(patterns)
  1600. cv2.namedWindow('vpattern', 0)
  1601. cv2.imshow('vpattern', phase_map)
  1602. cv2.waitKey(0)
  1603. horizontal_lines, vertical_lines = extract_cut_lines(phase_map, threshold)
  1604. intersections = find_intersections(horizontal_lines, vertical_lines)
  1605. print('intersections = ', intersections)
  1606. visualize_results(phase_map, intersections)
  1607. return intersections
  1608. def draw_calibration_pattern(image_size, circle_radius, center_distance, rows, cols, img_name):
  1609. # 创建一个全白的图像
  1610. image = np.ones((image_size[1], image_size[0]), dtype=np.uint8) * 255
  1611. circle_distance = center_distance - 2 * circle_radius
  1612. # 计算圆心坐标
  1613. start_x = (image_size[0] - (circle_radius * 2 + circle_distance) * (cols - 1)) // 2
  1614. start_y = (image_size[1] - (circle_radius * 2 + circle_distance) * (rows - 1)) // 2
  1615. # 绘制每个圆
  1616. for i in range(rows):
  1617. for j in range(cols):
  1618. center_x = start_x + j * (circle_radius * 2 + circle_distance)
  1619. center_y = start_y + i * (circle_radius * 2 + circle_distance)
  1620. # 用黑色填充圆形
  1621. cv2.circle(image, (center_x, center_y), circle_radius, (0, 0, 0), -1)
  1622. # 显示图像
  1623. cv2.namedWindow('Calibration Pattern', 0)
  1624. cv2.imshow('Calibration Pattern', image)
  1625. cv2.waitKey(0)
  1626. cv2.destroyAllWindows()
  1627. # 可选:保存为文件
  1628. cv2.imwrite(img_name, image)
  1629. def paste_img():
  1630. src_path = 'D:\\code\\pmd-python\\tools\\patterns3_honor_800_400\\'
  1631. dst_path = 'D:\\code\\pmd-python\\tools\\patterns3_honor_800_400_paste\\'
  1632. os.makedirs(dst_path, exist_ok = True)
  1633. large_w, large_h = 1920, 1200
  1634. for img in os.listdir(src_path):
  1635. small_img = cv2.imread(src_path + img)
  1636. small_h, small_w, _ = small_img.shape
  1637. # 创建一张大图片(例如白色背景,3通道)
  1638. large_img = np.ones((large_h, large_w, 3), dtype=np.uint8) * 255 # 白色背景
  1639. # 计算小图片放置在大图片中心的起始位置
  1640. start_row = (large_h - small_h) // 2
  1641. start_col = (large_w - small_w) // 2
  1642. print('start_row , start_col = ',start_row, start_col)
  1643. # 将小图片贴到大图片中心
  1644. large_img[start_row:start_row + small_h, start_col:start_col + small_w] = small_img
  1645. # 保存结果
  1646. #cv2.imwrite(dst_path + img, large_img)
  1647. def pkl_vis():
  1648. cam_para_path = 'D:\\code\\pmd-python\\config\\cam_params_4.pkl'
  1649. with open(cam_para_path, 'rb') as pkl_file:
  1650. cam_params = pickle.load(pkl_file)
  1651. print('cam_params = ', cam_params)
  1652. def set_cam_params():
  1653. cam_params_path = 'D:\\code\\pmd-python\\config\\cam_params_4.pkl'
  1654. new_cam0_path = 'D:\\code\\pmd-python\\config\\camera_params_1227cam0.pkl'
  1655. new_cam1_path = 'D:\\code\\pmd-python\\config\\camera_params_1227cam1.pkl'
  1656. new_cam2_path = 'D:\\code\\pmd-python\\config\\camera_params_1227cam2.pkl'
  1657. new_cam3_path = 'D:\\code\\pmd-python\\config\\camera_params_1227cam3.pkl'
  1658. with open(new_cam0_path, 'rb') as pkl_file:
  1659. cam_params0 = pickle.load(pkl_file)
  1660. with open(new_cam1_path, 'rb') as pkl_file:
  1661. cam_params1 = pickle.load(pkl_file)
  1662. with open(new_cam2_path, 'rb') as pkl_file:
  1663. cam_params2 = pickle.load(pkl_file)
  1664. with open(new_cam3_path, 'rb') as pkl_file:
  1665. cam_params3 = pickle.load(pkl_file)
  1666. with open(cam_params_path, 'rb') as pkl_file:
  1667. cam_params = pickle.load(pkl_file)
  1668. cam_params[0]['camera_matrix'] = cam_params0[0]['camera_matrix']
  1669. cam_params[1]['camera_matrix'] = cam_params1[0]['camera_matrix']
  1670. cam_params[2]['camera_matrix'] = cam_params2[0]['camera_matrix']
  1671. cam_params[3]['camera_matrix'] = cam_params3[0]['camera_matrix']
  1672. with open('D:\\code\\pmd-python\\config\\cam_params_4_refined.pkl', 'wb') as pkl_file:
  1673. pickle.dump(cam_params, pkl_file)
  1674. def refine_cam_params():
  1675. img1_path = 'D:\\data\\one_cam\\1226image\\calibration\\cam\\Image_20241226135705692.bmp'
  1676. img2_path = 'D:\\data\\one_cam\\betone1230\\20250103151342402-pingjing\\0_frame_24.bmp'
  1677. matlab_calib_json_path = 'D:\\code\\code of PMD\\code of PMD\\cameraParams_1_cam0103.json'
  1678. if __name__ == '__main__':
  1679. #draw_calibration_pattern(image_size=(1920, 1080), circle_radius=40, center_distance=100, rows=6, cols=8, img_name='calibration_pattern-1226-1920-1080-40-100.png')
  1680. #paste_img()
  1681. #camera2world_vis()
  1682. #camera2world_aprilgrid_vis()
  1683. #pkl_vis()
  1684. # set_cam_params()
  1685. refine_cam_params()