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


一个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 BigEndian
public 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





评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

车斗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值