3

I have an array of bytes that correspond to a "grayscaled bitmap" (one byte->one pixel), and I need to create a PNG file for this image.

The method below works, but the png created is HUGE, as the Bitmap I am using is an ARGB_8888 bitmap, which takes 4 bytes per pixel instead of 1 byte.

I haven't been able to make it work with other Bitmap.Config different than ARGB_8888. Maybe ALPHA_8 is what I need, but I have not been able to make it work.

I have also tried the toGrayScale method which is included in some other posts (Convert a Bitmap to GrayScale in Android), but I have the same issue with the size.

public static boolean createPNGFromGrayScaledBytes(ByteBuffer grayBytes, int width,
        int height,File pngFile) throws IOException{

    if (grayBytes.remaining()!=width*height){
        Logger.error(Tag, "Unexpected error: size mismatch [remaining:"+grayBytes.remaining()+"][width:"+width+"][height:"+height+"]", null);
        return false;
    }
    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    // for each byte, I set it in three color channels.
    int gray,color;
    int x=0,y=0;        
    while(grayBytes.remaining()>0){

        gray = grayBytes.get();
        // integer may be negative as byte is signed. make them positive. 
        if (gray<0){gray+=256;}

        // for each byte, I set it in three color channels.
        color= Color.argb(-1, gray, gray, gray);


        bitmap.setPixel(x, y, color);
        x++;
        if (x==width){
            x=0;
            y++;
        }           
    }
    FileOutputStream fos=null;

    fos = new FileOutputStream(pngFile);
    boolean result= bitmap.compress(Bitmap.CompressFormat.PNG,100,fos);
    fos.close();
    return result;
}       

EDIT: Link to the generated file (it may look nonsense, but is just created with randon data). http://www.tempfiles.net/download/201208/256402/huge_png.html

Any help will be greatly appreciated.

Community
  • 1
  • 1
richardtz
  • 4,993
  • 2
  • 27
  • 38
  • Define "HUGE". My go-to image editing program creates an RGB PNG with transparency about 40% larger than the grayscale PNG, not bad considering it's 300% more data. – Mark Ransom Aug 09 '12 at 02:47
  • Original grayscaled png is 1732K. The png created with the above procedure is 5445K – richardtz Aug 09 '12 at 07:36
  • Wow, that almost sounds like a bug in the PNG compressor. If you take the 5445K result and run it through one of the many PNG compactors out there, how big is the result? Can you post that file somewhere? – Mark Ransom Aug 09 '12 at 13:30
  • Edited the post to add a link to the generated file. It makes sense it takes too much space, as I am creating it as ARGB_8888. The problem is that it's enough for me to have one byte per pixel. – richardtz Aug 09 '12 at 18:13
  • I declare your PNG compressor to be broken. 5445K is nearly what the file would be uncompressed, if you tossed the alpha. Merely opening and saving the file back out makes it 2513K. I think you need a better library. – Mark Ransom Aug 10 '12 at 02:51
  • Right now I am using Android Framework, but I am changing right now to use the one suggested below. – richardtz Aug 10 '12 at 09:19

2 Answers2

5

As you've noticed, saving a grayscale image as RGB is expensive. If you have luminance data then it would be better to save as a Grayscale PNG rather than an RGB PNG.

The bitmap and image functionality available in the Android Framework is really geared towards reading and writing image formats that are supported by the framework and UI components. Grayscale PNG is not included here.

If you want to save out a Grayscale PNG on Android then you'll need to use a library like http://code.google.com/p/pngj/

Error 454
  • 7,255
  • 2
  • 33
  • 48
  • I'll try with the library you suggest. What I find strange, is that it cannot be done using Android Framework. Thanks! – richardtz Aug 09 '12 at 07:47
  • It can be done in plain old Java but Android is missing the required classes. Saving luminance images is a pretty rare thing on the handset. – Error 454 Aug 09 '12 at 07:50
  • I'm worried because of the size, because the image is going to be sent by email. But I agree it is not the common use case for handsets. – richardtz Aug 09 '12 at 12:16
  • RGB_565 is marginally smaller than ARGB_8888 if you want to compare built-in options. Also if you don't mind losing data, saving to JPG allows you to compress vs PNG which ignores the compression parameter. – Error 454 Aug 09 '12 at 16:39
  • I need the exact bytes contained in the byte array. I also tried with RGB_565, but I couldn't recover the original info. Some of the pixels were rounded, and have a +/- 1 value. – richardtz Aug 09 '12 at 18:16
  • 2
    changed to use the library you suggest and works like a charm. Thanks a lot! – richardtz Aug 10 '12 at 10:26
2

If you use OPENCV for Android library, you can use the library to save a binary data to a png file. My way is: in jni part, set Mat whose data begin with the byte array:

jbyte* _ByteArray_BrightnessImgForOCR = env->GetByteArrayElements(ByteArray_BrightnessImgForOCR, 0);
Mat img(ByteArray_BrightnessImgForOCR_h, ByteArray_BrightnessImgForOCR_w, CV_8UC1, (unsigned char *) _ByteArray_BrightnessImgForOCR);

And then write it to a png file.

imwrite("/mnt/sdcard/binaryImg_forOCR.png", img);

Of course, you need to take some time to get yourself familiar with OpenCV and Java native coding. Following OpenCV for Android examples, it is fast to learn.

user1914692
  • 3,033
  • 5
  • 36
  • 61