loadImage() with base64 encoded jpg

I am writing a program that gets an image encoded in base64 through the Serial port. I want to then load that image in my Processing program to analyze it.

It seems that loadImage() should work with base64 encoded data, provided you use the right header (reference), but even the gist used in the pull request throws a NullPointerException.

I have checked out the discussions i will post on a comment (this forum only lets new users post 2 links per post, for some reason) but nothing seems to work. I get either NullPointerException or IllegalArgumentException. Any help would be appreciated!

Here’s my code:

import processing.serial.*;

import java.awt.Image;
import java.awt.Toolkit;
import java.util.Base64;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

processing.serial.Serial myPort;
String val;
PImage feed;
PrintWriter debug;

void setup(){
  size(800, 800);
  //print(Serial.list());
  String portName = "COM3";
  
  debug = createWriter("debug.txt");
  
  myPort = new processing.serial.Serial(this, portName, 2000000);

}

void draw(){
  background(0);
  if(myPort.available() > 0){
    val = myPort.readStringUntil('\n');
    if(val != null){
      println("_______________________");
      debug.println(val.substring(873)); //Get rid of a bunch of characters prior to the base64 image
      val = val.substring(val.indexOf("/"));
      println(val.length());

      if(val.startsWith("/")){
        
        //This throws NullPointerException on image(feed, 0, 0)
          //String data = "data:image/jpg;base64,"+val;
          //feed = loadImage(data);
        
        try {
          feed = DecodePImageFromBase64(val);
        } 
        catch (IOException e) {
          println(e);
        }
        
        image(feed, 0, 0);
      }
    }
  }
}

public PImage DecodePImageFromBase64(String i_Image64) throws IOException
{
  PImage result = null;
  byte[] decodedBytes = Base64.getDecoder().decode(i_Image64);
  println("Base64");

  ByteArrayInputStream in = new ByteArrayInputStream(decodedBytes);
  BufferedImage bImageFromConvert = ImageIO.read(in);
  BufferedImage convertedImg = new BufferedImage(bImageFromConvert.getWidth(), bImageFromConvert.getHeight(), BufferedImage.TYPE_INT_ARGB);
  convertedImg.getGraphics().drawImage(bImageFromConvert, 0, 0, null);
  result = new PImage(convertedImg);

  return result;
}

void keyPressed() {
  debug.flush(); // Writes the remaining data to the file
  debug.close(); // Finishes the file
  exit(); // Stops the program
}

I know I am getting the right serial data because I get it printed to a .txt file, and converting it with this tool gives me a valid image.

Using the code above, I get one of two errors:

  1. IllegalArgumentException: Input byte array has incorrect ending byte at 145536
  2. IllegalArgumentException: Input byte array has wrong 4-byte ending unit

I will also post an example of an encoded image.

1 Like

Here are the discussions I checked out (1/3)
this discussion, this discussion

Hello @papertree,

Welcome to the forum.

Assuming the system silenced you automatically for enthusiasm and you will be back soon!

I simplified your code without the serial and this works on my end:

import java.awt.Image;
import java.awt.Toolkit;
import java.util.Base64;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

PImage feed;

String val = "data:image/jpeg;base64,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";

void setup() 
  {
  size(200, 200);
  noLoop();
  }

void draw() 
  {
  background(0);

  // Remove the "data:image/jpeg;base64," prefix (if exists)
  if (val.startsWith("data:image/jpeg;base64,"))
    {
    val = val.substring("data:image/jpeg;base64,".length());
    //println(base64Str);
    //println();
    }

  try 
    {
    feed = DecodePImageFromBase64(val);
    }
  catch (IOException e) 
    {
    println(e);
    }

  image(feed, 0, 0);
  }

public PImage DecodePImageFromBase64(String i_Image64) throws IOException
  {
  PImage result = null;
  byte[] decodedBytes = Base64.getDecoder().decode(i_Image64);
  println("Base64");

  ByteArrayInputStream in = new ByteArrayInputStream(decodedBytes);
  BufferedImage bImageFromConvert = ImageIO.read(in);
  BufferedImage convertedImg = new BufferedImage(bImageFromConvert.getWidth(), bImageFromConvert.getHeight(), BufferedImage.TYPE_INT_ARGB);
  convertedImg.getGraphics().drawImage(bImageFromConvert, 0, 0, null);
  result = new PImage(convertedImg); //Depracated but works

  return result;
  }

test

I used this to create the encoded string:

:)

2 Likes

Hi,
Since the Forum wouldn’t give me access again, and I am really hitting a wall with this, I had to create another account..

Your sketch works for me too, yay! I since discovered that, since I changed the code that took and encoded the images, the base64 strings I was receiving through the Serial port were missing some characters, which meant that they weren’t valid files anyway.

However, I am now getting 100% correct strings, and the encoder still doesn’t work. Here’s an example of a base64 encoded jpeg file, and here’s my current sketch:

import processing.serial.*;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.Base64;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

processing.serial.Serial myPort;

PImage feed = null;

String val = "";

void setup() 
  {
  size(320, 240);
  String portName = "COM3";
  myPort = new processing.serial.Serial(this, portName, 115200);

  
  //noLoop();
  }

void draw() 
  {
  background(0);
  if(myPort.available() > 0){
  
    val = myPort.readStringUntil('\n');
    if(val != null){
      println(val); //THIS prints a correct base64-encoded jpeg string
      
      // Remove the "data:image/jpeg;base64," prefix (if exists)
      if (val.startsWith("data:image/jpeg;base64,"))
        {
        val = val.substring("data:image/jpeg;base64,".length());
        }
    
      try 
        {
        feed = DecodePImageFromBase64(val);
        }
      catch (IOException e) 
        {
        println(e);
        }
    
      image(feed, 0, 0);
    }
  }
}

