Page 1 of 2

Indigo Image (.igi) format

Posted: Tue Feb 13, 2007 10:07 pm
by OnoSendai
All comments and suggestions welcome:

Obviously this is a very simple format, just the way I like it :)

Also the image sizes are gigantic, but I don't really care.

Note that the Indigo Tone Mapper program expects the colour space to be XYZ.

Do whatever you want with this format, the spec is 'public domain'.
Look forward to seeing some cool stuff :)

Code: Select all

/*=====================================================================
IndigoImageHeader
-----------------
This should be stored on disk as a tightly packed structure (no padding between elements)
in little endian byte order.
Image data starts at byte 5032. (This should also be the size of the header on disk)

All integers and unsigned integers are 32 bit
=====================================================================*/
class IndigoImageHeader
{
public:
	IndigoImageHeader(){}
	~IndigoImageHeader(){}

	static const int MAGIC_NUMBER = 66613373;
	int magic_number; // Should be 66613373
	static const int LATEST_FORMAT_VERSION = 4;
	int format_version;

	double num_samples; // Total num samples taken over entire image
	unsigned int width; // Width of supersampled (large) image
	unsigned int height; // Height of supersampled (large) image
	unsigned int supersample_factor; // >= 1
	int zipped; // Boolean

	int image_data_size; // Size of image data in bytes
	// Should be equal to width*height*12*num_layers, if data is uncompressed.

	unsigned int colour_space; // 0 = XYZ

	// New in format version 2: total render time spent on image, in seconds.
	double render_time;

	// New in format version 3: number of light layers in file.
	unsigned int num_layers;

	// New in format version 4: Layer names
	unsigned int layer_name_format; // 0 = ASCII
	unsigned int num_named_layers; // This should be <= num_layers
	struct LayerName
	{
		unsigned int layer_name_len;
		static const int MAX_LAYER_NAME_BYTES = 64;
		char name_data[MAX_LAYER_NAME_BYTES];
	}; // 68 bytes

	static const int MAX_NUM_LAYER_NAMES = 16;
	LayerName layer_names[MAX_NUM_LAYER_NAMES]; // 68B * 16 = 1088B

	unsigned int last_rng_seed;

	// 16 * 4 = 64 bytes + 1088 bytes = 1152 bytes

	unsigned char padding[3880]; // Padding in case I want more stuff in the header in future.  Starts at byte 1152.

	// Image data follows:
	// For each layer,
	// top row, then next-to-top row, etc...
	// left to right across the row.
	// 3 32 bit floats per pixel.
};

Old specification (version 2)

Code: Select all

class IndigoImageHeader
{
public:
	IndigoImageHeader();

	~IndigoImageHeader();

	// All integers and unsigned integers are 32 bit
	// Byte order should be little endian (Intel byte order)

	int magic_number; // Should be 66613373
	int format_version; // Latest version is 2

	double num_samples; // Total num samples taken over entire image
	unsigned int width; // Width of supersampled (large) image
	unsigned int height; // Height of supersampled (large) image
	unsigned int supersample_factor; // >= 1
	int zipped; // Boolean

	int image_data_size; // Size of image data in bytes
	// Should be equal to width*height*12, if data is uncompressed.

	unsigned int colour_space; // 0 = XYZ

	// New in format version 2: total render time spent on image, in seconds.
	double render_time;

	unsigned char padding[4992]; // Padding in case I want more stuff in the header in future.

	// Image data follows:
	// top row, then next-to-top row, etc...
	// left to right across the row.
	// 3 32 bit floats per pixel.
};






Old v1 spec:

Code: Select all

class IndigoImageHeader
{
public:
	/*=====================================================================
	IndigoImageHeader
	-----------------
	
	=====================================================================*/
	IndigoImageHeader();

	~IndigoImageHeader();

	//all integers and unsigned integers are 32 bit

	int magic_number;//should be 66613373
	int format_version;//1

	double num_samples;//total num samples taken over entire image
	unsigned int width;//width of supersampled (large) image
	unsigned int height;//height of supersampled (large) image
	unsigned int supersample_factor;// >= 1
	int zipped;//boolean

