16bit grayscale images

As I understand it right now Processing uses a 32bit value for each pixel in an image, devided into 8bits for each color plus an alpha channel.
However I would like load, modify and save grayscale images with a single 16bit channel. This type of image is often used as a heightmap. I attatched an image of the grand canyon as an example.
As of now I can load this image in processing, however I end up with the 3 collor channels containing identical 8bit values and so loose half of the 16bits I want to work with.

example

1 Like

How many images are we talking about?

I want to load 2 of those images and output one. However the size of my example picture is not representative, I want to work with images up to 4100x4100 pixels. Speed is not an issue by the way.

I had a poke at it and here is what I got so far.
I’m using a .raw format now which I actually have easier acess to.
What I display in the green channel is already quite promissing. I still need to figure out what to do with the stuff I display in red though.

int HeightmapWidth = 100;  //Width of the heightmap in pixels
byte[] Bytes;  //Where I read my file to
short[] Shorts;  //Hopeing to fill this with useful height data
color[] Colors;  //Using this for easier debuging

void setup() {
  size(100, 100);
  Bytes = loadBytes("heightmap.raw");  //loading file into my Bytes array
  Shorts = new short[Bytes.length / 2];  //initialising to the correct size
  Colors = new color[Bytes.length / 2];  //initialising to the correct size

  for ( int i = 0; i < Bytes.length; i=i+2) {
    Shorts[i/2] = (short)(Bytes[i] << 8 | Bytes[i+1]);  //not sure how the endianness works out yet
    Colors[i/2] = color(Bytes[i], Bytes[i+1], 0);  //just to visualise better
  }

  background(0);
  for ( int j = 0; j < Colors.length; j++) {
    set(j%HeightmapWidth, j/HeightmapWidth, Colors[j]);  //just to visualise better
  }
}

void draw() {
  //nothing to do here
}

Apparently I can’t attatch my heightmap.raw so I uploaded it somewhere else. Please let me know if there is a better way to do this: https://www.file-upload.net/download-13590186/heightmap.raw.html

And here is the result of my code:
result

1 Like

I think I actually got done what I wanted. Signed and unsigned datatypes had me tripped up.
So here is my code in case anyone searches for this.
For this example I just invert the height values of my heightmap.raw and save it as output.raw

int HeightmapWidth = 100;  //Width of the heightmap in pixels
char[] input;  //Where I read my file to
char[] chars;  //Hopeing to fill this with useful height data
byte[] output;  //I put my output in here

void setup() {
  size(100, 100);
  input = char(loadBytes("heightmap.raw"));  //loading file into my Bytes array
  chars = new char[input.length / 2];  //initialising to the correct size
    output = new byte[input.length];  //initialising to the correct size

  for ( int i = 0; i < input.length; i=i+2) {
    chars[i/2] = (char)(input[i+1] << 8 | input[i]);  //This endianess is the right for my application
  }

  background(0);

  for ( int j = 0; j < chars.length; j++) {
    chars[j] = char(65535-chars[j]);  //Inverting the height values just to try it out

    int gray8bit = int(map(chars[j], 0, 65535, 0, 255));  //turning it back into a regular color, just for display purposes
    color gray = color(gray8bit, gray8bit, gray8bit);
    set(j%HeightmapWidth, j/HeightmapWidth, gray);  //displaying my result (yes would be faster with the pixels[] array)
  }

  for ( int k = 0; k < input.length; k++) {
    if (k%2 == 1) {  //getting it back in the correct byte order
      output[k] = byte(chars[k/2]>>8);
    } else {
      output[k] = byte(chars[k/2]);
    }
  }
  saveBytes("output.raw", output);  //saving my output
}

void draw() {
  //nothing to do here
}

And here is the result. I turned into a .png for ease of posting:
output

2 Likes

Thanks for sharing your results!

One small note, variable names should be with lowercase.
I’m not to keen on java conventions, but this one is important.
So int HeightmapWidth = 100; should be int heightmapWidth = 100;.
This makes it more readable for other programmers, specially if you have classes.
Cause if you have the instance of a class with a uppercase you can have thins like this:

Wrong:

class Foo {
  int x = 10;
}

Foo Foo = new Foo();

println(Foo.x);

Now the println(Foo.x); makes it look like we are printing a static variable from the class Foo while we actually print a variable local to the instance.

2 Likes