【问题标题】:Decoding ROS messages from ROSBrdidge从 ROSBrdidge 解码 ROS 消息
【发布时间】:2021-09-14 07:05:08
【问题描述】:

如何在浏览器中解码 rosbridge 数据?

到目前为止,我已经能够解码以下类型:

  • 未压缩的原始 RGB
  • 未压缩的原始深度
  • JPEG 压缩 RGB

我现在的问题是解码压缩深度和 PointCloud2 数据。据我了解,数据被编码为base64。深度图像已被压缩为 mono16 PNG。我尝试了许多不同的方法,但似乎都没有奏效。深度图像应该包含 307200 个深度值,每个 16 位。

我不想以 ros3djs 或 webviz 之类的方式显示这些数据(云不知道它们是如何进行解码的)。我想解码数据并在我自己的分析中使用它。

重现步骤:

这是一个示例文件。它包含 JSON 消息的数据字段:https://drive.google.com/file/d/18ZPpWrH9TKtPBbevfGdceZVpmmkiP4bh/view?usp=sharing

  1. 确保您有一个正在发布的设备或正在播放的 rosbag
  2. roslaunch rosbridge_server rosbridge_websocket.launch
  3. 启动您的网页并确保您在脚本标签中添加了 roslibjs

我网页的JS简化成这样:

var ros = new ROSLIB.Ros({
    url: 'ws://127.0.0.1:9090'
});


var depthListener = new ROSLIB.Topic({
    ros: ros,
    name: '/camera/color/image_raw/compressedDepth',
    messageType: 'sensor_msgs/CompressedImage'
});

var pointCloudListener = new ROSLIB.Topic({
    ros: ros,
    name: '/camera/depth/color/points',
    messageType: 'sensor_msgs/PointCloud2'
});



depthListener.subscribe(function (message) {
    console.log(message);
    depthListener.unsubscribe();
});

pointCloudListener.subscribe(function (message) {
    console.log(message);
    pointCloudListener.unsubscribe();
});

我已将两个主题设置为在第一条消息后取消订阅,这样我的控制台就会被淹没。

提供了深度图像的控制台日志截图

对于点云

这是我目前所拥有的,但从未触发过 onload 功能。

image = new Image();
    image.src = "data:image/png;base64, " + message.data

    image.onload = function(){
        image.decode().then(() =>{
            if(image.width != 0 && image.height != 0){
                canvas.width = image.width;
                canvas.height = image.height;
                ctx = canvas.getContext('2d');
                ctx.drawImage(image, 0, 0);
                image_data = canvas.getContext('2d').getImageData(0,0, 640,480).data;
            }
        });
    }

我认为this opencv 代码用于压缩图像。 该消息本质上可以被认为是一个 16 位灰度图像。

如果我可以用具体信息更新问题,请发表评论

感谢您

【问题讨论】:

    标签: javascript ros


    【解决方案1】:

    根据to libpng一个PNG起始签名是

     89  50  4e  47  0d  0a  1a  0a
    

    正如here 所指出的,签名出现在标头之后(在您的情况下,很少有初始00 字节)。这将解决您的问题:

    function extractPng(base64) {
      // const signature = '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a';
      const signature = '\x89PNG\r\n\x1a\n';
      let binary = window.atob(base64);
      let ix = binary.indexOf(signature);
      ix = ix > 0 ? ix : 0;
      return 'data:image/png;base64,' + window.btoa(binary.substring(ix));
    }
    

    你的图片

    [640 x 480]

    如您所见,有一些非常深的灰色区域

    对比版

    完整示例

    这是一个完整的示例,也可以显示图像:

    function openDataImage(data) {
      const image = new Image();
      image.src = data;
      const w = window.open("");
      w.document.write(image.outerHTML);
    }
    
    openDataImage(extractPng(`...`));
    

    完成解码

    不幸的是,<canvas> 有一个 8 位的固定色深,因此它不能用于访问您的 16 位灰度数据。我建议使用pngjs。 Pngjs 不可用(至少我还没有找到)作为已编译的浏览器就绪库,因此您需要以某种方式打包您的“网站”(例如使用 Webpack)。

    该函数需要将png二进制数据提取为Buffer

    function extractPngBinary(base64) {
      const signature = Buffer.from("\x89PNG\r\n\x1a\n", "ascii");
      let binary = Buffer.from(base64, "base64");
      let ix = binary.indexOf(signature);
      ix = ix > 0 ? ix : 0;
      return binary.slice(ix);
    }
    

    然后解码png:

    const PNG = require("pngjs").PNG;
    const png = PNG.sync.read(extractPngBinary(require("./img.b64")));
    

    要从 PNG 中读取值,然后 (encoding is BE):

    function getValueAt(png, x, y) {
      // Check is Monotchrome 16bit
      if (png.depth !== 16 || png.color || png.alpha) throw "Wrong PNG color profile";
      // Check position
      if (x < 0 || x > png.width || y < 0 || y > png.height) return undefined;
      // Read value and scale to [0...1]
      return (
        png.data.readUInt16BE((y * png.width + x) * (png.depth / 8)) /
        2 ** png.depth
      );
    }
    

    然后读取一个数据区域:

    function getRegion(png, x1, x2, y1, y2) {
      const out = [];
      for (let y = y1; y < y2; ++y) {
        const row = [];
        out.push(row);
        for (let x = x1; x < x2; ++x) {
          row.push(getValueAt(png, x, y));
        }
      }
      return out;
    }
    

    所有图片:

    getRegion(png, 0, png.width, 0, png.height);
    

    Here a complete example (with source code)

    “解码图像”按钮将对小区域的深度进行解码。

    【讨论】:

    • 感谢您的回答。到目前为止,这对我帮助很大。我只是不确定如何从中获得原始的 16 位数组。我已经尝试了一些东西,但总是以 uint8 告终。除了你的答案,我也没有真正指定要获取原始 16 位数组
    • @PaulBrink 不幸的是,在您的情况下,由于 Canvas 的 8 位 RGB 通道,灰色的 16 位最终被钳制。画布的通道深度不可配置。但是你可以试试这个npmjs.com/package/pngjs(他们说支持16位)
    • 我想通了。关键是位移
    • 感谢您的帮助。您能否指出我解码点云消息的正确方向?
    • @PaulBrink 我终于有时间回答你了。请参阅编辑后的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-12
    相关资源
    最近更新 更多