	int image_data_size;//size of image data in bytes
	//should be equal to width*height*12, if data is uncompressed.

	unsigned int colour_space;//0 = XYZ

	unsigned char padding[5000];//padding in case i want more stuff in the header in future

	//image data follows:
	//top row, then next-to-top row, etc...
	//left to right across the row.
	//3 32 bit floats per pixel.
};


Posted: Tue Feb 13, 2007 11:52 pm
by VictorJapi
Why not a pre/post scale and burn vars? so the image could be saved without any transformation applied and do it at load time with the defaults saved.

Posted: Wed Feb 14, 2007 1:36 pm
by eman7613
it occurs to me... is there a conversion method to get the igi to a png, jpg, or bmp?

Posted: Wed Feb 14, 2007 1:40 pm
by manitwo
you can use the new tonemapper to export to jpg or png :wink:

Posted: Thu Feb 15, 2007 9:51 pm
by ryjo
Thanks,

Could youd add the endianess of the ints/floats etc, and the specific order the color values are stored (R,G,B or B,G,R). :)

ryjo

Posted: Thu Feb 15, 2007 10:04 pm
by OnoSendai
little endian (Intel byte order).

The pixels aren't RGB, but XYZ, a more general 3-d colour space.

Posted: Thu Feb 15, 2007 10:23 pm
by Deus
Little endian is gay.

Posted: Thu Feb 15, 2007 10:32 pm
by OnoSendai
Deus wrote:Little endian is gay.
lol :)

Posted: Thu Feb 15, 2007 10:33 pm
by VictorJapi
Deus wrote:Little endian is gay.
THIS IS THE WAAAARRRR!!! DEATH TO BIG ENDIAAAAAAN!!!!

Posted: Fri Feb 16, 2007 2:34 am
by ryjo
Hi,

I wrote an implementation of this format so igi files can be read from Java.

Code: Select all

/*
 * Public Domain.
 * 
 * Note that this piece of software may be unfinished or buggy, or both.
 * 
 */
package igi;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.channels.FileChannel;

/**
 * Support for reading igi (indigo image) files.
 * <p>
 * Example usage:
 * <pre>
 * IGI igi = new IGI(filename);
 * int w = igi.getWidth();
 * int h = igi.getHeight();
 * float[] x = new float[w * h];
 * float[] y = new float[w * h];
 * float[] z = new float[w * h];
 * igi.readData(x, y, z);
 * </pre>
 * 
 * @version igi ver 1
 */
public class IGI {
	private static final int IGI_VERSION = 1;
	private static final int HEADER_SIZE = 40;
	private static final int HEADER_EXTRA_SIZE = 5000;
	private static final int DATA_BLOCK_START = HEADER_SIZE + HEADER_EXTRA_SIZE;
	
	private final File file;
	private final int dataSize;
	private final int width;
	private final int height;
	private final int sampleFactor;
	private final double samples;
	
	/**
	 * Create an IGI instance and read the header data.
	 * 
	 * @param path
	 *            The path to the file.
	 * @throws IOException
	 *             If the file is not accessable, or is not in the correct
	 *             format.
	 */
	public IGI(String path) throws IOException {
		this(new File(path));
	}

	/**
	 * Create an IGI instance and read the header data.
	 * 
	 * @param file
	 *            The file to read.
	 * @throws IOException
	 *             If the file is not accessable, or is not in the correct
	 *             format.
	 */
	public IGI(File f) throws IOException {
		this.file = f.getCanonicalFile();

		ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE);
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
		FileChannel fileChannel = randomAccessFile.getChannel();

