当前位置: 首页 > news >正文

Nuscenes数据集点云数据如何转换到图像上

零、概要

注意:该文章是手写ai自动驾驶,Nuscenes数据集的笔记。

  1. 首先,学习需要使用到 nuScenes 数据集。python 工具需要使用到 nuscenes-devkit、pyquaternion
from nuscenes.nuscenes import NuScenes
from pyquaternion import Quaternion # 四元数操作的包

https://github.com/nutonomy/nuscenes-devkit/tree/master

  1. 官网https://github.com/nutonomy/nuscenes-devkit/blob/master/python-sdk/tutorials/nuscenes_tutorial.ipynb可以看非常好

  2. https://www.nuscenes.org/nuscenes#explore该网址能够可视化了解nuscenes数据集。

一、提出疑问

  1. 如何将Lidar的点与图片对应,画出

在这里插入图片描述

二、代码大体了解

# pip install nuscenes-devkit               # tingfeng1 需要包 nuscenes-devkit
from nuscenes.nuscenes import NuScenes
from nuscenes.utils.data_classes import Box
from pyquaternion import Quaternion
import numpy as np
import cv2 
import osnp.set_printoptions(precision=3, suppress=True)version = "mini"
dataroot = "/data/mini"
nuscenes = NuScenes(version='v1.0-{}'.format(version), dataroot=dataroot, verbose=False) # tingfeng2 实例化类NuScenescameras = ['CAM_FRONT_LEFT', 'CAM_FRONT', 'CAM_FRONT_RIGHT', 'CAM_BACK_LEFT', 'CAM_BACK', 'CAM_BACK_RIGHT']
sample  = nuscenes.sample[0] # tingfeng3获取第一个样本def to_matrix4x4_2(rotation, translation, inverse=True):output = np.eye(4)output[:3, :3] = rotationoutput[:3, 3]  = translationif inverse:output = np.linalg.inv(output)return outputdef to_matrix4x4(m):output = np.eye(4)output[:3, :3] = mreturn output# tingfeng4 拿到样本后,获取Lidar的信息
# Lidar的点云是基于Lidar坐标系的
# 需要将lidar的坐标转换到ego,然后ego到global
lidar_sample_data = nuscenes.get('sample_data', sample['data']["LIDAR_TOP"])
lidar_file        = os.path.join(nuscenes.dataroot, lidar_sample_data["filename"])
lidar_pointcloud  = np.fromfile(lidar_file, dtype=np.float32).reshape(-1, 5)
ego_pose          = nuscenes.get('ego_pose', lidar_sample_data['ego_pose_token'])
ego_to_global     = to_matrix4x4_2(Quaternion(ego_pose['rotation']).rotation_matrix, np.array(ego_pose['translation']), False)
lidar_sensor      = nuscenes.get('calibrated_sensor', lidar_sample_data['calibrated_sensor_token'])
lidar_to_ego      = to_matrix4x4_2(Quaternion(lidar_sensor['rotation']).rotation_matrix, np.array(lidar_sensor['translation']), False)
lidar_to_global   = ego_to_global @ lidar_to_ego
lidar_points      = np.concatenate([lidar_pointcloud[:, :3], np.ones((len(lidar_pointcloud), 1))], axis=1)
lidar_points      = lidar_points @ lidar_to_global.T # tingfeng5 点云转到global坐标系# tingfeng6 获取每一个camera的信息
# 1. annotation标注是global坐标系的
# 2. 由于时间戳的缘故,每个camera/lidar/radar都有ego_pose用于修正差异
for camera in cameras:camera_sample_data = nuscenes.get('sample_data', sample['data'][camera])image_file         = os.path.join(nuscenes.dataroot, camera_sample_data["filename"])ego_pose           = nuscenes.get('ego_pose', camera_sample_data['ego_pose_token'])global_to_ego      = to_matrix4x4_2(Quaternion(ego_pose['rotation']).rotation_matrix, np.array(ego_pose['translation']))camera_sensor    = nuscenes.get('calibrated_sensor', camera_sample_data['calibrated_sensor_token']) camera_intrinsic = to_matrix4x4(camera_sensor['camera_intrinsic'])ego_to_camera    = to_matrix4x4_2(Quaternion(camera_sensor['rotation']).rotation_matrix, np.array(camera_sensor['translation']))global_to_image  = camera_intrinsic @ ego_to_camera @ global_to_ego # tingfeng7 根据相机中得到的信息得到的三个矩阵,将global坐标系的坐标转换到image上image = cv2.imread(image_file)  for annotation_token in sample['anns']:   # tingfeng8 标注的框的信息,在sample['anns']中,遍历标注框instance = nuscenes.get('sample_annotation', annotation_token)box = Box(instance['translation'], instance['size'], Quaternion(instance['rotation']))corners = np.ones((4, 8))corners[:3, :] = box.corners() corners = (global_to_image @ corners)[:3]   # tingfeng11 注意角点是global坐标,这里转换到image上corners[:2] /= corners[[2]]corners = corners.T.astype(int) # tingfeng9 到此是计算框的角点ix, iy = [0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7], [4, 5, 6, 7, 1, 2, 3, 0, 5, 6, 7, 4]for p0, p1 in zip(corners[ix], corners[iy]):if p0[2] <= 0 or p1[2] <= 0: continuecv2.line(image, (p0[0], p0[1]), (p1[0], p1[1]), (0, 255, 0), 2, 16) # tingfeng10 到此是把角点划到image上image_based_points = lidar_points @ global_to_image.T   # tingfeng12 雷达的信息也投射到image上image_based_points[:, :2] /= image_based_points[:, [2]]show_points = image_based_points[image_based_points[:, 2] > 0][:, :2].astype(np.int32)for x, y in show_points:cv2.circle(image, (x, y), 3, (255, 0, 0), -1, 16) # # tingfeng13 lidar的信息画到image上cv2.imwrite(f"{camera}.jpg", image)

三、手写

3.1 了解 NuScenes 的实例对象

  1. 了解如何实例化,参数需要什么
    在这里插入图片描述

    • 第一个参数 version。目前理解是给 nuscenes 下载下来。那个有一堆 json 文件的文件夹名字。
      • 通常是 v1.0-{mini, test, trainval}
    • 第二个参数是数据集根目录。图片上是 nuscenes
  2. 了解如何获取数据,数据是什么样

    • 如下实例化后。就能简单查看里面的数据了。
