3
votes

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.

2
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
Original grayscaled png is 1732K. The png created with the above procedure is 5445Krichardtz
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
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
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

2 Answers

5
votes

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/

2
votes

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.