一个Java写的用来构建影像金字塔的Bitmap类

来源:互联网 发布:ios 九宫格算法 编辑:程序博客网 时间:2024/06/07 02:25


一个Java写的用来构建金字塔影像的Bitmap类

cheungmine

2012

下面每个图像都是256x256像素。目的就是把这4幅影像合成一个256x256的图像,即:

Ln+1 = Fn(00, 01, 10, 11);

Ln+1表示第n+1层金字塔图像块。它是在第n层金字塔的基础上创建的。

处理前的第n层金字塔(4个瓦片):

00 |  01

----------

10 |  11

  

  


处理后的第n+1层金字塔(1个瓦片):

  

(1) 最邻近点采样得到的                                                     (2) 4像素取平均值得到的(双线性差值得特例)

我写的Java源代码:

/****************************************************************************** * BitmapDecoder.java *   Read and Write DIB bitmap:  *   only 24-bits supported currently. * Author: *   cheungmine *****************************************************************************///package imagery.bitmap;import java.io.Closeable;import java.lang.*;import java.io.File;import java.io.FileNotFoundException;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;// Note: Bitmap is always LittleEndian//       JVM is always BigEndianpublic class BitmapDecoder {  private static final int   BMPFILE_HEADER_SIZE = 54;  private static final short BMPFILE_TAG_BM = 0x4D42;  // BITMAPFILEHEADER  //  private short tagBM;           // 0x4D42  private int   bfSize;          // bytes size of file  private short bfReserved1;     // 0  private short bfReserved2;     // 0  private int   bfOffBits;       // offset from begin to image data  // BITMAPINFOHEADER  //  private int   biSize;          // size of info block  private int   width;           // width pixels  private int   height;          // height pixels  private short biPlanes;        // plane == 1  private short biBitCount;      // color bits (1, 4, 8, 24, 32)  private int   biCompression;   // compression mode: 0 - not; 1 - 8bits; 2 - 4bits  private int   biSizeImage;     // images data size = 4*n  private int   biXPelsPerMeter; // horizontal pixels per meter, in DIB is 00H  private int   biYPelsPerMeter; // vertical pixels per meter, in DIB is 00H  private int   biClrUsed;       // 0  private int   biClrImportant;  // 0  // RGBQUADs: ignored by 24, 32 bits bitmap  // sizeof(RGBQUAD)==4  // biBitCount     = 1, 4, 8  // total RGBQUADs size = sizeof(RGBQUAD)*(2^biBitCount). bytes  private byte [] _RGBQUADs = null;  // file handle  //  private FileInputStream _inStream = null;  private int _widthBytes = 0;  private byte [] _header = null;  private static short bytes2short(int _0, int _1) {    return (short)(((_1<<8)&0xff00) | (_0&0xff));  }  private static int bytes2int(int _0, int _1, int _2, int _3) {    return (int)(((_3<<24)&0xff000000)|((_2<<16)&0xff0000)|((_1<<8)&0xff00)|(_0&0xff));  }  private static void closeStream(Closeable stream) {    try {      if (stream != null) {        stream.close();      }    } catch (IOException e) {      throw new ImageryException(e);    } catch (Exception e) {      throw new ImageryException(e);    }  }  private short readShort(byte [] b, int offset) {    return bytes2short(b[offset], b[offset+1]);  }  private int readInt(byte [] b, int offset) {    return bytes2int(b[offset], b[offset+1], b[offset+2], b[offset+3]);  }  private void readBitmapFileHeader(FileInputStream stream) {    try {      byte [] header = new byte[BMPFILE_HEADER_SIZE];      if (BMPFILE_HEADER_SIZE != stream.read(header)) {        throw new ImageryException(new BitmapReadException("Bitmap header not found"));      }      int pos = 0;      tagBM = readShort(header, pos);      pos += 2;      if (tagBM != BMPFILE_TAG_BM) {        throw new ImageryException(new BitmapReadException("BM tag not found"));      }      bfSize = readInt(header, pos);      pos += 4;      bfReserved1 = readShort(header, pos);      pos += 2;      bfReserved2 = readShort(header, pos);      pos += 2;      bfOffBits = readInt(header, pos);      pos += 4;      biSize = readInt(header, pos);      pos += 4;      width = readInt(header, pos);      pos += 4;      height = readInt(header, pos);      pos += 4;      biPlanes = readShort(header, pos);      pos += 2;      biBitCount = readShort(header, pos);      pos += 2;      biCompression = readInt(header, pos);      pos += 4;      biSizeImage = readInt(header, pos);      pos += 4;      biXPelsPerMeter= readInt(header, pos);      pos += 4;      biYPelsPerMeter= readInt(header, pos);      pos += 4;      biClrUsed= readInt(header, pos);      pos += 4;      biClrImportant= readInt(header, pos);      pos += 4;      if (pos != BMPFILE_HEADER_SIZE) {        throw new RuntimeException("Application has error");      }      _widthBytes = calcWidthBytes();      _header = header;    } catch (IOException e) {      throw new ImageryException(e);    }  }  private void readBitmapRGBQUADs(FileInputStream stream) throws BitmapReadException {    if (biClrUsed > 0) {      _RGBQUADs = new byte[4*biClrUsed];      try {        if (4*biClrUsed != stream.read(_RGBQUADs)) {          throw new BitmapReadException("Read RGBQUAD error.");        }      } catch (IOException e) {        throw new ImageryException(e);      }    }  }    private int calcWidthBytes() {    int widthBytes = 0;    switch (biBitCount) {    case 1:    // 2-colors      widthBytes = (width + 7)/8;      break;    case 4:    // 16-colors      widthBytes = (width+1)/2;      break;        case 8:   // 256-colors      widthBytes = width;      break;    case 24:  // 24-bits true colors:rgb      widthBytes = width*3;      break;    case 32:  // 32 bits true colors: rgba      widthBytes = width*4;      break;    }    return ((widthBytes+3)/4)*4;  }  public BitmapDecoder(String bmpPathfile) {    try {      FileInputStream stream = new FileInputStream(new File(bmpPathfile));      readBitmapFileHeader(stream);      readBitmapRGBQUADs(stream);                  _inStream = stream;    } catch (NullPointerException e) {      throw new ImageryException(e);    } catch (FileNotFoundException e) {      throw new ImageryException(e);    } catch (SecurityException e) {      throw new ImageryException(e);    } catch (IOException e) {      throw new ImageryException(e);    } catch (Exception e) {      throw new ImageryException(e);    }  }  public BitmapDecoder(File fileBmp) {    try {      FileInputStream stream = new FileInputStream(fileBmp);      readBitmapFileHeader(stream);      readBitmapRGBQUADs(stream);         _inStream = stream;    } catch (NullPointerException e) {      throw new ImageryException(e);    } catch (FileNotFoundException e) {      throw new ImageryException(e);    } catch (SecurityException e) {      throw new ImageryException(e);    } catch (IOException e) {      throw new ImageryException(e);    } catch (Exception e) {      throw new ImageryException(e);    }  }  public void open(String bmpPathfile) {    close();    try {      FileInputStream stream = new FileInputStream(new File(bmpPathfile));      readBitmapFileHeader(stream);      readBitmapRGBQUADs(stream);      _inStream = stream;    } catch (NullPointerException e) {      throw new ImageryException(e);    } catch (FileNotFoundException e) {      throw new ImageryException(e);    } catch (SecurityException e) {      throw new ImageryException(e);    } catch (IOException e) {      throw new ImageryException(e);    } catch (Exception e) {      throw new ImageryException(e);    }  }  public void close() {    FileInputStream stream = _inStream;    _inStream = null;    closeStream(stream);  }  public FileInputStream getStream() {    return _inStream;  }  public int readBytes(byte [] b, int len) {    try {      return _inStream.read(b, 0, len);    } catch (IOException e) {      throw new ImageryException(e);    }  }  public int readLines(byte [] b, int numLines) {    try {      return _inStream.read(b, 0, _widthBytes*numLines);    } catch (IOException e) {      throw new ImageryException(e);    }  }  public int getSizeImage() {    return biSizeImage;  }  public int getColorBits() {    return biBitCount;  }  public int getWidthPixels() {    return width;  }  public int getHeightPixels() {    return height;  }    public int getWidthBytes() {    return _widthBytes;  }  public byte [] getBmpHeader() {    return _header;  }    public void printBmpHeader() {    // BITMAPINFOHEADER    //    System.out.println("----------- BITMAPFILEHEADER -----------");    System.out.println("file tag: " + tagBM);    System.out.println("bytes size of file: " + bfSize);    System.out.println("bfReserved1: " + bfReserved1);    System.out.println("bfReserved2: " + bfReserved2);    System.out.println("image data offset: " + bfOffBits);    // BITMAPINFOHEADER    System.out.println("----------- BITMAPINFOHEADER -----------");    System.out.println("size of info block: " + biSize);    System.out.println("width pixels: " + width);    System.out.println("height pixels: " + height);    System.out.println("bit plane: " + biPlanes);    System.out.println("color bits: " + biBitCount);    System.out.println("compression mode: " + biCompression);    System.out.println("images data size bytes: "+ biSizeImage);    System.out.println("horizontal pixels per meter: " + biXPelsPerMeter);    System.out.println("vertical pixels per meter: " + biYPelsPerMeter);    System.out.println("colors Used: " + biClrUsed);    System.out.println("colors Important: " + biClrImportant);    // RGBQUAD    if (_RGBQUADs==null) {      System.out.println("----------- RGBQUAD NOT FOUND -----------");    }    else {      System.out.println("----------- RGBQUAD TABLE -----------");      for (int i=0; i<biClrUsed; i++) {        System.out.println(i+":("+          (_RGBQUADs[i*4+0]&0xff)+","+          (_RGBQUADs[i*4+1]&0xff)+","+          (_RGBQUADs[i*4+2]&0xff)+","+          (_RGBQUADs[i*4+3]&0xff)+")");      }    }  }  /**   * ------------   * | 00  | 01 |     --------   * ------------  => |      |   * | 10  | 11 |     --------   * ------------   */  private static void calcPixel(byte [] dst, int off,    byte [] src, int off_00, int off_01, int off_10, int off_11) {    int b00 = src[off_00+0]&0xff;   int b01 = src[off_01+0]&0xff;    int g00 = src[off_00+1]&0xff;   int g01 = src[off_01+1]&0xff;    int r00 = src[off_00+2]&0xff;   int r01 = src[off_01+2]&0xff;    int b10 = src[off_10+0]&0xff;   int b11 = src[off_11+0]&0xff;      int g10 = src[off_10+1]&0xff;   int g11 = src[off_11+1]&0xff;    int r10 = src[off_10+2]&0xff;   int r11 = src[off_11+2]&0xff;    dst[off+0] = (byte)((b00+b01+b10+b11)/4);    dst[off+1] = (byte)((g00+g01+g10+g11)/4);    dst[off+2] = (byte)((r00+r01+r10+r11)/4);  }  public static void buildPyramid(File outputBmp, int bmpWidthPixels, int bmpHeightPixels,    BitmapDecoder _00, BitmapDecoder _01,     BitmapDecoder _10, BitmapDecoder _11) throws BitmapReadException {    if (_00.getWidthPixels() != _01.getWidthPixels() ||        _10.getWidthPixels() != _11.getWidthPixels() ||        _00.getWidthPixels() != _11.getWidthPixels() ||        _00.getHeightPixels() != _01.getHeightPixels() ||        _10.getHeightPixels() != _11.getHeightPixels() ||        _00.getHeightPixels() != _11.getHeightPixels()) {      throw new BitmapReadException("Bitmaps size not matched.");    }    if (_00.getColorBits() != _01.getColorBits() ||        _10.getColorBits() != _11.getColorBits() ||        _00.getColorBits() != _11.getColorBits()) {      throw new BitmapReadException("Bitmaps colorbits not matched.");    }    if (bmpWidthPixels != bmpHeightPixels) {      //??TODO: Should we support a rectangle one      throw new BitmapReadException("Width not equal with height for output bitmap.");    }    if (bmpWidthPixels != _00.getWidthPixels() ||        bmpHeightPixels != _00.getHeightPixels()) {      throw new BitmapReadException("Bitmap not square.");    }    FileOutputStream outStream = null;    try {      outStream = new FileOutputStream(outputBmp);      outStream.write(_00.getBmpHeader());      int el = bmpWidthPixels;         // length of edge      int bpp = _00.getColorBits()/8;  // Bytes Per Pixel      int wb = _00.getWidthBytes();    // Width Bytes      byte [] rb1 = new byte[wb*2];    // 2-lines buffer      byte [] rb2 = new byte[wb*2];    // 2-lines buffer      byte [] dst = new byte[wb];      // 1-lines buffer      int i, n, r1, r2;      while ((r1=_10.readLines(rb1, 2))==wb*2 && (r2=_11.readLines(rb2, 2))==wb*2) {        for (n=0; n<el/2; n++) {          i = n*2*bpp;  // source          calcPixel(dst, n*bpp, rb1, i, i+bpp, wb+i, wb+i+bpp);        }        for (n=el/2; n<el; n++) {          i = (n-el/2)*2*bpp;  // source          calcPixel(dst, n*bpp, rb2, i, i+bpp, wb+i, wb+i+bpp);        }        outStream.write(dst);      }      while ((r1=_00.readLines(rb1, 2))==wb*2 && (r2=_01.readLines(rb2, 2))==wb*2) {        for (n=0; n<el/2; n++) {          i = n*2*bpp;  // source          calcPixel(dst, n*bpp, rb1, i, i+bpp, wb+i, wb+i+bpp);        }        for (n=el/2; n<el; n++) {          i = (n-el/2)*2*bpp;  // source          calcPixel(dst, n*bpp, rb2, i, i+bpp, wb+i, wb+i+bpp);        }        outStream.write(dst);      }    } catch (IOException e) {      throw new ImageryException(e);    } finally {      closeStream(outStream);    }  }}