from nuscenes.nuscenes import NuScenes
import numpy as np
import cv2
import os# 1. 实例化NuScenes
version = "v1.0-mini"  # 因为我们是用的是mini数据集
dataroot = "/home/shenlan09/tingfeng/BEVFusion/BEVfusion/bevfusion/configs/nuscenes" 
# 我的数据集放这里了,我用的绝对路径
nuscenes = NuScenes(version=version, dataroot=dataroot, verbose=False)
# print(len(nuscenes.sample)) # 404sample = nuscenes.sample[0] # 读取sample中第一个数据,具体了解 
print(nuscenes)
  • 另外。官方教程上,拿到第一个 sample 可以用
%matplotlib inline
from nuscenes.nuscenes import NuScenes
nusc = NuScenes(version='v1.0-mini', dataroot="/home/shenlan09/tingfeng/BEVFusion/BEVfusion/bevfusion/configs/nuscenes", verbose=True)
'''
Loading NuScenes tables for version v1.0-mini...
23 category,
8 attribute,
4 visibility,
911 instance,
12 sensor,
120 calibrated_sensor,
31206 ego_pose,
8 log,
10 scene,
404 sample,
31206 sample_data,
18538 sample_annotation,
4 map,
Done loading in 0.5 seconds.
============================Reverse indexing ...
Done reverse indexing in 0.1 seconds.
====================================='''
nusc.list_scenes()'''
scene-0061, Parked truck, construction, intersectio... [18-07-24 03:28:47]   19s, singapore-onenorth, #anns:4622
scene-0103, Many peds right, wait for turning car, ... [18-08-01 19:26:43]   19s, boston-seaport, #anns:2046
scene-0655, Parking lot, parked cars, jaywalker, be... [18-08-27 15:51:32]   20s, boston-seaport, #anns:2332
scene-0553, Wait at intersection, bicycle, large tr... [18-08-28 20:48:16]   20s, boston-seaport, #anns:1950
scene-0757, Arrive at busy intersection, bus, wait ... [18-08-30 19:25:08]   20s, boston-seaport, #anns:592
scene-0796, Scooter, peds on sidewalk, bus, cars, t... [18-10-02 02:52:24]   20s, singapore-queensto, #anns:708
scene-0916, Parking lot, bicycle rack, parked bicyc... [18-10-08 07:37:13]   20s, singapore-queensto, #anns:2387
scene-1077, Night, big street, bus stop, high speed... [18-11-21 11:39:27]   20s, singapore-hollandv, #anns:890
scene-1094, Night, after rain, many peds, PMD, ped ... [18-11-21 11:47:27]   19s, singapore-hollandv, #anns:1762
scene-1100, Night, peds in sidewalk, peds cross cro... [18-11-21 11:49:47]   19s, singapore-hollandv, #anns:935
'''
my_scene = nusc.scene[0]
'''
{'token': 'cc8c0bf57f984915a77078b10eb33198',
'log_token': '7e25a2c8ea1f41c5b0da1e69ecfa71a2',
'nbr_samples': 39,
'first_sample_token': 'ca9a282c9e77460f8360f564131a8af5',
'last_sample_token': 'ed5fc18c31904f96a8f0dbb99ff069c0',
'name': 'scene-0061',
'description': 'Parked truck, construction, intersection, turn left, following a van'}
'''first_sample_token = my_scene['first_sample_token']# The rendering command below is commented out because it tends to crash in notebooksnusc.render_sample(first_sample_token) # 能够在 ipynb 文件中渲染

3.1.1 数据格式

  • (nuscenes.sample[0]的打印输出信息)nuscenes.sample[0]是指第一个样本。是某时刻的所有数据
    • 包含多个相机数据token、Lidar数据对应的token。anns是3D标注边框的token
{'token': 'ca9a282c9e77460f8360f564131a8af5', 
'timestamp': 1532402927647951, 
'prev': '', 
'next': '39586f9d59004284a7114a68825e8eec', 
'scene_token': 'cc8c0bf57f984915a77078b10eb33198', 
'data!!传感器信息!!': {'RADAR_FRONT': '==每个里面的字符串叫token,根据这个就能找到位置的信息==37091c75b9704e0daa829ba56dfa0906', 'RADAR_FRONT_LEFT': '11946c1461d14016a322916157da3c7d', 'RADAR_FRONT_RIGHT': '491209956ee3435a9ec173dad3aaf58b', 'RADAR_BACK_LEFT': '312aa38d0e3e4f01b3124c523e6f9776', 'RADAR_BACK_RIGHT': '07b30d5eb6104e79be58eadf94382bc1', 'LIDAR_TOP': '9d9bf11fb0e144c8b446d54a8a00184f', 'CAM_FRONT': 'e3d495d4ac534d54b321f50006683844', 'CAM_FRONT_RIGHT': 'aac7867ebf4f446395d29fbd60b63b3b', 'CAM_BACK_RIGHT': '79dbb4460a6b40f49f9c150cb118247e', 'CAM_BACK': '03bea5763f0f4722933508d5999c5fd8', 'CAM_BACK_LEFT': '43893a033f9c46d4a51b5e08a67a1eb7', 'CAM_FRONT_LEFT': 'fe5422747a7d4268a4b07fc396707b23'}, 'anns': ['ef63a697930c4b20a6b9791f423351da', '6b89da9bf1f84fd6a5fbe1c3b236f809', '924ee6ac1fed440a9d9e3720aac635a0', '91e3608f55174a319246f361690906ba', 'cd051723ed9c40f692b9266359f547af', '36d52dfedd764b27863375543c965376', '70af124fceeb433ea73a79537e4bea9e', '63b89fe17f3e41ecbe28337e0e35db8e', 'e4a3582721c34f528e3367f0bda9485d', 'fcb2332977ed4203aa4b7e04a538e309', 'a0cac1c12246451684116067ae2611f6', '02248ff567e3497c957c369dc9a1bd5c', '9db977e264964c2887db1e37113cddaa', 'ca9c5dd6cf374aa980fdd81022f016fd', '179b8b54ee74425893387ebc09ee133d', '5b990ac640bf498ca7fd55eaf85d3e12', '16140fbf143d4e26a4a7613cbd3aa0e8', '54939f11a73d4398b14aeef500bf0c23', '83d881a6b3d94ef3a3bc3b585cc514f8', '74986f1604f047b6925d409915265bf7', 'e86330c5538c4858b8d3ffe874556cc5', 'a7bd5bb89e27455bbb3dba89a576b6a1', 'fbd9d8c939b24f0eb6496243a41e8c41', '198023a1fb5343a5b6fad033ab8b7057', 'ffeafb90ecd5429cba23d0be9a5b54ee', 'cc636a58e27e446cbdd030c14f3718fd', '076a7e3ec6244d3b84e7df5ebcbac637', '0603fbaef1234c6c86424b163d2e3141', 'd76bd5dcc62f4c57b9cece1c7bcfabc5', '5acb6c71bcd64aa188804411b28c4c8f', '49b74a5f193c4759b203123b58ca176d', '77519174b48f4853a895f58bb8f98661', 'c5e9455e98bb42c0af7d1990db1df0c9', 'fcc5b4b5c4724179ab24962a39ca6d65', '791d1ca7e228433fa50b01778c32449a', '316d20eb238c43ef9ee195642dd6e3fe', 'cda0a9085607438c9b1ea87f4360dd64', 'e865152aaa194f22b97ad0078c012b21', '7962506dbc24423aa540a5e4c7083dad', '29cca6a580924b72a90b9dd6e7710d3e', 'a6f7d4bb60374f868144c5ba4431bf4c', 'f1ae3f713ba946069fa084a6b8626fbf', 'd7af8ede316546f68d4ab4f3dbf03f88', '91cb8f15ed4444e99470d43515e50c1d', 'bc638d33e89848f58c0b3ccf3900c8bb', '26fb370c13f844de9d1830f6176ebab6', '7e66fdf908d84237943c833e6c1b317a', '67c5dbb3ddcc4aff8ec5140930723c37', 'eaf2532c820740ae905bb7ed78fb1037', '3e2d17fa9aa5484d9cabc1dfca532193', 'de6bd5ffbed24aa59c8891f8d9c32c44', '9d51d699f635478fbbcd82a70396dd62', 'b7cbc6d0e80e4dfda7164871ece6cb71', '563a3f547bd64a2f9969278c5ef447fd', 'df8917888b81424f8c0670939e61d885', 'bb3ef5ced8854640910132b11b597348', 'a522ce1d7f6545d7955779f25d01783b', '1fafb2468af5481ca9967407af219c32', '05de82bdb8484623906bb9d97ae87542', 'bfedb0d85e164b7697d1e72dd971fb72', 'ca0f85b4f0d44beb9b7ff87b1ab37ff5', 'bca4bbfdef3d4de980842f28be80b3ca', 'a834fb0389a8453c810c3330e3503e16', '6c804cb7d78943b195045082c5c2d7fa', 'adf1594def9e4722b952fea33b307937', '49f76277d07541c5a584aa14c9d28754', '15a3b4d60b514db5a3468e2aef72a90c', '18cc2837f2b9457c80af0761a0b83ccc', '2bfcc693ae9946daba1d9f2724478fd4']}
  • 直接使用下方代码,可以直接打印
