惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Latest news
Latest news
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
博客园 - 司徒正美
B
Blog RSS Feed
AWS News Blog
AWS News Blog
MyScale Blog
MyScale Blog
Scott Helme
Scott Helme
Cisco Talos Blog
Cisco Talos Blog
Last Week in AI
Last Week in AI
NISL@THU
NISL@THU
博客园 - Franky
P
Proofpoint News Feed
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
S
Schneier on Security
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
G
GRAHAM CLULEY
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
WordPress大学
WordPress大学
The Hacker News
The Hacker News
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Microsoft Azure Blog
Microsoft Azure Blog
T
The Exploit Database - CXSecurity.com
Engineering at Meta
Engineering at Meta
罗磊的独立博客
T
The Blog of Author Tim Ferriss
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
SecWiki News
SecWiki News
云风的 BLOG
云风的 BLOG
美团技术团队
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Security Latest
Security Latest
C
Cyber Attacks, Cyber Crime and Cyber Security
B
Blog
S
Security Affairs

博客园 - 流逝的轻风

单目相机 MATLAB 参数标定详解 nmcli修改ip地址 nginx 交叉编译报错问题解决(转) 图片转视频并推流源码,已通过测试 Hadoop的安装,图文并茂的安装过程 线程占用等待释放解决方案 ffmpeg重新编码示例 编译ffmpeg以支持rtmp, librtmp not found问题 瑞芯微rk3399实现硬编硬解编译rkmpp到ffmpeg Openc4.8QT中编译异常处理 yolov8模型转onnx MIPS编译opencv-自用 docker启动ubuntu,并映射端口 mips交叉编译相关库文件,主要做以后参考 OPENSSL1.1交叉编译编译异常处理 mips交叉编译ffmpeg 关于/proc/id/status中内容的描述 Linux GPIO控制方法 A7-NXP-6G2C修改分驱大小
3D点云到2D点云投影图像具体方法
流逝的轻风 · 2026-01-29 · via 博客园 - 流逝的轻风

由于工作需求,需要将三维雷达点云数据投影到二维平面上,以便进行后续的可视化分析或与其他2D传感器(如摄像头)的数据融合。为此,我整理并实现了完整的3D到2D投影代码,并在此记录整个过程,以备日后查阅与复用。

下图展示了投影后的效果:原始的3D点云经过坐标变换与视角映射后,成功呈现在2D图像平面上,保留了关键的空间结构信息,为后续的目标检测、场景理解等任务奠定了基础。

原始点云

41abe72d659552cf3aed37c18c1715a6

含强度信号转灰度图

image

纯绿色投影投影

image

增加颜色投影

 该实现充分考虑了雷达坐标系与图像坐标系之间的转换关系,支持灵活调整投影参数(如俯仰角、偏航角、视场角等),具有良好的通用性和可扩展性。

1. pcl库的安装

sudo apt install libpcl-dev

2. opencv库的安装

sudo apt-get install libopencv-dev

3. 准备摄像机的内参标定

我使用的是棋盘标定法,用matlab做的,具体参考我的另一篇博客: https://www.cnblogs.com/cqwo/p/19179297

image

相机内参标定

image

相机内参素材

 4. 相机雷达参数标定

image

标定参数图像

image

标定参数对应点云

具体步骤参数下面港大论文和公共的

参考论文:http://arxiv.org/abs/2103.01627
参考代码:https://github.com/hku-mars/livox_camera_calib

代码1():

#include <iostream>
#include <opencv2/opencv.hpp>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <algorithm> // for min_element, max_element

