ros中相机话题在web页面上的显示,尝试js解析sensor_msgs/Image数据

发布于:2025-06-20 ⋅ 阅读:(17) ⋅ 点赞:(0)

ros中相机话题在web页面上的显示

思路:
rosbridge websocket 开启ros与web的通路,
话题数据转换为image或者绘制在 canvas中。

话题格式:
sensor_msgs/Image
测试数据编码类型为bgr8

尝试:

解析 为bitmap arraybuffer 写入bgr8,颜色错误
转换颜色通道arraybuffer 填到 canvas
直接绘制为canvas
直接 显示image

占用资源很高,显示不太流畅,待优化

<!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/three@0.89.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/eventemitter2@6.4/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>

网站公告

今日签到

点亮在社区的每一天
去签到