nuscenes.list_sample(sample["token"])
'''
Sample: ca9a282c9e77460f8360f564131a8af5sample_data_token: 37091c75b9704e0daa829ba56dfa0906, mod: radar, channel: RADAR_FRONT
sample_data_token: 11946c1461d14016a322916157da3c7d, mod: radar, channel: RADAR_FRONT_LEFT
sample_data_token: 491209956ee3435a9ec173dad3aaf58b, mod: radar, channel: RADAR_FRONT_RIGHT
sample_data_token: 312aa38d0e3e4f01b3124c523e6f9776, mod: radar, channel: RADAR_BACK_LEFT
sample_data_token: 07b30d5eb6104e79be58eadf94382bc1, mod: radar, channel: RADAR_BACK_RIGHT
sample_data_token: 9d9bf11fb0e144c8b446d54a8a00184f, mod: lidar, channel: LIDAR_TOP
sample_data_token: e3d495d4ac534d54b321f50006683844, mod: camera, channel: CAM_FRONT
sample_data_token: aac7867ebf4f446395d29fbd60b63b3b, mod: camera, channel: CAM_FRONT_RIGHT
sample_data_token: 79dbb4460a6b40f49f9c150cb118247e, mod: camera, channel: CAM_BACK_RIGHT
sample_data_token: 03bea5763f0f4722933508d5999c5fd8, mod: camera, channel: CAM_BACK
sample_data_token: 43893a033f9c46d4a51b5e08a67a1eb7, mod: camera, channel: CAM_BACK_LEFT
sample_data_token: fe5422747a7d4268a4b07fc396707b23, mod: camera, channel: CAM_FRONT_LEFTsample_annotation_token: ef63a697930c4b20a6b9791f423351da, category: human.pedestrian.adult
sample_annotation_token: 6b89da9bf1f84fd6a5fbe1c3b236f809, category: human.pedestrian.adult
sample_annotation_token: 924ee6ac1fed440a9d9e3720aac635a0, category: vehicle.car
sample_annotation_token: 91e3608f55174a319246f361690906ba, category: human.pedestrian.adult
sample_annotation_token: cd051723ed9c40f692b9266359f547af, category: movable_object.trafficcone
sample_annotation_token: 36d52dfedd764b27863375543c965376, category: vehicle.bicycle
sample_annotation_token: 70af124fceeb433ea73a79537e4bea9e, category: human.pedestrian.adult
sample_annotation_token: 63b89fe17f3e41ecbe28337e0e35db8e, category: vehicle.car
sample_annotation_token: e4a3582721c34f528e3367f0bda9485d, category: human.pedestrian.adult
sample_annotation_token: fcb2332977ed4203aa4b7e04a538e309, category: movable_object.barrier
...
sample_annotation_token: 49f76277d07541c5a584aa14c9d28754, category: vehicle.car
sample_annotation_token: 15a3b4d60b514db5a3468e2aef72a90c, category: movable_object.barrier
sample_annotation_token: 18cc2837f2b9457c80af0761a0b83ccc, category: movable_object.barrier
sample_annotation_token: 2bfcc693ae9946daba1d9f2724478fd4, category: movable_object.barrier
'''

3.1.2 数据格式–官网图例

  • 这个图例学完下面的代码,回来看。就能取到自己想要的数据。

在这里插入图片描述

  • 得到sample_data,获取不同数据需使用不同代码
    1. 通过sample_data 的filename 定位到具体文件。用 np.fromfile读取数据
    2. 通过sample_data 的calibrated_sensor_token,用nuscenes定位定位calibrated_sensor,拿到标定数据
3.1.2.1 上图理解–sample_token 获取 sample_data
  • sample["data"]["LIDAR_TOP"]
    • sample["data"]中存储了 RADAR LIDAR CAM的信息
    • sample["data"]["LIDAR_TOP"]能拿到相机对应的sample_token
    • 图中的弧线箭头,我理解为通过这个 sample_token 是能够获得 sample_data的
    • sample_data = nuscenes.get('sample_data', lidar_sample_token)是通过sample_token 获得 sample_data具体方法
      • 当得到了 sample_data 我们就能得到 sample_data 中的其他信息
      • print(sample_data)
{'token': '9d9bf11fb0e144c8b446d54a8a00184f', 
'sample_token': 'ca9a282c9e77460f8360f564131a8af5', 
'ego_pose_token': '9d9bf11fb0e144c8b446d54a8a00184f', 
'calibrated_sensor_token': 'a183049901c24361a6b0b11b8013137c', 
'timestamp': 1532402927647951, 
'fileformat': 'pcd', 
'is_key_frame': True, 
'height': 0, 
'width': 0, 
'filename': 'samples/LIDAR_TOP/n015-2018-07-24-11-22-45+0800__LIDAR_TOP__1532402927647951.pcd.bin', 
'prev': '', 
'next': '0cedf1d2d652468d92d23491136b5d15', 
'sensor_modality': 'lidar', 
'channel': 'LIDAR_TOP'}
可视化
  • 会输出名字为123.png的图像