int main() {
    // 1. 读取带 intensity 的 PCD
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
    if (pcl::io::loadPCDFile<pcl::PointXYZI>("your_pointcloud.pcd", *cloud) == -1) {
        std::cerr << "❌ 无法加载 PCD 文件!" << std::endl;
        return -1;
    }

    // 2. 提取有效点(含 intensity)
    std::vector<pcl::PointXYZI> valid_points;
    for (const auto& pt : cloud->points) {
        if (std::isfinite(pt.x) && std::isfinite(pt.y) && std::isfinite(pt.z)) {
            valid_points.push_back(pt);
        }
    }

    if (valid_points.empty()) {
        std::cerr << "⚠️ 没有有效点!" << std::endl;
        return -1;
    }

    // 3. 构建 OpenCV 所需的 Point3f 列表(仅用于 projectPoints)
    std::vector<cv::Point3f> object_points;
    object_points.reserve(valid_points.size());
    for (const auto& pt : valid_points) {
        object_points.emplace_back(pt.x, pt.y, pt.z);
    }

    // 4. 相机内参(你的真实参数)
    cv::Mat cameraMatrix = (cv::Mat_<double>(3, 3) <<
        783.890724, 0.198476, 628.151868,
        0.0,        783.350933, 382.160525,
        0.0,        0.0,        1.0
    );

    // 5. 畸变系数(你的真实参数)
    cv::Mat distCoeffs = (cv::Mat_<double>(5, 1) <<
        -0.413786, 0.249752, 0.00262, -0.00224, 0.0
    );

    // 6. 外参 T_C^L(你的真实参数)
    Eigen::Matrix4f T_CL;
    T_CL << -0.013604, -0.999425, -0.031069, 0.075062,
            -0.024031,  0.031390, -0.999218, 0.037218,
             0.999619, -0.012847, -0.024444, -0.692299,
             0.0,       0.0,       0.0,       1.0;

    // 提取 R 和 t
    cv::Mat R_mat = (cv::Mat_<double>(3, 3) <<
        T_CL(0,0), T_CL(0,1), T_CL(0,2),
        T_CL(1,0), T_CL(1,1), T_CL(1,2),
        T_CL(2,0), T_CL(2,1), T_CL(2,2)
    );
    cv::Mat t_vec = (cv::Mat_<double>(3, 1) <<
        T_CL(0,3), T_CL(1,3), T_CL(2,3)
    );

    // 转为旋转向量
    cv::Mat rvec;
    cv::Rodrigues(R_mat, rvec);

    // 7. 投影
    std::vector<cv::Point2f> image_points;
    cv::projectPoints(object_points, rvec, t_vec, cameraMatrix, distCoeffs, image_points);

    // 8. 创建图像(根据你的相机分辨率)
    int img_width = 1280;   // 请确认你的图像宽度
    int img_height = 720;   // 请确认你的图像高度
    cv::Mat img = cv::Mat::zeros(img_height, img_width, CV_8UC3);

    // 9. 归一化 intensity 到 [0, 255]
    float min_i = std::numeric_limits<float>::max();
    float max_i = std::numeric_limits<float>::lowest();
    for (const auto& pt : valid_points) {
        if (pt.intensity < min_i) min_i = pt.intensity;
        if (pt.intensity > max_i) max_i = pt.intensity;
    }
    float range = max_i - min_i;
    if (range <= 0) range = 1.0f; // 防止除零

    // 10. 绘制点(灰度图)
    for (size_t i = 0; i < image_points.size(); ++i) {
        const cv::Point2f& p = image_points[i];
        if (p.x >= 0 && p.x < img_width && p.y >= 0 && p.y < img_height) {
            float norm_i = (valid_points[i].intensity - min_i) / range;
            int gray = static_cast<int>(norm_i * 255.0f);
            cv::circle(img, cv::Point(static_cast<int>(p.x), static_cast<int>(p.y)),
                       2, cv::Scalar(gray, gray, gray), -1);
        }
    }

    // 11. 保存结果
    std::string out_file = "projection_intensity.png";
    if (cv::imwrite(out_file, img)) {
        std::cout << "✅ 投影图像已保存为: " << out_file << std::endl;
        std::cout << "📊 Intensity 范围: [" << min_i << ", " << max_i << "]" << std::endl;
    } else {
        std::cerr << "❌ 无法保存图像!" << std::endl;
        return -1;
    }

    return 0;
}

 附件2: 带上色的图

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/point_cloud.h>
#include <opencv2/opencv.hpp>
#include <lidar/LdsLidar.h>
#include <thread>
#include <cmath>
#include <algorithm>

#include <pcl/common/transforms.h>
#include <pcl/common/common.h>
#include <iostream>
#include <chrono>
#include <pcl/point_cloud.h>