public PImage DecodePImageFromBase64(String i_Image64) throws IOException{
  PImage result = null;
  println(i_Image64);//THIS prints a correct base64-encoded jpeg string

  byte[] decodedBytes = Base64.getDecoder().decode(i_Image64); //IllegalArgumentException: Input byte array has incorrect ending byte at 6372. String is correct JPG
  println(decodedBytes.length);

  ByteArrayInputStream in = new ByteArrayInputStream(decodedBytes);
  BufferedImage bImageFromConvert = ImageIO.read(in);
  BufferedImage convertedImg = new BufferedImage(bImageFromConvert.getWidth(), bImageFromConvert.getHeight(), BufferedImage.TYPE_INT_ARGB);
  convertedImg.getGraphics().drawImage(bImageFromConvert, 0, 0, null);
  result = new PImage(convertedImg); //Depracated but works

  return result;
}

As you can see, I just edited your code to add the Serial reading. The println(val) before sending it to the DecodePImageFromBase64() function prints a valid base64-encoded jpeg string, and the println(i_Image64) prints the exact same string, but Base64.getDecoder().decode(i_image64) throws Input array has incorrect ending byte OR Illegal base64 character d (always d, the character never changes).

Could this be a problem of the decoder? Should I look into using another library?

Hi @papertree / @papertree2,

It looks like your account was mistakenly flagged by Discourse’s automatic spam detection. I have removed the flag on your original account so you should be able to log back in. Sorry for the inconvenience.

1 Like

Hello @papertree,

If this sends the terminating character it needs to be trimmed on receiving end:
https://docs.arduino.cc/language-reference/en/functions/communication/serial/println/

Reference:
trim() / Reference / Processing.org

Example:

Try while (myPort.available() > 0) in your loop:
readStringUntil() / Libraries / Processing.org
Although… it may be blocking for a large frame of data ending in a \n.

This uses virtual COM ports on PC and works:

import processing.serial.*;

import java.awt.Image;
import java.awt.Toolkit;
import java.util.Base64;
import java.io.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

processing.serial.Serial portTx;
processing.serial.Serial portRx;

String imgTxt = "data:image/jpeg;base64,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";
String val = "";

PImage feed = null;

void setup()
  {
  size(200, 200);
  println(imgTxt.length());
  //imgTxt = imgTxt + "\n";

  printArray(processing.serial.Serial.list());

  String comRx = "COM4";
  portRx = new processing.serial.Serial(this, comRx, 115200);
  portRx.clear();

  String comTx = "COM5";
  portTx = new processing.serial.Serial(this, comTx, 115200); // Fixed variable name

  //portRx.readStringUntil('\n');

  // Think of this as Arduino
  thread("txtImage");   // start thread to generate data;
  }

void draw()
  {
  //background(0);
  while (portRx.available() > 0)
    {
    //println(portRx.available());
    val = portRx.readStringUntil('\n');

    if (val != null)
      {
      val = val.trim();
      println("test");
      println(val); //THIS prints a correct base64-encoded jpeg string

      // Remove the "data:image/jpeg;base64," prefix (if exists)
      if (val.startsWith("data:image/jpeg;base64,"))
        {
        println("here", val.length());
        val = val.substring("data:image/jpeg;base64,".length());
        }
      try
        {
        feed = DecodePImageFromBase64(val);
        println("ok");
        }
      catch (IOException e)
        {
        println(e);
        }
      image(feed, 0, 0);  
      }
    }
  }

public PImage DecodePImageFromBase64(String i_Image64) throws IOException   {
  PImage result = null;
  println(i_Image64);//THIS prints a correct base64-encoded jpeg string

  byte[] decodedBytes = Base64.getDecoder().decode(i_Image64); //IllegalArgumentException: Input byte array has incorrect ending byte at 6372. String is correct JPG
  println(decodedBytes.length);

  ByteArrayInputStream in = new ByteArrayInputStream(decodedBytes);
  BufferedImage bImageFromConvert = ImageIO.read(in);
  BufferedImage convertedImg = new BufferedImage(bImageFromConvert.getWidth(), bImageFromConvert.getHeight(), BufferedImage.TYPE_INT_ARGB);
  convertedImg.getGraphics().drawImage(bImageFromConvert, 0, 0, null);
  result = new PImage(convertedImg); //Depracated but works

  return result;
  }

// Think of this as the Arduino
void txtImage()   {
  println("Thread 1");
  println(imgTxt.length());
  //while (true)
    {
    if (portTx != null)   {
      println("Thread 2");

      // Loop through every character in the Base64 string
      for (int i = 0; i < imgTxt.length(); i++)
        {
        portTx.write(imgTxt.charAt(i));
        //println(i, imgTxt.charAt(i));
        //delay(1); // 1ms delay per character to stabilize transmission
        }

      // Send the terminal newline so readStringUntil('\n') triggers
      portTx.write('\n');
      println("Finished sending full string.");
      }
    }
  }
``'
You will not be able to run above until you have virtual COM ports available.
The code works and displays the image.

Reference:
https://en.wikipedia.org/wiki/Virtual_COM_port

``:)``

@sableraph Thanks! Really appreciate it.

Hi @glv,

I had no idea the Arduino serial.println() method printed a \r at the end of the data.

Thanks for your trim() suggestion inside or Processing, everything works as expected now! I haven’t encountered the Illegal base64 character d again, either, so that was the issue there also.

Thanks a lot again!

2 Likes

You are very welcome!

:)

2 Likes