nuscenes.render_sample_data(lidar_sample_data["token"], out_path="./123")
3.1.2.2 通过sampe_data中的filename拿到具体的雷达数据.pcd.bin文件
lidar_filename = sample_data["filename"] # samples/LIDAR_TOP/n015-2018-07-24-11-22-45+0800__LIDAR_TOP__1532402927647951.pcd.bin
data = np.fromfile(os.path.join(dataroot, lidar_filename), dtype=np.float32).reshape(-1, 5)
print(data.shape) # (34688, 5) #x y z  intensity   ring_index
  • 实际上拿到的是某种路径samples/LIDAR_TOP/n015-2018-07-24-11-22-45+0800__LIDAR_TOP__1532402927647951.pcd.bin
  • 暂时理解为 os.path.join(dataroot, lidar_filename)

3.2 坐标系的理解

3. 坐标系3.1 全局坐标系,global coordinate- 可以简单的认为,车辆在t0时刻的位置认为是全局坐标系的原点  3.2 车体坐标系,ego_pose. ego coordinate- 以车体为原点的坐标系3.3 传感器坐标系- lidar   的坐标系- camera  的坐标系- radar   的坐标系

3.3 标定的理解

  • 第一个理解。整个过程如下:
    • 即将 lidar 点云转换到 global,再转换到图片上
过程为 timestamp = t0 时的lidar_points -> ego_pose0 -> global -> ego_pose1 -> camera -> intrinsic -> image
  • 第二个理解,拿到 4*4 变换矩阵的过程

在这里插入图片描述

4. 标定calibater
lidar的标定,获得的结果是:lidar相对于ego而言的位置(translation),和旋转(rotation) - translation 可以用3float数字表示位置- 相对于ego而言的位置- rotation则是用4float表示旋转,用的是四元数camera的标定,获得的结果是:camera相对于ego而言的位置 (translation),和旋转(rotation) - translation 可以用3float数字表示位置- 相对于ego而言的位置- rotation则是用4float表示旋转,用的是四元数- carmera 还多了一个camera_intrinsic 相机的内参(3d->2d平面)- 相机畸变参数(目前nuScenes数据集不考虑)
  1. 通过 lidar_calibrated_data = nuscenes.get(&quot;calibrated_sensor&quot;, lidar_sample_data[&quot;calibrated_sensor_token&quot;]) 类似的语句,就能拿到传感器的标定

3.3.1 LIDAR calibrated_sensor

  • 主要完成 lidar_points -> ego_pose0 -> global
拿到 lidar 相对 ego 的标定数据

通过 lidar_calibrated_data = nuscenes.get("calibrated_sensor", lidar_sample_data["calibrated_sensor_token"]) 拿到 lidar 的标定数据。

在这里插入图片描述

标定数据 rotation 四元数 转 旋 转矩阵的过程。
'''
# lidar_calibrated_data = nuscenes.get("calibrated_sensor", lidar_sample_data["calibrated_sensor_token"])
# calibrated_sensor 中的rotation属性使用四元数
lidar_calibrated_data["rotation"] ----》[0.7077955119163518, -0.006492242056004365, 0.010646214713995808, -0.7063073142877817]Quaternion(lidar_calibrated_data["rotation"]).rotation_matrix- 使用四元数转换成旋转矩阵,如下:[[ 0.00203327  0.99970406  0.02424172][-0.99998053  0.00217566 -0.00584864][-0.00589965 -0.02422936  0.99968902]]
'''
旋转矩阵平移矩阵合并

理解lidar 旋转矩阵 与 平移矩阵 合并

  • 旋转矩阵是 3*3 与 平移矩阵
  • 合并就是创建一个4*4 的对角矩阵,把前三行前三列替换为旋转矩阵。前三行第四列替换为
  • 二维数据的矩阵一般22就搞定,旋转和平移合并就变成33.这里用二维,简单理解3维为啥要变成4*4。 [ s c a l e 0 x 0 s c a l e y 0 0 1 ] [ x 1 y 1 1 ] = [ x 2 y 2 1 ] \begin{bmatrix} scale&0&x\\ 0&scale&y \\ 0&0&1\end{bmatrix} \begin{bmatrix} x1\\ y1 \\ 1\end{bmatrix} = \begin{bmatrix} x2\\ y2 \\ 1\end{bmatrix} scale000scale0xy1 x1y11 = x2y21
  • 代码