		try {
			fileChannel.read(buffer);
			buffer.flip();
			
			int magic = buffer.getInt();
			int version = buffer.getInt();
			double samples = buffer.getDouble();
			int width = buffer.getInt();
			int height = buffer.getInt();
			int sampleFactor = buffer.getInt();
			int compression = buffer.getInt();
			int dataSize = buffer.getInt();
			int colorSpace = buffer.getInt();
	
			if (magic != 66613373)
				throw new IOException("wrong magic: " + magic);
			
			if (version != IGI_VERSION)
				throw new IOException("unsupported version: " + version);
			
			if (compression != 0)
				throw new IOException("unsupported compression: " + compression);
	
			if (colorSpace != 0)
				throw new IOException("unsupported color space: " + colorSpace);
			
			if (dataSize < 0)
				throw new IOException("illegal data block size: " + dataSize);
			
			// Only support 31 bit "unsigned" ints
			if (width <= 0 || height <= 0 || sampleFactor <= 0)
				throw new IOException(
						"unsupported dimension: width=" + width + 
						" height=" + height + 
						" sampleFactor=" + sampleFactor);
			
			this.samples = samples;
			this.dataSize = dataSize;
			this.width = width;
			this.height = height;
			this.sampleFactor = sampleFactor;
		} finally {
			try {
				fileChannel.close();
			} catch (IOException e) {
			}
		}
	}
	
	/**
	 * Read the data block and store as X, Y and Z channels.
	 * @param x The X data values.
	 * @param y The Y data values.
	 * @param z The Z data values.
	 * @throws IOException If there is an io problem or the data block is incomplete.
	 */
	public void readData(float[] x, float[] y, float[] z) throws IOException {
		RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
		FileChannel fileChannel = randomAccessFile.getChannel();

		try {
			readData(fileChannel, x, y, z);
		} finally {
			try {
				fileChannel.close();
			} catch (IOException e) {
			}
		}
	}

	private void readData(FileChannel fileChannel, float[] x, float[] y, float[] z) throws IOException {
		ByteBuffer dataBuffer = ByteBuffer.allocate(dataSize);
		dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
		int n = fileChannel.read(dataBuffer, DATA_BLOCK_START);
		if (n != dataSize)
			throw new IOException("incomplete data block: expected " + dataSize + " got " + n);

		dataBuffer.flip();
		FloatBuffer xyzFloats = dataBuffer.asFloatBuffer();

		try {
			for (int i=0; xyzFloats.hasRemaining(); i++) {
				x[i] = xyzFloats.get();
				y[i] = xyzFloats.get();
				z[i] = xyzFloats.get();
			}
		} catch (RuntimeException e) {
			throw new IOException("buffer error: " + e);
		}
	}

	/**
	 * Return the "Super Sample Factor" from the igi file header.
	 * @return The "Super Sample Factor" from the igi file header.
	 */
	public int getSampleFactor() {
		return sampleFactor;
	}

	/**
	 * Return the igi image height.
	 * @return The image height.
	 */
	public int getHeight() {
		return height;
	}

	/**
	 * Return the igi image width.
	 * @return The image width.
	 */
	public int getWidth() {
		return width;
	}

	/**
	 * Return the size of the image data block.
	 * @return The size of the image data block.
	 */
	public int getDataSize() {
		return dataSize;
	}
	
	/**
	 * Return the samples field of the igi header.
	 * @return The samples field of the igi header.
	 */
	public double getSamples() {
		return samples;
	}

	/**
	 * Retrieve the file for this igi image.
	 * @return The file object.
	 */
	public File getFile() {
		return file;
	}
}

Posted: Fri Feb 16, 2007 2:37 am
by OnoSendai
nice! looks kinda like my C++ code :)

Posted: Fri Feb 16, 2007 2:56 am
by ryjo
OnoSendai wrote:nice! looks kinda like my C++ code :)
Yes, Java and C++ are fairly similar, except in Java you write all code for a class in the same file. And all memory management is very different. And speed, when you run it :) (though I think its pretty fast nowadays)

Posted: Fri Feb 16, 2007 3:45 am
by Deus
"However, when Java is promoted as the sole programming language, its flaws and limitations become serious."
Bjarne Stroustrup

I like java. But when I need to solve a problem that could benefit from java compared to C/C++ I would pick python in 80% of the cases.

Posted: Fri Feb 23, 2007 8:10 am
by matsta
rofl noob question coming up XD.

wat must i do with the code to be able to save as igi?

lol told u

Posted: Fri Feb 23, 2007 8:58 am
by ryjo
matsta wrote:rofl noob question coming up XD.

wat must i do with the code to be able to save as igi?

lol told u
infile.txt -> set "save_igi" to "true"

That should work.