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

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
Cloudbric
Cloudbric
N
News and Events Feed by Topic
S
Secure Thoughts
Vercel News
Vercel News
S
Security @ Cisco Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
Hacker News: Ask HN
Hacker News: Ask HN
博客园 - 聂微东
WordPress大学
WordPress大学
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
PCI Perspectives
PCI Perspectives
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
Blog — PlanetScale
Blog — PlanetScale
Apple Machine Learning Research
Apple Machine Learning Research
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
博客园 - 司徒正美
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
P
Proofpoint News Feed
大猫的无限游戏
大猫的无限游戏
V
V2EX
I
Intezer
H
Hacker News: Front Page
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
Lohrmann on Cybersecurity
F
Fortinet All Blogs
Schneier on Security
Schneier on Security
博客园 - 叶小钗
The Cloudflare Blog
月光博客
月光博客
W
WeLiveSecurity
T
Tenable Blog
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
Help Net Security
Help Net Security
L
LangChain Blog
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
美团技术团队
B
Blog

博客园 - 流逝的轻风

单目相机 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;
}