def get_matrix(calibrated_data):output = np.eye(4)output[:3, :3] = Quaternion(calibrated_data["rotation"]).rotation_matrixoutput[:3, 3] = calibrated_data["translation"]return outputlidar_calibrated_data = nuscenes.get("calibrated_sensor", lidar_sample_data["calibrated_sensor_token"])
# 6.1.2 
# lidar_to_ego_matrix 是基于ego而言的。
# point = lidar_to_ego_matrix @ lidar_points.T   代表了lidar -> ego 的过程。
lidar_to_ego_matrix = get_matrix(lidar_calibrated_data)
print(lidar_to_ego_matrix) 
'''
[[ 0.00203327  0.99970406  0.02424172  0.943713  ][-0.99998053  0.00217566 -0.00584864  0.        ][-0.00589965 -0.02422936  0.99968902  1.84023   ][ 0.          0.          0.          1.        ]]
'''- $$\begin{bmatrix} scale_1&scale_2&scale_3&x\\ scale_4&scale_5&scale_6&y \\ scale_7&scale_8&scale_9&z \\
0&0&0&1\end{bmatrix}  \begin{bmatrix} x1\\ y1 \\ z1 \\1\end{bmatrix} = \begin{bmatrix} x2\\ y2 \\z2\\ 1\end{bmatrix} $$lidar_points.T 是方便矩阵乘法
[图片]#### 得到 lidar_to_ego_matrix```python
lidar_calibrated_data = nuscenes.get("calibrated_sensor", lidar_sample_data["calibrated_sensor_token"])
lidar_to_ego_matrix = get_matrix(lidar_calibrated_data)
print(lidar_to_ego_matrix) 
'''
[[ 0.00203327  0.99970406  0.02424172  0.943713  ][-0.99998053  0.00217566 -0.00584864  0.        ][-0.00589965 -0.02422936  0.99968902  1.84023   ][ 0.          0.          0.          1.        ]]
拿到 ego 相对于 global 的标定
ego_pose_data0 = nuscenes.get("ego_pose", lidar_sample_data["ego_pose_token"])
# print(ego_pose_data0)
'''
{'token': '9d9bf11fb0e144c8b446d54a8a00184f', 
'timestamp': 1532402927647951, 
'rotation': [0.5720320396729045, -0.0016977771610471074, 0.011798001930183783, -0.8201446642457809], 
'translation': [411.3039349319818, 1180.8903791765097, 0.0]}
'''
拿到 ego_to_global_matrix
go_to_global_matrix = get_matrix(ego_pose_data0)
print(ego_to_global_matrix)
'''
[[-3.45552926e-01  9.38257989e-01  1.62825160e-02  4.11303935e+02][-9.38338111e-01 -3.45280305e-01 -1.74097708e-02  1.18089038e+03][-1.07128245e-02 -2.12945025e-02  9.99715849e-01  0.00000000e+00][ 0.00000000e+00  0.00000000e+00  0.00000000e+00  1.00000000e+00]]
'''
具体变换
lidar_to_global_matrix = ego_to_global_matrix @ lidar_to_ego_matrix # lidar-ego-global
global_points = lidar_point @ lidar_to_global_matrix.T # 真正变换点
代码
def get_matrix(calibrated_data, inverse=False):"""args:@calibrated_data : calibrated_sensor对象。一般通过nuscenes.get("calibrated_sensor",token..)得到@inverse : 是否取逆矩阵。具体根据calibrated_sensor对象里面的 rotation 与 translation 计算出一个4*4的旋转平移矩阵。如果inverse设置为ture。则对这个矩阵逆变换"""output = np.eye(4)output[:3, :3] = Quaternion(calibrated_data["rotation"]).rotation_matrixoutput[:3, 3] = calibrated_data["translation"]if inverse:output = np.linalg.inv(output)return outputlidar_calibrated_data = nuscenes.get("calibrated_sensor", lidar_sample_data["calibrated_sensor_token"])# 6.1.2 
# lidar_to_ego_matrix 是基于ego而言的。
# point = lidar_to_ego_matrix @ lidar_points.T   代表了lidar -> ego 的过程。
lidar_to_ego_matrix = get_matrix(lidar_calibrated_data)
print(lidar_to_ego_matrix) 
'''
[[ 0.00203327  0.99970406  0.02424172  0.943713  ][-0.99998053  0.00217566 -0.00584864  0.        ][-0.00589965 -0.02422936  0.99968902  1.84023   ][ 0.          0.          0.          1.        ]]
'''# 6.2 timestamp = t0 时的ego_pose0 -> global
# ego_to_global_matrix 是基于ego而言的。
# point = ego_to_global_matrix @ lidar_points.T
ego_pose_data0 = nuscenes.get("ego_pose", lidar_sample_data["ego_pose_token"])
# print(ego_pose_data0)
'''
{'token': '9d9bf11fb0e144c8b446d54a8a00184f', 
'timestamp': 1532402927647951, 
'rotation': [0.5720320396729045, -0.0016977771610471074, 0.011798001930183783, -0.8201446642457809], 
'translation': [411.3039349319818, 1180.8903791765097, 0.0]}
'''
ego_to_global_matrix = get_matrix(ego_pose_data0)
print(ego_to_global_matrix)
'''
[[-3.45552926e-01  9.38257989e-01  1.62825160e-02  4.11303935e+02][-9.38338111e-01 -3.45280305e-01 -1.74097708e-02  1.18089038e+03][-1.07128245e-02 -2.12945025e-02  9.99715849e-01  0.00000000e+00][ 0.00000000e+00  0.00000000e+00  0.00000000e+00  1.00000000e+00]]
'''

3.3.2 相机 camera 的标定

  • 主要完成 global -> ego_pose1 -> camera -> intrinsic -> image
  • camera 有很多,要循环处理
  • 相机标定注意点。求得的变换矩阵后,求逆矩阵
拿到 camera 相对 ego 的标定数据

通过 lidar_calibrated_data = nuscenes.get("calibrated_sensor", lidar_sample_data["calibrated_sensor_token"]) 拿到 lidar 的标定数据。

  • 代码

camera_token = sample["data"][cam]
camera_data  = nuscenes.get("sample_data", camera_token)image_file = os.path.join(dataroot, camera_data["filename"])
image = cv2.imread(image_file)camera_calibrated_token = camera_data["calibrated_sensor_token"]
camera_calibrated_data = nuscenes.get("calibrated_sensor", camera_calibrated_token)
'''camera多了camera_intrinsic 所以写下来。
{'token': '75ad8e2a8a3f4594a13db2398430d097', 
'sensor_token': 'ec4b5d41840a509984f7ec36419d4c09', 
'translation': [1.52387798135, 0.494631336551, 1.50932822144], 
'rotation': [0.6757265034669446, -0.6736266522251881, 0.21214015046209478, -0.21122827103904068], 
'camera_intrinsic': [[1272.5979470598488, 0.0, 826.6154927353808], [0.0, 1272.5979470598488, 479.75165386361925], [0.0, 0.0, 1.0]]}
得到camera_to_ego_matrix,然后取逆,得到ego_to_camera_matrix
ego_to_camera_matrix = get_matrix(camera_calibrated_data, True) # 不加True得到的是camera_to_ego_matrix
拿到 ego 相对于 global 的标定
camera_ego_pose = nuscenes.get("ego_pose", camera_data["ego_pose_token"])
拿到 ego_to_global_matrix,然后取逆,得到 global_to_ego_matrix
global_to_ego_matrix = get_matrix(camera_ego_pose, True) # 不加True得到的是ego_to_global_matrix
相比 lidar,新增相机内参矩阵
## 7.2 新增步骤,处理camera_intrinsiccamera_intrinsic = np.eye(4)camera_intrinsic[:3, :3] = camera_calibrated_data["camera_intrinsic"] # shape= 【3 * 3】
具体变换
global_to_image = camera_intrinsic @ ego_to_camera_matrix @ global_to_ego_matrix

3.3.3 画框

