ros中相机话题在web页面上的显示
思路:
rosbridge websocket 开启ros与web的通路,
话题数据转换为image或者绘制在 canvas中。
话题格式:
sensor_msgs/Image
测试数据编码类型为bgr8
尝试:
解析 为bitmap arraybuffer 写入bgr8,颜色错误
转换颜色通道arraybuffer 填到 canvas
直接绘制为canvas
直接 显示image
占用资源很高,显示不太流畅,待优化
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="js/three.min.js"></script>
<script src="js/eventemitter2.js"></script>
<script src="js/roslib.js"></script>
<script src="js/ros3d.js"></script>
<!--
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/eventemitter2.js"></script>
<script src="https://cdn.jsdelivr.net/npm/roslib@1/build/roslib.js"></script>
<script src="../build/ros3d.js"></script>
-->
<script>
/**
* Setup all visualization elements when the page is loaded.
*/
function init() {
// Connect to ROS.
var ros = new ROSLIB.Ros({
//url : 'ws://192.168.20.104:9090'
url : 'ws://192.168.10.168:9090'
});
ros.on('connection', function() {
console.log('Connected to websocket server.');
});
ros.on('error', function(error) {
console.log('Error connecting to websocket server: ', error);
});
ros.on('close', function() {
console.log('Connection to websocket server closed.');
});
var canvas = document.getElementById('img_canvas');
canvas.width = 800;
canvas.height = 600;
var image = new Image();
document.body.appendChild(image);
image.onload = function() {
var canvasWidth=canvas.width ;
var canvasHeight=canvas.height;
var imageWidth = image.width;
var imageHeight = image.height;
var imageAspectRatio = imageWidth / imageHeight;
var canvasAspectRatio = canvasWidth / canvasHeight;
var scaledWidth=1.0;
var scaledHeight=1.0;
if (imageAspectRatio > canvasAspectRatio) {
scaledWidth = canvasWidth;
scaledHeight = Math.round(scaledWidth / imageAspectRatio);
} else {
scaledHeight = canvasHeight;
scaledWidth = Math.round(scaledHeight * imageAspectRatio);
}
var x = (canvasWidth - scaledWidth) / 2;
var y = (canvasHeight - scaledHeight) / 2;
ctx = canvas.getContext('2d');
// 设置背景色并填充整个画布
ctx.fillStyle = "#f0f0f0"; // 浅灰色
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(image, x, y, scaledWidth, scaledHeight);
};
var example2 = ros.Topic({
name: '/usb_cam/image_raw',
messageType: 'sensor_msgs/Image'
});
var ccc=0
function generateBMP(width,height,channel) {
//var channel=3;
//const width = 100;
//const height = 100;
const fileSize = 54 + width * height * channel; // Header size + pixel data size
var buffer = new ArrayBuffer(fileSize);
const data = new DataView(buffer);
// BMP Header
data.setUint8(0, 0x42); // 'B'
data.setUint8(1, 0x4d); // 'M'
data.setUint32(2, fileSize, true); // file size
data.setUint32(6, 0, true); // reserved
data.setUint32(10, 54, true); // pixel data offset
// DIB Header
data.setUint32(14, 40, true); // DIB header size
data.setUint32(18, width, true); // width
data.setUint32(22, height, true); // height
data.setUint16(26, 1, true); // planes
data.setUint16(28, channel*8, true); // bits per pixel (32-bit)
data.setUint32(30, 0, true); // compression (none)
//data.setUint32(34, width * height * 4, true); // image size
data.setUint32(34, width * height * channel, true); // image size --------------zzz
data.setUint32(38, 2835, true); // horizontal resolution (72 DPI)
data.setUint32(42, 2835, true); // vertical resolution (72 DPI)
data.setUint32(46, 0, true); // colors in palette (none)
data.setUint32(50, 0, true); // important colors (all)
// Pixel data (simple gradient with transparency)
let offset = 54;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const red = (x / width) * 255;
const green = (y / height) * 255;
const blue = ((x + y) / (width + height)) * 255;
const alpha = (x / width) * 255; // Varying alpha
//for transparency
data.setUint8(offset++, blue); // blue
data.setUint8(offset++, green); // green
data.setUint8(offset++, red); // red
//data.setUint8(offset++, alpha); // alpha
}
}
const blob = new Blob([buf], { type: "image/bmp" });
image.src = URL.createObjectURL(blob);
return buffer
}
var buf=null;
example2.subscribe(function(message) {
ccc++;
if (ccc==1){
buf=generateBMP(message.width, message.height,3)
}
//console.log('Received image seq=%d', message['header']['seq']);
//AI???
//const image = new ROSLIB.Image(); // 创建 Image 对象实例
//image.data = new Uint8Array(message.data); // 设置图像数据
//image.width = message.width; // 设置图像宽度
//image.height = message.height; // 设置图像高度
//image.encoding = message.encoding; // 设置图像编码格式,例如 'rgb8' 或 'mono8' 等
//if (ccc%5!=1){return;}
let raw = window.atob(message.data);
let rawLength = raw.length;
//buf.set(raw, 54);
a=rawLength/3
b=new Uint8Array(buf)
/*
b.set(raw.split('').map(char => char.charCodeAt(0)), 54);
const blob = new Blob([b.buffer], { type: "image/bmp" });
image.src = URL.createObjectURL(blob);
//*/
/*
// 将base64字符串中的每个字符转换成ASCII码(字符编码值)
for (let i = 0; i < a; i++) {
b[54+i*3] = raw.charCodeAt(i*3+2);
b[54+i*3+2] = raw.charCodeAt(i*3);
b[54+i*3+1] = raw.charCodeAt(i*3+1);
}
const blob = new Blob([b.buffer], { type: "image/bmp" });
image.src = URL.createObjectURL(blob);
//*/
//*
var ctx = canvas.getContext('2d');
var imageData = ctx.createImageData(message.width, message.height); // 创建ImageData对象
//console.log('Received image', message.width, message.height,message.step, message.encoding);
// 创建一个Uint8Array类型的数组
let uInt8Array = new Uint8Array(rawLength/3*4);
// 将base64字符串中的每个字符转换成ASCII码(字符编码值)
for (let i = 0; i < a; i++) {
uInt8Array[i*4] = raw.charCodeAt(i*3);
uInt8Array[i*4+1] = raw.charCodeAt(i*3+1);
uInt8Array[i*4+2] = raw.charCodeAt(i*3+2);
uInt8Array[i*4+3] = 255;
}
imageData.data.set(uInt8Array); // 将图像数据转换为Uint8ClampedArray并设置到ImageData对象中
ctx.putImageData(imageData, 0, 0); // 将ImageData绘制到Canvas上
//*/
/*
//console.log('Received image', message.width, message.height,message.step, message.encoding);
var ctx = canvas.getContext('2d');
var imageData = ctx.createImageData(message.width, message.height); // 创建ImageData对象
// 将base64字符串中的每个字符转换成ASCII码(字符编码值)
for (let i = 0; i < a; i++) {
imageData.data[i*4] = raw.charCodeAt(i*3);
imageData.data[i*4+1] = raw.charCodeAt(i*3+1);
imageData.data[i*4+2] = raw.charCodeAt(i*3+2);
imageData.data[i*4+3] = 255;
}
ctx.putImageData(imageData, 0, 0); // 将ImageData绘制到Canvas上
//*/
});
}
</script>
</head>
<body onload="init()">
<h1>sensor_msgs/Image Example</h1>
<!--
<p>Run the following commands in the terminal then refresh the page.</p>
<ol>
<li><tt>roscore</tt></li>
<li><tt>roslaunch rosbridge_server rosbridge_websocket.launch</tt></li>
<li><tt>rosrun tf2_web_republisher tf2_web_republisher</tt></li>
<li><tt>roslaunch openni_launch openni.launch depth_registration:=true</tt></li>
</ol>
-->
<canvas id="img_canvas"> </canvas>
</body>
</html>