Smart Phone (Mobile)/Android 2011. 8. 12. 01:09

내/외장에 저장 된 이미지 파일(PNG or JPEG ...)을 Bitmap으로 읽어 들이는 간단한 코드이다.
이미지 파일을 읽을 때 너무 큰 이미지는 OutOfMemory가 발생한다. 그래서 폰의 화면 해상도에 가장 근접하게 리스케일을 하여 읽어들이게 했다.
코드는 아주 간단하다.

/**

  * 지정한 패스의 파일을 읽어서 Bitmap 리턴 (화면사이즈에 최다한 맞춰서 리스케일한다.)

  *

  * @param context

  *            application context

  * @param imgFilePath

  *            bitmap file path

  * @return Bitmap

  * @throws IOException

  */

public static Bitmap loadBackgroundBitmap(Context context,

        String imgFilePath) throws Exception, OutOfMemoryError { 

    if (!FileUtil.exists(imgFilePath)) {

        throw new FileNotFoundException("background-image file not found : " + imgFilePath);

    }


    // 폰의 화면 사이즈를 구한다.
    Display display = ((WindowManager) context

            .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

    int displayWidth = display.getWidth();

    int displayHeight = display.getHeight();

 

    // 읽어들일 이미지의 사이즈를 구한다.

    BitmapFactory.Options options = new BitmapFactory.Options();

    options.inPreferredConfig = Config.RGB_565;

    options.inJustDecodeBounds = true;

    BitmapFactory.decodeFile(imgFilePath, options);

 
    // 화면 사이즈에 가장 근접하는 이미지의 리스케일 사이즈를 구한다.
    // 리스케일의 사이즈는 짝수로 지정한다. (이미지 손실을 최소화하기 위함.) 

    float widthScale = options.outWidth / displayWidth;

    float heightScale = options.outHeight / displayHeight;

    float scale = widthScale > heightScale ? widthScale : heightScale;

            

    if(scale >= 8) {

        options.inSampleSize = 8;

    } else if(scale >= 6) {

        options.inSampleSize = 6;

    } else if(scale >= 4) {

        options.inSampleSize = 4;

    } else if(scale >= 2) {

        options.inSampleSize = 2;

    } else {

        options.inSampleSize = 1;

    }

    options.inJustDecodeBounds = false;

 

    return BitmapFactory.decodeFile(imgFilePath, options);

}

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. profeel 2012.10.04 11:19  댓글주소  수정/삭제  댓글쓰기

    잘 보고 갑니다.



Smart Phone (Mobile)/Android 2010. 10. 5. 16:39

안드로이드의 Bitmap은 기본적으로 16Bit(RGB_565)를 제공하며, 32Bit(ARGB_8888)을 지원한다.
그러나 이미지 처리를 위해서 Bitmap을 24Bit(RGB_888)로 변경을 해야하는 경우도 있다.
이런 경우는 기본 Android Platform에서는 지원을 하지 않고 있다. 변경이 필요하다면 소스코드에서 실제 데이터 값의 변경 작업으로 수행을 해야 하는 것 같다.

기본적으로 24Bit의 헤더 정보를 생성하야 하며, 16Bit 또는 32Bit Bitmap의 색상데이터 값에서 24Bit로 변경하는 작업을 수행해야 한다.

16Bit Bitmap : #RRRRRGGGGGGBBBBB (RGB_565)
24Bit Bitmap : #RRRRRRRRGGGGGGGGBBBBBBBB (RGB_888)
32Bit Bitmap : #AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB (ARGB_8888)

16Bit를 24Bit 또는 32Bit로의 변환은 헤더정보가 동일하며, Android에서 pixel 값을 int 형으로 추출하므로 동일하게 처리를 할 수 있다. 물론 int 형이 아닌 실 byte[]의 값으로 가져오는 것이 처리 속도면에서는 좋으나, byte에 대한 데이터형의 차이에 때문에 조금 번거로워 int 형의 픽셀값으로 처리를 한다.

* Bitmap 헤더 정보 생성
private static void encode24BMPHeader(DataOutputStream stream, int width,
        int height, int fileSize) throws IOException {
    // the magic number used to identify the BMP file: 0x42 0x4D
    stream.writeByte(0x42);
    stream.writeByte(0x4D);
    stream.writeInt(swapEndian(fileSize));
    // reserved
    stream.writeInt(0);
    // the offset, i.e. starting address of the bitmap data
    stream.writeInt(swapEndian(14 + 40));
    // INFORMATION HEADER (Windows V3 header) the size of this header (40 bytes)
    stream.writeInt(swapEndian(40));
    // the bitmap width in pixels (signed integer).
    stream.writeInt(swapEndian(width));
    // the bitmap height in pixels (signed integer).
    stream.writeInt(swapEndian(height));
    // the number of colour planes being used. Must be set to 1.
    stream.writeShort(swapEndian((short) 1));
    // the number of bits per pixel, which is the colour depth of the image.
    stream.writeShort(swapEndian((short) 24));
    // the compression method being used.
    stream.writeInt(0);
    // image size. The size of the raw bitmap data. 0 is valid for uncompressed.
    stream.writeInt(0);
    // the horizontal resolution of the image. (pixel per meter, signed integer)
    stream.writeInt(0);
    // the vertical resolution of the image. (pixel per meter, signed integer)
    stream.writeInt(0);
    // the number of colours in the colour palette, or 0 to default to 2n.
    stream.writeInt(0);
    // the number of important colours used, or 0 when every colour is important. generally ignored.
    stream.writeInt(0);
}
실제 Bitmap의 헤더 정보에 대한 부분은 구글을 통해 손쉽게 알 수 있을 것이다.

* Bitmap 픽셀 값의 변경 (32/16Bit to 24Bit)
public static byte[] encodeBMPTo24(int[] rgb, int width, int height) throws IOException {
    int padding = (4 - (width % 4)) % 4;
    // the size of the BMP file in bytes
    int fileSize = 14 + 40 + height * (padding + width * 3);

    ByteArrayOutputStream bytes = new ByteArrayOutputStream(fileSize);
    DataOutputStream out = new DataOutputStream(bytes);

    // encode bitmap header (24bit over)
    encode24BMPHeader(out, width, height, fileSize);

    // PALETTE (none for 24 bit depth) IMAGE DATA starting in the bottom left, working right and then up
    // a series of 3 bytes per pixel in the order B G R.
    int colorValue = 0;
    for (int j = height - 1; j >= 0; j--) {
        for (int i = 0; i < width; i++) {
            colorValue = rgb[i + width * j];
    
            out.writeByte(colorValue & 0x000000FF);
            out.writeByte((colorValue >>> 8) & 0x000000FF);
            out.writeByte((colorValue >>> 16) & 0x000000FF);
        }

        // number of bytes in each row must be padded to multiple of 4
        for (int i = 0; i < padding; i++) {
            out.writeByte(0);
        }
    }

    byte[] encodeBytes = bytes.toByteArray();
    bytes.close();

    // quick consistency check
    if (encodeBytes.length != fileSize) {
        throw new RuntimeException("bitmap file size bad match (32bit to 24bit)");
    }

    return encodeBytes;
}

위의 소스를 이용하여 실제 변경하는 소스는 아래와 같다.

public static byte[] decode24Bitmap(Bitmap bitmap) throws IOException {
    
int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int[] rgb = new int[width * height];
    bitmap.getPixels(rgb, 0, width, 0, 0, width, height);

    byte[] bitmap24Bytes = encodeBMPTo24(rgb, width, height);
    
    bitmap.recycle();
    bitmap = null;
    rgb = null;
  
    // system gc
    //System.gc();

    return bitmap24Bytes;
}

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 레티아웨이 2014.12.04 19:12 신고  댓글주소  수정/삭제  댓글쓰기

    와 감사합니다.
    팀프 하는데 처음으로 안드로이드를 만져보면서 사진을 전송해야 하는데
    장비에서 오직 BMP파일 밖에 읽지 못하는 반면 안드로이드 사진은 JPEG로 저장되서 Bitmap 으로 변경은 하긴 했는데
    인터넷에서 찾아보니 PNG로 변경하는 것이 대부분이라 다른 방법을 찾았는데
    그 방법은 헤더 자체를 만들지 않고 정말 데이터만 보내 읽지 못했습니다.
    그리고 추가로 문제를 해결하려는 도중 BMP인데 24비트이여야만 읽을 수 있다고 하여 어려워질것 같았는데(32비트로 변경되어 저장해서)
    님이 올리신 소스덕에 쉽게 해결하였습니다.