4

I am trying to use jpeglib-turbo with android ndk to get the pixel rgb values of a jpeg image, and i am new to both C++,C and android NDK, till now i've tried using the solutions provided in Examples or tutorials of using libjpeg-turbo's TurboJPEG but i am unable to fix the issue at hand, Currently using https://github.com/openstf/android-libjpeg-turbo to build libjpeg-turbo.

Current code is

#include <turbojpeg.h>
#include <jni.h>
#include <android/log.h>
#include <syslog.h>



JNIEXPORT jbyteArray
Java_com_serelay_jpegturbo_MainActivity_getImagePixelData( JNIEnv*  env,
                                jobject  this,
                                jbyteArray data)
{
long unsigned int _jpegSize=2464742; //!< _jpegSize from above
unsigned char *_compressedImage; //!< _compressedImage from above

_compressedImage = data;

int width, height, jpegSubSamp;
// int jpegSubSamp = TJSAMP_444;


tjhandle _jpegDecompressor = tjInitDecompress();

tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, 
&height, &jpegSubSamp);

unsigned long len = width * height * 4;
unsigned long pitch = width * 4;
syslog(LOG_CRIT, "====================");
syslog(LOG_CRIT, "jpegSize %lu", _jpegSize);

syslog(LOG_CRIT, "width %d", width);
syslog(LOG_CRIT, "height %d", height);
syslog(LOG_CRIT, "total length %lu", len);

syslog(LOG_CRIT, "====================");



unsigned char buffer[len]; //!< will contain the decompressed image

tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, buffer, width, pitch, height, TJPF_RGBA, 0);

tjDestroy(_jpegDecompressor);


// char array to byte array 

jbyteArray jbytes = buffer;
//jbyteArray jbytes = _compressedImage;

return jbytes;
}

To call it from java i am using public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    System.loadLibrary("twolib-second");
    InputStream inputStream= getResources().openRawResource(R.raw.image1);
    byte[] bytes;
    try {
        bytes = new byte[inputStream.available()];
        inputStream.read(bytes);

        byte[] array = getImagePixelData(bytes);
        tv.setText(String.valueOf(getImagePixelData(bytes).length));

    } catch (IOException e) {
        e.printStackTrace();
    }

//        tv.setText(getImagePixelData(5));
}

/**
 * A native method that is implemented by the 'native-lib' native library,
 * which is packaged with this application.
 */
public native byte[] getImagePixelData(byte[] x);
}

I've been successfully calling from the code from java with the help of so files generated by ndk, but the issue i am faced was exceptions in the code which said
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xeaa6cc30

on further inspection i logged values of some variables which felt a bit bad considering the image being passed is of size 3840*2160, upon logging the variables i get

03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
====================
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
jpegSize 2464742
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
width 4
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
height -1386946560
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
total length 3578658816
03-07 20:26:05.180 13613-13613/com.serelay.jpegturbo E/com.serelay.jpegturbo: 
====================

i can see the width and height values are very wrong but i can't figure out why and how to fix it. It would be great if any help or directions can be provided to fix this issue, am stuck with this since last 2 days :) thanks.

Ankit Arora
  • 509
  • 2
  • 18
  • Your C code requires complete rewrite; you must learn how JNI works with byte arrays. But truth is, you don't need all this complexity here. To decode your Jpeg to pixel array using Android API, is one line in Java, and no less efficient. – Alex Cohn Mar 09 '18 at 20:42
  • I know the same can be achieved in java, but the requirement is to get the values with jpegturbo, that's because we need to have the same pixel values in multiple platforms. Currently if I use bitmap class the values are different for different android phones for the same image. – Ankit Arora Mar 09 '18 at 21:10
  • OK, that's [a valid cause](https://stackoverflow.com/questions/23565889/jpeg-images-have-different-pixel-values-across-multiple-devices). – Alex Cohn Mar 09 '18 at 21:26
  • 1
    thanks for pointing me in the right direction about the byte arrays – Ankit Arora Mar 12 '18 at 09:47

1 Answers1

1

Changes was required in tjDecompressHeader2 and we needed to use tjDecompressHeader3 in the library version, also we need to convert jbyteArray to unsigned char * and then back to jbyteArray in order to return a byte array to java, both of them can be observed in the code below.

#include <turbojpeg.h>
#include <jni.h>
#include <android/log.h>
#include <syslog.h>



JNIEXPORT jbyteArray
Java_com_serelay_jpegturbo_MainActivity_getImagePixelData( JNIEnv*  env,
                                jobject  this,
                                jbyteArray data,
                                jint dataLength)
{
long unsigned int _jpegSize=dataLength; //!< _jpegSize from above
unsigned char *_compressedImage; //!< _compressedImage from above
jboolean isCopy;

_compressedImage = (unsigned char*)(*env)->GetByteArrayElements(env, data, 
&isCopy);
int width, height, jpegSubSamp, jpegColorSpace;
// int jpegSubSamp = TJSAMP_444;


tjhandle _jpegDecompressor = tjInitDecompress();

tjDecompressHeader3(_jpegDecompressor, _compressedImage, _jpegSize, &width, 
&height, &jpegSubSamp, &jpegColorSpace);

long len = width * height * 4;
long pitch = width * 4;
syslog(LOG_CRIT, "====================");
syslog(LOG_CRIT, "jpegSize %lu", _jpegSize);

syslog(LOG_CRIT, "width %d", width);
syslog(LOG_CRIT, "height %d", height);
syslog(LOG_CRIT, "subsampl %d", jpegSubSamp);
syslog(LOG_CRIT, "colorSpace %d", jpegColorSpace);
    syslog(LOG_CRIT, "len %lu", len);
    syslog(LOG_CRIT, "pitch %lu", pitch);


syslog(LOG_CRIT, "====================");
syslog(LOG_CRIT, "===================0");



unsigned char* mumfer = (unsigned char*)malloc(len); //!< will contain the 
decompressed image
syslog(LOG_CRIT, "===================1");

tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, mumfer, width, 
0, height, TJPF_RGBA, TJFLAG_FASTDCT);
syslog(LOG_CRIT, "===================2");

tjDestroy(_jpegDecompressor);
syslog(LOG_CRIT, "===================3");


// char array to byte array 

jbyteArray array = (*env)->NewByteArray(env, len);
syslog(LOG_CRIT, "===================4");

//HERE I GET THE ERROR, I HAVE BEEN TRYING WITH len/2 and WORKS , PROBABLY 
SOME BYTS ARE GETTING LOST.
(*env)->SetByteArrayRegion (env, array, 0, len, (jbyte*)(mumfer));
syslog(LOG_CRIT, "===================5");
return array;
}

Also in java we need to convert the byte[] to int[] in order to get the exact pixel color values which can be done with the help of the following function

public static int byteToColorInt(byte b) {
    return b & 0xFF;
}
Ankit Arora
  • 509
  • 2
  • 18