package com.profcon.tini.stikclik; import java.io.DataOutputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.DataInputStream; /** Convert a camera bitmap into a BMP image. This class converts * the raw rasters from the camera into a BMP image. Understanding * it propbably requires a fairly detailed knowledge of both. * See http://www.chipcenter.com/circuitcellar/august00/c0800bl1.htm * for the camera's format. */ public class BmpBuilder { /** Convert from rggb into bmp. */ private byte[] rggb, bmp; /** bmp[at] is the next byte of bmp to write. */ private int at; private ByteArrayOutputStream baos = new ByteArrayOutputStream(); private DataOutputStream dos = new DataOutputStream(baos); private void flush() { byte[] buf = baos.toByteArray(); baos.reset(); System.arraycopy(buf, 0, bmp, at, buf.length); at += buf.length; } // Remember: BMP files have data in the "wrong" (Intel-style) byte order. private void writeSwappedInt(int i) throws Exception { dos.write(i); dos.write(i>>>8); dos.write(i>>>16); dos.write(i>>>24); } /** Translate the raw rggb from the camera into BMP format. */ public synchronized void translate(byte[] rggb, byte[] bmp) throws Exception { this.rggb = rggb; this.bmp = bmp; at = 0; writeHeader(); writePixels(); } private void writeHeader() throws Exception { dos.write(new byte[] { 0x42, 0x4d }); // "BM" //dos.writeInt(swapInt(BMP_TOTAL_SIZE)); writeSwappedInt(BMP_TOTAL_SIZE); dos.writeShort(0); // Reserved dos.writeShort(0); // Reserved dos.writeInt(0x36000000); // Swapped int: pixel offset dos.writeInt(0x28000000); // Swapped int: info header size writeSwappedInt(BMP_WIDTH); // Swapped int: width writeSwappedInt(BMP_HEIGHT); // Swapped int: height dos.writeShort(0x0100); // Swapped short: 1 plane of pixels dos.writeShort(0x1800); // Swapped short: 24 bits/pixel dos.writeInt(0); // compression = 0 writeSwappedInt(BMP_DATA_SIZE); // Swapped int: image size writeSwappedInt(2000); // X pixels/meter writeSwappedInt(2000); // Y pixels/meter dos.writeInt(0); // Unused field dos.writeInt(0); // Unused field flush(); // The pixel data follows: Bottom row first, each line padded to // a 4-byte boundary. } private void writePixels() throws Exception { flush(); int pixelsAt = at; // starting pos for BMP pixel data Diagnostics.startTimer("rgb transform"); monochrome_transform(); //balanceBmp(pixelsAt); Diagnostics.endTimer("rgb transform"); flush(); } /* The G1-RR-BB-G2 values are Bayer pixels. The X values are BMP pixels. Note that the BMP image is smaller by one pixel in each dimension. This picture does not show how the Bayer pixels are physically layed out. G1 RR G1 RR G1 RR X X X X X BB G2 BB G2 BB G2 X X X X X G1 RR G1 RR G1 RR X X X X X BB G2 BB G2 BB G2 X X X X X G1 RR G1 RR G1 RR */ // Read from camera: A series of 2-row blocks consisting of: // // (1) One row of red pixels values (0-255) // (2) One row of green1 pixels values (0-255) // (3) One row of green2 pixels values (0-255) // (4) One row of blue pixels values (0-255) /** A property of the BMP format */ private final static int BMP_HEADER_LEN = 0x36; private static final int RGGB_WIDTH = CameraController.RGGB_WIDTH, RGGB_HEIGHT = CameraController.RGGB_HEIGHT-1; public static final int BMP_WIDTH = RGGB_WIDTH, BMP_HEIGHT = RGGB_HEIGHT, // Row length is 3*BMP_WIDTH padded up to a multiple of 4 BMP_ROW_LEN = ((3*BMP_WIDTH + 3) / 4) * 4, BMP_ROW_PADDING = BMP_ROW_LEN - (3*BMP_WIDTH), // Total file size of bitmap BMP_DATA_SIZE = BMP_HEIGHT*BMP_ROW_LEN, BMP_TOTAL_SIZE = BMP_DATA_SIZE + BMP_HEADER_LEN; private static final int WIDTHBY2 = CameraController.RGGB_WIDTH/2, RGGB_BLOCK = WIDTHBY2*4; /** Transform a raw Bayer bitmap from the camera into raw, * unfiltered BMP rows. Don't mess with this method unless * you have a detailed understanding of both formats. */ // BROKEN private void orig_transform() { byte[] result = bmp, rggb = this.rggb; int at = this.at; for( int block=0; block 255) n=255; return (byte)n; } /////////////////////////////////////////////////////////////////////// private void balanceBmp(int startAt) { byte[] bmp = this.bmp; final int BMP_PIXELS = BMP_HEIGHT*BMP_WIDTH; int avgRed=0, avgGreen=0, avgBlue=0; int at = startAt; for(int row=0; row