1

I have a byte array retrieved from JNI part of my application. The input to JNI was also a byte array of bitmap picture from camera in ARGB8888 format. C++ has modified my bitmap to grayscale, using FastCV function. Here is my code:

Java:

private void fcvTest(String fileName) { //path to stored image 

        Bitmap bmp = BitmapFactory.decodeFile(fileName);
        //we are going from 32bit integer (ARGB8888) to 8bit byte (grayscale) => 4 * number of pixels:
        ByteBuffer argbBuf = ByteBuffer.allocate(bmp.getWidth() * bmp.getHeight() * 4); 
        bmp.copyPixelsToBuffer(argbBuf);
         byte[] grayBuffer = new byte[]{};
        grayBuffer = fcvGrayScale(argbBuf.array(), bmp.getWidth(), bmp.getHeight());


        Bitmap grayScaledPic = BitmapFactory.decodeByteArray(grayBuffer, 0, grayBuffer.length);

    }

private native byte[] fcvGrayScale(byte[] data, int w, int h);

C++:

JNIEXPORT jbyteArray JNICALL Java_com_qualcomm_loadjpeg_LoadJpeg_fcvGrayScale(JNIEnv* env, jobject obj, jbyteArray img, uint32_t w, uint32_t h) {

    jbyte* jImg;
    jboolean isCopy;
    jbyteArray grayToJava;
    jbyte* jbyteGray;

    jImg = env->GetByteArrayElements(img, &isCopy);
    uint8_t* uint8Gray = (uint8_t*) fcvMemAlloc(w*h*4, 16);

    fcvColorRGB888ToGrayu8((uint8_t*) jImg, w, h, 0, uint8Gray, 0);

    jbyteGray = (jbyte*) uint8Gray;
    grayToJava = env->NewByteArray(w*h); //no need for 4-more space for grayscale
    env->SetByteArrayRegion(grayToJava, 0, w*h, jbyteGray);

    env->ReleaseByteArrayElements(img, jImg, JNI_ABORT);
    fcvMemFree(uint8Gray);

    return grayToJava;
}

The documentation for FastCV function fcvColorRGB888ToGrayu8 can be seen here: fcvColorRGB888ToGrayu8 doc

As you can see in the title, I am not able to set my new bitmap grayScaledPic from JNI. Log cat gives me this usual statement:

SkImageDecoder::Factory returned null

It seems like bytes in my new array grayBuffer are not proper or I am overlooking some issue, for example setting the right format of bitmap via BitmapFactory.Options. Or input array even can be wrong, because fcvColorRGB888ToGrayu8 is expecting RGB888 as an input, but Android does not support this format. Does I have to remove alpha channel first?

Does anybody know, what else could be the point?

SOLVED >> see accepted answer

Problem was in decoding byte array by BitmapFactory.decodeByteArray - only works with compressed bitmaps (JPEG,PNG)

1 Answer 1

1

The problem is in your Java code:

Bitmap grayScaledPic = BitmapFactory.decodeByteArray(grayBuffer, 0, grayBuffer.length);

BitmapFactor.decodeByteArray() decodes a compressed image such as a JPG or PNG file that has been loaded into a byte array. It doesn't work on raw uncompressed data.

To set from a grey scale byte array you would have to use a different method. You can convert to an int array and then create the bitmap using Bitmap.createBitmap() e.g:

final int pixCount = bmp.getWidth() * bmp.getHeight();
int[] intGreyBuffer = new int[pixCount];
for(int i=0; i < pixCount; i++)
{
    int greyValue = (int)greyBuffer[i] & 0xff;
    intGreyBuffer[i] = 0xff000000 | (greyValue << 16) | (greyValue << 8) | greyValue;
}
Bitmap grayScaledPic = Bitmap.createBitmap(intGreyBuffer, bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);

Another way would be to return an int array from your JNI code instead of a byte array and then create the bitmap from it in Java directly.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, this helped. Now I ask myself, why this information isn't included in official Android documentation? Never mind, however I have another problem now. When I compress the bitmap and try to save it to a file, output image has wrong size and does not look like it should (absolutely). Integer array from your solution contains long negative values (-100000 and less). Is this correct?
Yes, it's correct, they are ARGB values, and the alpha is set to 255, so the high 8 bits including the sign bit are all set, resulting in large negative values. I don't know why the bitmap would be saving incorrectly.
Ok, I will study what exactly has happened. Here is the link for some example: grayscaled pic. It seems like only one part (a "column" with the lion head) is decoded back to bitmap and copied across whole image.
I think the problem is that fcvColorRGB888ToGrayu8 takes 3 bytes per pixel RGB888 data, but you are passing in 4 bytes per pixel ARGB8888 data

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.