从ROS Bridge解码ROS消息

4

如何在浏览器中解码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();
});

我已将这两个主题设置为在第一条消息后取消订阅,以便我的控制台不会被淹没。
提供了深度图像的控制台日志截图。

Screenshot of depth image output

并且对于点云

screenshot of pointcloud console log

这是我目前的代码,但是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位灰度图像。
如果我可以提供具体信息,请评论更新问题。
提前感谢。
1个回答

2
根据 libpng ,PNG 文件的起始标识是:
 89  50  4e  47  0d  0a  1a  0a

正如此处所指出的,签名位于标头之后(在您的情况下是几个初始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]
正如您所看到的,有一些非常深灰色的区域。

ROSBRIDGE depth image

对比版本

enter image description here

完整示例

这是一个完整的示例,它还显示了图片:

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中读取数值(编码为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);

这里有一个完整的例子(带源代码)。"解码图片"按钮将解码一个小区域的深度。

enter image description here


谢谢您的回答。迄今为止,这帮了我很多。我只是不确定您如何从中获取原始的16位数组。我尝试了一些方法,但最终总是得到uint8。也许我应该接受您的答案,因为我没有真正指定获取原始的16位数组。 - Paul Brink
@PaulBrink 很不幸,由于画布的8位RGB通道,你的灰色16位最终会被夹紧。画布的通道深度是不可配置的。但你可以尝试使用https://www.npmjs.com/package/pngjs(他们声称支持16位)。 - Newbie
我搞定了。关键是位移操作。 - Paul Brink
谢谢您的帮助。你能指点我解码点云消息的正确方向吗? - Paul Brink
@PaulBrink,我终于找到时间来回答你了。请查看已编辑的回答。 - Newbie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接