skip to Main Content

In my JS script I’m receiving string with encoded image.jpeg. The image is created with OpenCV in C++:

cv::imencode(".jpeg", rgb_img, jpeg_data);
std::string jpeg_string(jpeg_data.begin(), jpeg_data.end());

Then the string is passed through websocket.
In JS I have to decode that string and use as src of img tag. I tried (ChatGPT):

// Create a Uint8Array from the binary data
    var arrayBuffer = new ArrayBuffer(jpeg_string.length);
    var uint8Array = new Uint8Array(arrayBuffer);
    for (var i = 0; i < jpeg_string.length; i++) {
        uint8Array[i] = jpeg_string.charCodeAt(i);
    }

    // Create a Blob object from the ArrayBuffer
    var blob = new Blob([arrayBuffer], { type: 'image/jpeg' });

    // Create an Object URL for the Blob
    var imageDataUri = URL.createObjectURL(blob);
    img.src = imageDataUri;

and (Stack Overflow):

const imageDataUri = `data:image/jpeg;base64,${jpeg_string}`;
img.src = imageDataUri;

None of this works. In the first example:

src = blob:http://0.0.0.0:7000/<random numbers, lowercase letters and dashes>

in the other one:

src = data:image/jpeg;base64,<uppercase letters and numbers>

How to display that image?
I’m working with Robot Operating System, Ubuntu 22.4 (shouldn’t make a difference).

4

Answers


  1. Chosen as BEST ANSWER

    Solution: I used marked solution. To make it work I stole some code from other topic:

    static const std::string base64_chars =
                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                 "abcdefghijklmnopqrstuvwxyz"
                 "0123456789+/";
    
    
    static inline bool is_base64(unsigned char c) {
      return (isalnum(c) || (c == '+') || (c == '/'));
    }
    
    std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
      std::string ret;
      int i = 0;
      int j = 0;
      unsigned char char_array_3[3];
      unsigned char char_array_4[4];
    
      while (in_len--) {
        char_array_3[i++] = *(bytes_to_encode++);
        if (i == 3) {
          char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
          char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
          char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
          char_array_4[3] = char_array_3[2] & 0x3f;
    
          for(i = 0; (i <4) ; i++)
            ret += base64_chars[char_array_4[i]];
          i = 0;
        }
      }
    
      if (i)
      {
        for(j = i; j < 3; j++)
          char_array_3[j] = '';
    
        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;
    
        for (j = 0; (j < i + 1); j++)
          ret += base64_chars[char_array_4[j]];
    
        while((i++ < 3))
          ret += '=';
    
      }
    
      return ret;
    
    }
    

  2. I think this code below looks like it’s almost right:

    const imageDataUri = `data:image/jpeg;base64,${jpeg_string}`;
    img.src = imageDataUri;
    

    The problem appears to be that the server is not base64 encoding.

    Login or Signup to reply.
  3. This seems to be the correct way to encode your image:

    cv::imencode(".jpeg", rgb_img, jpeg_data);
    auto *enc_msg = reinterpret_cast<unsigned char*>(jpeg_data.data());
    std::string encoded = base64_encode(enc_msg, jpeg_data.size());
    

    Then, you can show your image using this:

    const imageDataUri = `data:image/jpeg;base64,${jpeg_string}`;
    img.src = imageDataUri;
    
    Login or Signup to reply.
  4. If you want to send a stream of images to a browser, a common and simple solution is to to declare multipart/x-mixed-replace MIME type in the HTTP response, then keep sending pictures from the server in the same HTTP response.

    This will require complex logic on the HTTP server side if you expect more than one client to read that video feed.

    If you’re already working with websockets, perhaps stick to that. It’s cleaner. The server side can deal with websockets in smarter ways.


    There is good reason to send the original binary instead of the base64 encoding because base64 blows the data up by a factor of 8/6, i.e. adding 33%. You could hope for any transparent gzip compression to catch that.

    There is no reason to base64-encode the binary data on the server. HTTP is happy to carry binary data. Clientside Javascript can turn the binary file into a base64-encoded data: URI.

    With the right APIs (createObjectURL from Blob or File?), the base64 encoding may not even be needed. The browser might just wrap the binary data in an "Object URL" that does not contain base64 at any point, yet is well digested by the HTML rendering. Base64 would be an implementation detail you need not think about.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search