for token in sample["anns"]:annotation = nuscenes.get("sample_annotation", token)box = Box(annotation["translation"], annotation['size'], Quaternion(annotation["rotation"]))corners = box.corners().T # box.corners()形状是[3, 8]global_corners = np.concatenate((corners, np.ones((len(corners), 1))), axis=1)image_base_corners = global_corners @ global_to_image.Timage_base_corners[:, :2] /= image_base_corners[:, [2]]image_base_corners = image_base_corners.astype(np.int32)ix, iy = [0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7], [4, 5, 6, 7, 1, 2, 3, 0, 5, 6, 7, 4]for p0, p1 in zip(image_base_corners[ix], image_base_corners[iy]):if p0[2] <= 0 or p1[2] <= 0: continuecv2.line(image, (p0[0], p0[1]), (p1[0], p1[1]), (0, 255, 0), 2, 16)'''   循环表示,画0 与 4 的边   画1 与 5 的边   画2 与 6 的边 画3 与 7 的边 画0 与 1 的边   画1 与 2 的边   画2 与 3 的边 画3 与 0 的边 画4 与 5 的边   画5 与 6 的边   画6 与 7 的边 画7 与 4 的边 0 ------ 1/ |     /  |4 ------ 5   ||   3 ---|-- 2    |  /     | /7 ------ 6'''
可视化

nuscenes.render_annotation(token, out_path="./234")

在这里插入图片描述

相关文章:

Nuscenes数据集点云数据如何转换到图像上

零、概要 注意&#xff1a;该文章是手写ai自动驾驶&#xff0c;Nuscenes数据集的笔记。 首先&#xff0c;学习需要使用到 nuScenes 数据集。python 工具需要使用到 nuscenes-devkit、pyquaternion from nuscenes.nuscenes import NuScenes from pyquaternion import Quatern…...

【C语言期末】商品管理系统

本文资源&#xff1a;https://download.csdn.net/download/weixin_47040861/88820155 1.题目要求 商品管理系统 商品信息包括&#xff1a;包括编号、类别、名称、价格、折扣比例、生产时间 、存货数量等要求&#xff1a;1、信息首先保存在文件中&#xff0c;然后打开文件进行…...

单片机学习笔记---串口通信(2)

目录 串口内部结构 串口相关寄存器 串口控制寄存器SCON SM0和SM1 SM2 REN TB8和RB8 TI和RI 电源控制寄存器PCON SMOD 串口工作方式 方式0 方式0输出&#xff1a; 方式0输入 方式1 方式1输出。 方式1输入 方式2和方式3 方式2和方式3输出&#xff1a; 方式2和…...

【Java】乐观锁有哪些常见实现方式?

Java中的乐观锁主要有两种常见的实现方式&#xff1a; CAS&#xff08;Compare and Swap&#xff09;&#xff1a;这是实现乐观锁的核心算法。CAS操作包含三个参数&#xff1a;内存地址V、旧的预期值A和要修改的新值B。执行CAS操作时&#xff0c;会先比较内存地址V中的值是否等…...

Javaweb之SpringBootWeb案例之登录校验功能的详细解析

2. 登录校验 2.1 问题分析 我们已经完成了基础登录功能的开发与测试&#xff0c;在我们登录成功后就可以进入到后台管理系统中进行数据的操作。 但是当我们在浏览器中新的页面上输入地址&#xff1a;http://localhost:9528/#/system/dept&#xff0c;发现没有登录仍然可以进…...

CSS之盒模型

盒模型概念 浏览器盒模型&#xff08;Box Model&#xff09;是CSS中的基本概念&#xff0c;它描述了元素在布局过程中如何占据空间。盒模型由内容&#xff08;content&#xff09;、内边距&#xff08;padding&#xff09;、边框&#xff08;border&#xff09;、和外边距&…...

博客系统-SpringBoot版本

相比于之前使用Servlet来完成的博客系统&#xff0c;SpringBoot版本的博客系统功能更完善&#xff0c;使用到的技术更接近企业级&#xff0c;快来看看吧~ 目录 1.项目介绍 2.数据库准备 3.实体化类 4.返回格式 5.登录和注册功能 6.登出&#xff08;注销&#xff09;功能…...

详细分析Redis中数值乱码的根本原因以及解决方式

目录 前言1. 问题所示2. 原理分析3. 拓展 前言 对于这方面的相关知识推荐阅读&#xff1a; Redis框架从入门到学精&#xff08;全&#xff09;Java关于RedisTemplate的使用分析 附代码java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; …...

网络专栏目录

大家好我是苏麟 , 这是网络专栏目录 . 图解网络 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 图解网络目录 基础篇 基础篇 TCP/IP网络模型有几层? : TCP/IP网络模型 键入网址到页面显示,期间发生了什么? : 键入网址到页面显示,期间发生了什么 现阶…...

【Python网络编程之Ping命令的实现】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python开发技术 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; Python网络编程之Ping命令的实现 代码见资源&#xff0c;效果图如下一、实验要求二、协议原理2…...

OpenHarmony轻量级驱动开发

OpenHarmony轻量级驱动开发 思维导图: https://download.csdn.net/download/lanlingxueyu/88817155 GPlO(General-purpose input/output)即通用型输入输出 描述 GPlO(General-purpose input/output)即通用型输入输出。通俗地说,GPlO口就是一些引脚可以通过它们输出高低…...

C语言如何输⼊字符数组?

一、问题 在程序中&#xff0c;scanf()函数可以输⼊任意类型的数据&#xff0c;gets()函数只能输⼊字符串等&#xff0c;但是如何更好地输⼊字符数组呢&#xff1f; 二、解答 我们知道如何使⽤格式输⼊函数 scanf()&#xff0c;那么可以使⽤%c 格式符逐个输⼊字符。这样输⼊有…...

人脸追踪案例及机器学习认识

1.人脸追踪机器人初制 用程序控制舵机运动的方法与机械臂项目完全相同。 由于摄像头的安装方式为上下倒转安装&#xff0c;我们在编写程序读取图像时需使用 flip 函数将 图像上下翻转。 现在&#xff0c;只需要使用哈尔特征检测得到人脸在图像中的位置&#xff0c;再指示舵机运…...

鸿蒙开发理论之页面和自定义组件生命周期

1、自定义组件和页面的关系 页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&#xff0c;Entry装饰的自定义组件为页面的入口组件&#xff0c;即页面的根节点&#xff0c;一个页面有且仅能有一个Entry。只有被Entry装饰的组件才可以调用页面的生命周期。自…...

docker-compose部署gitlab和jenkins

通过docker-compose部署gitlab和jenkins&#xff0c;方便后续工作 注意&#xff1a; gitlab占用资源较多&#xff0c;最好系统内存在8G以上&#xff0c;CPU4核心以上&#xff0c;否则gitlab有可能报错无法启动。docker版本用最新版本&#xff0c;低版本的docker可能会导致doc…...

Pytorch 复习总结 1