int main() {
    // 1. 读取带 intensity 的 PCD
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
    if (pcl::io::loadPCDFile<pcl::PointXYZI>("../Config/PCD/SLIDER.pcd", *cloud) == -1) {
        std::cerr << "❌ 无法加载 PCD 文件!" << std::endl;
        return -1;
    }

    // 2. 提取有效点(含 intensity)
    std::vector<pcl::PointXYZI> validPoints;
    for (const auto& pt : cloud->points) {
        if (std::isfinite(pt.x) && std::isfinite(pt.y) && std::isfinite(pt.z)) {
            validPoints.push_back(pt);
        }
    }

    if (validPoints.empty()) {
        std::cerr << "⚠️ 没有有效点!" << std::endl;
        return -1;
    }

    // 3. 构建 OpenCV 所需的 Point3f 列表(仅用于 projectPoints)
    std::vector<cv::Point3f> objectPoints;
    objectPoints.reserve(validPoints.size());
    for (const auto& pt : validPoints) {
        objectPoints.emplace_back(pt.x, pt.y, pt.z);
    }

    // 4. 相机内参(你的真实参数)
    cv::Mat cameraMatrix = (cv::Mat_<double>(3, 3) <<
                                                   783.890724, 0.198476, 628.151868,
            0.0,        783.350933, 382.160525,
            0.0,        0.0,        1.0
    );

    // 5. 畸变系数(你的真实参数)
    cv::Mat distCoeffs = (cv::Mat_<double>(5, 1) <<
                                                 -0.413786, 0.249752, 0.00262, -0.00224, 0.0
    );

    // 6. 外参 T_C^L(你的真实参数)
    Eigen::Matrix4f T_CL;
    T_CL << -0.013604, -0.999425, -0.031069, 0.075062,
            -0.024031,  0.031390, -0.999218, 0.037218,
            0.999619, -0.012847, -0.024444, -0.692299,
            0.0,       0.0,       0.0,       1.0;

    // 提取 R 和 t
    cv::Mat R_mat = (cv::Mat_<double>(3, 3) <<
                                            T_CL(0,0), T_CL(0,1), T_CL(0,2),
            T_CL(1,0), T_CL(1,1), T_CL(1,2),
            T_CL(2,0), T_CL(2,1), T_CL(2,2)
    );
    cv::Mat t_vec = (cv::Mat_<double>(3, 1) <<
                                            T_CL(0,3), T_CL(1,3), T_CL(2,3)
    );

    // 转为旋转向量
    cv::Mat rvec;
    cv::Rodrigues(R_mat, rvec);

    // 7. 投影
    std::vector<cv::Point2f> imagePoints;
    cv::projectPoints(objectPoints, rvec, t_vec, cameraMatrix, distCoeffs, imagePoints);

    // 8. 创建图像(根据你的相机分辨率)
    int imgWidth = 1280;   // 请确认你的图像宽度
    int imgHeight = 720;   // 请确认你的图像高度

    // 9. 归一化 intensity 到 [0, 255]
    float minI = std::numeric_limits<float>::max();
    float maxI = std::numeric_limits<float>::lowest();
    for (const auto& pt : validPoints) {
        if (pt.intensity < minI) minI = pt.intensity;
        if (pt.intensity > maxI) maxI = pt.intensity;
    }
    float range = maxI - minI;
    if (range <= 0) range = 1.0f; // 防止除零

    // === 9. 创建单通道灰度图 ===
    cv::Mat grayImg = cv::Mat::zeros(imgHeight, imgWidth, CV_8UC1);

    for (size_t i = 0; i < imagePoints.size(); ++i) {
        const cv::Point2f& p = imagePoints[i];
        int x = static_cast<int>(p.x + 0.5);
        int y = static_cast<int>(p.y + 0.5);

        if (x >= 0 && x < imgWidth && y >= 0 && y < imgHeight) {
            float norm_i = (validPoints[i].intensity - minI) / range;
            uint8_t val = static_cast<uint8_t>(std::min(255.0f, std::max(0.0f, norm_i * 255.0f)));

            // 可选:保留最强反射(最大 intensity)
            auto& current = grayImg.at<uint8_t>(y, x);
            if (val > current) {
                current = val;
            }
        }
    }
    // === 10. 应用 JET 伪彩色 ===
    cv::Mat colorImg;
    cv::applyColorMap(grayImg, colorImg, cv::COLORMAP_JET);

    // === 11. 将无点区域设为黑色(默认 JET 中 0=深蓝,我们改为黑)===
    cv::Mat mask = (grayImg == 0);
    colorImg.setTo(cv::Scalar(0, 0, 0), mask); // BGR 黑色

    // 12. 保存结果
    std::string outFile = "projection_color.png";
    if (cv::imwrite(outFile, colorImg)) {
        std::cout << "✅ 投影图像已保存为: " << outFile << std::endl;
        std::cout << "📊 Intensity 范围: [" << minI << ", " << maxI << "]" << std::endl;
    } else {
        std::cerr << "❌ 无法保存图像!" << std::endl;
        return -1;
    }

    return 0;
}