目的
32bit長のfloatの値を、8bit長に分割して、byte[4]として扱い、RGBAのPNGとして保存する手順です。
需要はあまりないと思いますが、自分の場合、こんな思考でした。
・HDRやEXRフォーマットは避けたい
・自分しか使わないので、解像度など決め打ち
・HeightMapで256階調では足りず、もっと細かくしたい
・表示がおかしくてもOSの標準機能で中身をみれたらなぁ
・float値のバイナリ書き出しだと、みれないしなぁ
・pngなら勝手に圧縮してくれる
・CTのようなボリュームデータも扱えそう。
float値のバイナリ書き出し
5年前、2021年3月30日の、自分の記事を見つけたので、(ショック!)参考にして、
コード「floatの精度を捨ててpngとして保存」
まず、ターゲットとするfloatのデータを決める。
32bitから8bitに丸め込まれるので、精度は落ちる。
PImage img;
float[][] f32 = new float[1024][1024];
size(1024, 1024);
noiseSeed(0);
img = createImage(1024, 1024, ARGB);
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
f32[x][y] = noise(x*0.02, y*0.02)*256;
img.set(x, y, color(f32[x][y], 255));
}
}
img.save("F.png");
image(img, 0, 0);
コード「floatをバイナリで保存して、読み込んで表示」
さっきのnoiseで生成したfloat配列をバイナリで保存し、
それをfloat配列に読み込み直して、imgに描画し、表示する。
ファイル容量は
1024102432/8 = 4,194,304 Byte
zip圧縮したら
3,686,253 Byte
89%に圧縮できた。
import java.io.*;
size(1024, 1024);
noiseSeed(0);
// Prep
float[][] f32 = new float[1024][1024];
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
f32[x][y] = noise(x*0.02, y*0.02)*256;
}
}
// Write
try {
OutputStream os = createOutput("F32_bin.dat");
DataOutputStream dos = new DataOutputStream(os);
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
dos.writeFloat(f32[x][y]);
}
}
dos.flush();
dos.close();
}
catch(IOException e) {
e.printStackTrace();
}
// Load
float[][] tmp = new float[1024][1024];
try {
InputStream is = createInput("F32_bin.dat");
DataInputStream dis = new DataInputStream(is);
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
tmp[x][y] = dis.readFloat();
}
}
dis.close();
}
catch(IOException e) {
e.printStackTrace();
}
// Display
PImage img = createImage(1024, 1024, ARGB);
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
img.set(x, y, color(tmp[x][y], 255));
}
}
image(img, 0, 0);
コード「floatをintに変換してpng保存して、読み込んで表示」
color型がint型であることを利用し、かつ
Float.floatToIntBits
Float.intBitsToFloat
を駆使して、実装。
中間生成物であるpngファイルは、以下の様に見える。
ファイル容量は
3,688,761 Byte
zip圧縮とほぼ同等サイズになった。
import java.io.*;
size(1024, 1024);
noiseSeed(0);
// Prep
float[][] f32 = new float[1024][1024];
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
f32[x][y] = noise(x*0.02, y*0.02)*256;
}
}
// Write
PImage img = createImage(1024, 1024, ARGB);
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
int i32 = Float.floatToIntBits(f32[x][y]);
img.set(x, y, i32);
}
}
img.save("F32.png");
// Load
float[][] tmp = new float[1024][1024];
PImage img2 = loadImage("F32.png");
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
int c = img2.get(x,y);
tmp[x][y] = Float.intBitsToFloat(c);
}
}
// Display
PImage img3 = createImage(1024, 1024, ARGB);
for (int y=0; y<1024; y++) {
for (int x=0; x<1024; x++) {
img3.set(x, y, color(tmp[x][y], 255));
}
}
image(img3, 0, 0);