从 ROSBrdidge 解码 ROS 消息
Decoding ROS messages from ROSBrdidge
如何在浏览器中解码rosbridge数据?
到目前为止我已经能够解码以下类型:
- 未压缩的原始 RGB
- 未压缩的原始深度
- JPEG 压缩 RGB
我现在的问题是解码压缩深度和 PointCloud2 数据。据我了解,数据被编码为 base64。深度图像已压缩为 mono16 PNG。我尝试了许多不同的方法,但 none 似乎有效。深度图像应该包含 307200 个深度值,每个 16 位。
我不想在 ros3djs 或 webviz 之类的东西中显示这些数据(云不知道他们是如何进行解码的)。我想解码数据并将其用于我自己的分析。
重现步骤:
这是一个示例文件。它包含 JSON 消息的数据字段:https://drive.google.com/file/d/18ZPpWrH9TKtPBbevfGdceZVpmmkiP4bh/view?usp=sharing
或
- 确保您有设备发布或 rosbag 播放
- roslaunch rosbridge_server rosbridge_websocket.launch
- 启动您的网页并确保您在脚本标签中添加了 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 位灰度图像。
如果我可以用具体信息更新问题,请发表评论
提前致谢
根据 to libpng PNG 起始签名是
89 50 4e 47 0d 0a 1a 0a
正如 here 所指出的,签名出现在 header 之后(在您的情况下只有几个初始 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 不可用(至少我还没有找到它)作为编译的 browser-ready 库,所以你需要以某种方式打包你的 'website'(就像使用 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)
“解码图像”按钮将解码小区域的深度。
如何在浏览器中解码rosbridge数据?
到目前为止我已经能够解码以下类型:
- 未压缩的原始 RGB
- 未压缩的原始深度
- JPEG 压缩 RGB
我现在的问题是解码压缩深度和 PointCloud2 数据。据我了解,数据被编码为 base64。深度图像已压缩为 mono16 PNG。我尝试了许多不同的方法,但 none 似乎有效。深度图像应该包含 307200 个深度值,每个 16 位。
我不想在 ros3djs 或 webviz 之类的东西中显示这些数据(云不知道他们是如何进行解码的)。我想解码数据并将其用于我自己的分析。
重现步骤:
这是一个示例文件。它包含 JSON 消息的数据字段:https://drive.google.com/file/d/18ZPpWrH9TKtPBbevfGdceZVpmmkiP4bh/view?usp=sharing
或
- 确保您有设备发布或 rosbag 播放
- roslaunch rosbridge_server rosbridge_websocket.launch
- 启动您的网页并确保您在脚本标签中添加了 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 位灰度图像。
如果我可以用具体信息更新问题,请发表评论
提前致谢
根据 to libpng PNG 起始签名是
89 50 4e 47 0d 0a 1a 0a
正如 here 所指出的,签名出现在 header 之后(在您的情况下只有几个初始 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 不可用(至少我还没有找到它)作为编译的 browser-ready 库,所以你需要以某种方式打包你的 'website'(就像使用 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)
“解码图像”按钮将解码小区域的深度。