Pytorch 复习总结&#xff0c;仅供笔者使用&#xff0c;参考教材&#xff1a; 《动手学深度学习》 本文主要内容为&#xff1a;Pytorch 张量的常见运算、线性代数、高等数学、概率论。 Pytorch 张量的常见运算、线性代数、高等数学、概率论 部分 见 Pytorch 复习总结 1&…...

谷歌免费开放模糊测试框架OSS-Fuzz(物联网、车联网、供应链安全、C/C++)

目录 模糊测试的智能化和自动化 模糊测试不能代替安全设计原则 AI驱动的漏洞修补...

华为配置内部人员接入WLAN网络示例(802.1X认证)

配置内部人员接入WLAN网络示例&#xff08;802.1X认证&#xff09; 组网图形 图1 配置802.1X认证组网图 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件 业务需求 用户接入WLAN网络&#xff0c;使用802.1X客户端进行认证&#xff0c;输入正确的用户名和密…...

EXCEL中如何调出“数据分析”的菜单

今天发现&#xff0c;原来WPS还是和EXCEL比&#xff0c;还是少了“数据分析”这个日常基本做统计的菜单&#xff0c;只好用EXCEL了&#xff0c;但奇怪发现我的EXCEL中没发现这个菜单&#xff0c;然后查了下&#xff0c;才发现&#xff0c;要用如下的方法打开&#xff1a; 1&…...

基于Qt的人脸识别项目(功能:颜值检测,口罩检测,表情检测,性别检测,年龄预测等)

完整代码链接在文章末尾 效果展示 代码讲解(待更新) qt图片文件上传 #include <QtWidgets> #include <QFileDialog>...

书生谱语-大语言模型测试demo

课程内容简介 通用环境配置 开发机 InterStudio 配置公钥 在本地机器上打开 Power Shell 终端。在终端中&#xff0c;运行以下命令来生成 SSH 密钥对&#xff1a; ssh-keygen -t rsa您将被提示选择密钥文件的保存位置&#xff0c;默认情况下是在 ~/.ssh/ 目录中。按 Enter …...

2024-02-12 Unity 编辑器开发之编辑器拓展3 —— EditorGUI

文章目录 1 GUILayout2 EditorGUI 介绍3 文本、层级、标签、颜色拾取3.1 LabelField3.2 LayerField3.3 TagField3.4 ColorField3.5 代码示例 4 枚举选择、整数选择、按下按钮4.1 EnumPopup / EnumFlagsField4.2 IntPopup4.3 DropdownButton4.4 代码示例 5 对象关联、各类型输入…...

shell脚本编译与解析

文章目录 shell变量全局变量&#xff08;环境变量&#xff09;局部变量设置PATH 环境变量修改变量属性 启动文件环境变量持久化 ./和. 的区别脚本编写判断 和循环命令行参数传入参数循环读取命令行参数获取用户输入 处理选项处理简单选项处理带值选项 重定向显示并且同时输出到…...

第64讲个人中心用户操作菜单实现

静态页面 <!-- 用户操作菜单开始 --><view class"user_menu"><!-- 订单管理开始 --><view class"order_wrap"><view class"order_title">我的订单</view><view class"order_content"><n…...

线性代数的本质——1 向量

向量是线性代数中最为基础的概念。 何为向量&#xff1f; 从物理上看&#xff0c; 向量就是既有大小又有方向的量&#xff0c;只要这两者一定&#xff0c;就可以在空间中随便移动。 从计算机应用的角度看&#xff0c;向量和列表很接近&#xff0c;可以用来描述某对象的几个不同…...

工业以太网交换机引领现代工厂自动化新潮流

随着科技的飞速发展&#xff0c;现代工厂正迎来一场前所未有的自动化变革&#xff0c;而工业以太网交换机的崭新角色正是这场变革的关键组成部分。本文将深入探讨工业以太网交换机与现代工厂自动化的紧密集成&#xff0c;探讨这一集成如何推动工业生产的智能化、效率提升以及未…...

Linux第46步_通过“添加自定义菜单”来学习menuconfig图形化配置原理

通过“添加自定义菜单”来学习menuconfig图形化配置原理&#xff0c;将来移植linux要用到。 自定义菜单要求如下: ①、在主界面中添加一个名为“My test menu”&#xff0c;此菜单内部有一个配置项。 ②、配置项为“MY TESTCONFIG”&#xff0c;此配置项处于菜单“My test m…...

推荐高端资源素材图库下载平台整站源码

推荐高端图库素材下载站的响应式模板和完整的整站源码&#xff0c;适用于娱乐网资源网。该模板支持移动端&#xff0c;并集成了支付宝接口。 演示地 址 &#xff1a; runruncode.com/tupiao/19692.html 页面设计精美&#xff0c;不亚于大型网站的美工水准&#xff0c;并且用户…...

Redis实现:每个进程每30秒执行一次任务