/****************************************************************************** * ImageryException.java * *****************************************************************************///package imagery.bitmap;import java.io.*;class ImageryException extends RuntimeException {  private final String _stackTrace;  public Exception originalException;  public ImageryException(Exception e) {    super(e.toString());    originalException = e;    StringWriter sw = new StringWriter();    e.printStackTrace(new PrintWriter(sw));    _stackTrace = sw.toString();  }  public void printStackTrace() {    printStackTrace(System.err);  }  public void printStackTrace(java.io.PrintStream s) {    synchronized(s) {      s.print(getClass().getName() + ": ");      s.print(_stackTrace);    }  }  public void printStackTrace(java.io.PrintWriter s) {    synchronized(s) {      s.print(getClass().getName() + ": ");      s.print(_stackTrace);    }  }  public void rethrow() throws Throwable {    throw originalException;  }}

/****************************************************************************** * BitmapReadException.java * *****************************************************************************///package imagery.bitmap;import java.io.*;public class BitmapReadException extends Exception {  public BitmapReadException() {    super();  }  public BitmapReadException(String message) {    super(message);  }  public BitmapReadException(String message, Throwable cause) {    super(message, cause);  }  public BitmapReadException(Throwable cause) {    super(cause);  }}

测试代码:

 /*   *    ------------   *    | 00  | 01 |      --------   *    ------------  =>  |      |   *    | 10  | 11 |      --------   *    ------------   */  public void testResampleBMP24() throws Exception {    String srcdir = "C:/nv_workspace/zebra/sandbox/mapred/data/";    BitmapDecoder tile_00 = new BitmapDecoder(srcdir+"hadoop_0-0.bmp");    BitmapDecoder tile_01 = new BitmapDecoder(srcdir+"hadoop_0-1.bmp");    BitmapDecoder tile_10 = new BitmapDecoder(srcdir+"hadoop_1-0.bmp");    BitmapDecoder tile_11 = new BitmapDecoder(srcdir+"hadoop_1-1.bmp");    tile_00.printBmpHeader();    assertEquals(tile_00.getColorBits(), 24);    assertEquals(tile_00.getWidthPixels(), 256);    assertEquals(tile_00.getHeightPixels(), 256);    BitmapDecoder.buildPyramid(new File(srcdir+"hadoop_out.bmp"),      tile_00.getWidthPixels(),      tile_00.getHeightPixels(),      tile_00, tile_01,      tile_10, tile_11);    tile_00.close();    tile_01.close();    tile_10.close();    tile_11.close();  }

结论:

需要注意的是,Java的byte是有符号的,而像素的值是无符号的,需要(见calcPixel)

int iv = bv&0xff;

处理以变成无符号的.这个小小的BUG折腾我一天时间.最后把像素打印出来一个个分析才知道原来Java这么恶心.

同样功能的代码在C中需要100行的话,Java里就需要200行.无语啊!

cheungmine