前言 项目中要实现每一进程每30秒执行一次 代码实现: public class DistributedScheduler {private final RRedisClient redisson;private final String processKeyPrefix; // 例如 "process_"public DistributedScheduler(RRedisClient redisson) {this.redisson…...

【AI之路】使用RWKV-Runner启动大模型,彻底实现大模型自由

文章目录 前言一、RWKV-Runner是什么&#xff1f;RWKV-Runner是一个大语言模型的启动平台RWKV-Runner官方功能介绍 二、使用步骤1. 下载文件 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; ChatGPT的横空出世&#xff0c;打开了AI的大门&#xff…...

Dockerfile和.gitlab-ci.yml文件模板

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…...

Linux--基础开发工具篇(2)(vim)(配置白名单sudo)

目录 前言 1. vim 1.1vim的基本概念 1.2vim的基本操作 1.3vim命令模式命令集 1.4vim底行命令 1.5 异常问题 1.6 批量注释和批量去注释 1.7解决普通用户无法sudo的问题 1.8简单vim配置 前言 在前面我们学习了yum&#xff0c;也就是Linux系统的应用商店 Linux--基础开…...

Learn LaTeX 017 - LaTex Multicolumn 分栏

在科学排版中进行分栏操作&#xff0c;能够有效的利用页面中的空间&#xff0c;避免空白位置的浪费。 好的分栏设计能对你的排版增色不少&#xff01; https://www.ixigua.com/7298100920137548288?id7307237715659981346&logTag949adb699806392430bb...

Android 9.0 禁用adb install 安装app功能

1.前言 在9.0的系统产品定制化开发中,在进行一些定制开发中,对于一些app需要通过属性来控制禁止安装,比如adb install也不允许安装,所以就需要 熟悉adb install的安装流程,然后来禁用adb install安装功能,接下来分析下adb 下的安装流程 2.禁用adb install 安装app功能的…...

华为第二批难题五:AI技术提升六面体网格生成自动化问题

有CAE开发商问及OCCT几何内核的网格方面的技术问题。其实&#xff0c;OCCT几何内核的现有网格生成能力比较弱。 HybridOctree_Hex的源代码&#xff0c;还没有仔细去学习。 “HybridOctree_Hex”的开发者说&#xff1a;六面体网格主要是用在数值模拟领域的&#xff0c;比如汽车…...

【FFmpeg】ffplay 命令行参数 ⑤ ( 设置音频滤镜 -af 参数 | 设置统计信息 -stats 参数 | 设置同步时钟类型 -sync 参数 )

文章目录 一、ffplay 命令行参数 - 音频滤镜1、设置音频滤镜 -af 参数2、常用的 音频滤镜 参数3、音频滤镜链 示例 二、ffplay 命令行参数 - 统计信息1、设置统计信息 -stats 参数2、关闭统计信息 -nostats 参数 三、ffplay 命令行参数 - 同步时钟类型1、设置同步时钟类型 -syn…...

vscode开发FPGA(0)--windows平台搭建

一、从官网下载安装VScode Download Visual Studio Code - Mac, Linux, Windows 二、安装配置插件 1. 安装Chinese&#xff08;simplified&#xff09;中文汉化包 2.安装Verilog-HDL/systemVerilog插件(支持verilog语法) 3.配置CTags Support插件(支持代码跳转) 1)在github下…...

Java String源码剖析+面试题整理

由于字符串操作是计算机程序中最常见的操作之一&#xff0c;在面试中也是经常出现。本文从基本用法出发逐步深入剖析String的结构和性质&#xff0c;并结合面试题来帮助理解。 String基本用法 在Java中String的创建可以直接像基本类型一样定义&#xff0c;也可以new一个 Str…...

探索未来:集成存储器计算(IMC)与深度神经网络(DNN)的机遇与挑战

开篇部分&#xff1a;人工智能、深度神经网络与内存计算的交汇 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为科技领域的一股强大力量&#xff0c;而深度神经网络&#xff08;DNN&#xff09;则是AI的核心引擎之一。DNN是一种模仿人类神经系统运作…...

[C/C++] -- CMake使用

CMake&#xff08;Cross-platform Make&#xff09;是一个开源的跨平台构建工具&#xff0c;用于自动生成用于不同操作系统和编译器的构建脚本。它可以简化项目的构建过程&#xff0c;使得开发人员能够更方便地管理代码、依赖项和构建设置。 CMake 使用一个名为 CMakeLists.tx…...

笔记本选购配置参数详解

笔记本电脑的选购是一个技术活&#xff0c;涉及到众多的配置参数。本文将为您详细解析笔记本电脑的主要配置参数&#xff0c;帮助您在选购时做出明智的决策。 1. 处理器&#xff08;CPU&#xff09; 处理器是笔记本电脑的核心组件&#xff0c;负责执行计算任务…...

临睡之际的生死思索与生命哲学的启示

在人类生存体验中&#xff0c;有一种独特而深邃的感受——当人们准备进入梦乡时&#xff0c;会担忧第二天醒来是否还能感知到生命的律动。这种“入睡即未知”的心理状态&#xff0c;既是生命无常的深刻体现&#xff0c;也是对个体生命价值、生活态度及人生哲学的一种深度拷问。…...

QT学习(五)C++函数重载

一、 函数重载 在同一个作用域内&#xff0c;可以声明几个功能类似的同名函数&#xff0c; 这些同名函数的形式参数&#xff08;指参数的个数、类型或者顺序&#xff09;必须不同。您不能仅通过返回类型的不同来 重载函数。 下面的实例中&#xff0c;同名函数 print() 被用…...

微服务OAuth 2.1扩展额外信息到JWT并解析(Spring Security 6)

文章目录 一、简介二、重写UserDetailsService三、Controller解析JWT获取用户信息四、后记 一、简介 VersionJava17SpringCloud2023.0.0SpringBoot3.2.1Spring Authorization Server1.2.1Spring Security6.2.1mysql8.2.0 Spring Authorization Server 使用JWT时&#xff0c;前…...

Python@setter用法介绍

Pythonsetter是Python编程语言中的一个关键属性&#xff0c;它简化了Python开发者的编程过程&#xff0c;提高了编程效率。 一、Pythonsetter是什么 Pythonsetter是Python语言中的一个属性&#xff0c;它允许程序员设置Python中的类成员变量。在Python中&#xff0c;属性&…...

格子表单GRID-FORM | 文档网站搭建(VitePress)与部署(Github Pages)

格子表单/GRID-FORM已在Github 开源&#xff0c;如能帮到您麻烦给个星&#x1f91d; GRID-FORM 系列文章 基于 VUE3 可视化低代码表单设计器嵌套表单与自定义脚本交互文档网站搭建&#xff08;VitePress&#xff09;与部署&#xff08;Github Pages&#xff09; 效果预览 格…...

mac无法往硬盘里存东西 Mac硬盘读不出来怎么办 Mac硬盘格式 硬盘检测工具

mac有时候会出现一些问题&#xff0c;比如无法往硬盘里存东西&#xff0c;或者无法往硬盘上拷贝文件。这些问题会给用户带来很大的困扰&#xff0c;影响正常的工作和学习。那么&#xff0c;mac无法往硬盘里存东西&#xff0c;mac无法往硬盘上拷贝怎么办呢&#xff1f;软妹子将为…...

DataX源码分析 reader

系列文章目录 一、DataX详解和架构介绍 二、DataX源码分析 JobContainer 三、DataX源码分析 TaskGroupContainer 四、DataX源码分析 TaskExecutor 五、DataX源码分析 reader 六、DataX源码分析 writer 七、DataX源码分析 Channel 文章目录 系列文章目录前言Reader组件如何处理…...

openssl3.2 - exp - RAND_bytes_ex

文章目录 openssl3.2 - exp - RAND_bytes_ex概述笔记END openssl3.2 - exp - RAND_bytes_ex 概述 生成随机数时, 要检查返回值是否成功, 不能认为一定是成功的(官方文档上有说明). 生成随机数的API, 和库上下文有关系, 使用RAND_bytes_ex()比RAND_bytes()好些. 笔记 /*! * …...

Oracle中怎么设置时区和系统时间

在Oracle数据库中&#xff0c;设置时区和系统时间可以通过多种方法实现。下面是一些常见的方法&#xff1a; 1. 设置数据库的时区 Oracle数据库允许你为每个会话或整个数据库设置时区。 a. 为整个数据库设置时区 你可以使用ALTER DATABASE语句为整个数据库设置时区。例如&a…...