Strange results when compressing a batch of images using libjpegturbo

First, what I (want) to do: compress and reduce the number of shots (jpg). Suppose the original image is 1600w x 1200h. Now I want to have one compressed copy of 1600x1200 and another 800x600 and 400x300.

What I use: For this I use libJpegTurob. If LibJpegTurob has some problems, I'm trying to use android data.

Already tried: First, I used Java Wrapper ported from Tom Gall ( https://github.com/jberkel/libjpeg-turbo ).

Everything went pretty well (on nexus 4) until I start using pictures larger than 4 MB. What basically happened is the android throwing OutOfMemory exceptions. This happened when I used smaller snapshots (~ 1-2 MB), but compressed one by one.

It got even worse when he ran it on budget devices with lower memory, such as the nexus s. The problem caused by the low heap is what I think.

Well then, I thought, I have to do this in c. The memory problems seem to be resolved as I used images smaller than 3mb on a budget device. On bunch 4, I could even compress the image> 15mb.

This is an src image. enter image description here

But now ... the problem. First compressed image looks good enter image description here

but everyone else looks like this: enter image description here or that enter image description here

This happened as long as I save the selected pictures and compress them.

Now the code.

Scaling and compression occurred here

#include "_HelloJNI.h" #include <errno.h> #include <jni.h> #include <sys/time.h> #include <time.h> #include <android/log.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <android/bitmap.h> #include <unistd.h> #include <setjmp.h> #include "jpeglib.h" #include "turbojpeg.h" #define LOG_TAG "DEBUG" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) int IMAGE_COMPRESS_QUALITY = 80; typedef struct { int width; int height; }tSize; JNIEXPORT jint JNICALL Java_com_example_LibJpegTurboTest_NdkCall_nativeCompress (JNIEnv * env, jobject onj, jstring jniSrcImgPath, jstring jniDestDir, jstring jniDestImgName, jint jniSrcWidth, jint jniSrcHeight) { int pyramidRet = 0; tSize fileSize; fileSize.width = (int)jniSrcWidth; fileSize.height = (int)jniSrcHeight; const char* srcImgPath = (*env)->GetStringUTFChars(env, jniSrcImgPath, 0); const char* destDir = (*env)->GetStringUTFChars(env, jniDestDir, 0); const char* destFileName = (*env)->GetStringUTFChars(env, jniDestImgName, 0); pyramidRet = createPreviewPyramidUsingCustomScaling(srcImgPath, destDir, destFileName, fileSize, 4); return 0; } static tSize imageSizeForStep(int step, tSize *originalSize) { float factor = 1 / pow(2, step); return (tSize) { round(originalSize->width * factor), round(originalSize->height * factor) }; } int saveBitmapBufferImage(unsigned char *data, tSize *imageSize, char *destFileName, int quality) { int retValue = 1; int res = 0; unsigned long destinationJpegBufferSize = 0; tjhandle tjCompressHandle = NULL; unsigned char *destinationJpegBuffer = NULL; FILE *file = NULL; // jpgeg compress tjCompressHandle = tjInitCompress(); if(tjCompressHandle == NULL) { retValue = -1; goto cleanup; } res = tjCompress2(tjCompressHandle, data, imageSize->width, imageSize->width * tjPixelSize[TJPF_RGBX], imageSize->height, TJPF_RGBX, &destinationJpegBuffer, &destinationJpegBufferSize, 1, quality, TJFLAG_FASTUPSAMPLE); if(res < 0) { retValue = -1; goto cleanup; } file = fopen(destFileName, "wb"); if(file == NULL) { retValue = -1; goto cleanup; } long written = fwrite(destinationJpegBuffer, destinationJpegBufferSize, 1, file); retValue = (written == 1); cleanup: if(tjCompressHandle) { tjDestroy(tjCompressHandle); } if(destinationJpegBuffer) { tjFree(destinationJpegBuffer); } if(file) { fclose(file); } return retValue; } int createBitmapBufferFromFile(char *srcFileName, tSize imageDimensions, long *bytesPerRow, long *dataBufferSize, unsigned char **dataBuffer) { int retValue = 1; int res = 0; FILE *file = NULL; unsigned char* sourceJpegBuffer = NULL; long sourceJpegBufferSize = 0; tjhandle tjDecompressHandle = NULL; int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0; unsigned char* temp = NULL; unsigned char* rotatedSourceJpegBuffer = NULL; tjhandle tjTransformHandle = NULL; file = fopen(srcFileName, "rb"); if (file == NULL) { retValue = -1; goto cleanup; } res = fseek(file, 0, SEEK_END); if(res < 0) { retValue = -1; goto cleanup; } sourceJpegBufferSize = ftell(file); if(sourceJpegBufferSize <= 0) { retValue = -1; goto cleanup; } sourceJpegBuffer = tjAlloc(sourceJpegBufferSize); if(sourceJpegBuffer == NULL) { retValue = -1; goto cleanup; } res = fseek(file, 0, SEEK_SET); if(res < 0) { retValue = -1; goto cleanup; } res = fread(sourceJpegBuffer, (long)sourceJpegBufferSize, 1, file); if(res != 1) { retValue = -1; goto cleanup; } tjDecompressHandle = tjInitDecompress(); if(tjDecompressHandle == NULL) { retValue = -1; goto cleanup; } // decompress header to get image dimensions res = tjDecompressHeader2(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, &fileWidth, &fileHeight, &jpegSubsamp); if(res < 0) { retValue = -1; goto cleanup; } float destWidth = (float)imageDimensions.width; float destHeight = (float)imageDimensions.height; *bytesPerRow = destWidth * tjPixelSize[TJPF_RGBX]; // buffer for uncompressed image-data *dataBufferSize = *bytesPerRow * destHeight; temp = tjAlloc(*dataBufferSize); if(temp == NULL) { retValue = -1; goto cleanup; } res = tjDecompress2(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, temp, destWidth, *bytesPerRow, destHeight, TJPF_RGBX, TJ_FASTUPSAMPLE); if(res < 0) { retValue = -1; goto cleanup; } *dataBuffer = temp; temp = NULL; cleanup: if(file) { fclose(file); } if(sourceJpegBuffer) { tjFree(sourceJpegBuffer); } if(tjDecompressHandle) { tjDestroy(tjDecompressHandle); } if(temp) { tjFree(temp); } return retValue; } int createPreviewPyramidUsingCustomScaling(char* srcImgPath, char* destDir, char* destFileName, tSize orginalImgSize, int maxStep) { int retValue = 1; int res = 1; int success = 0; int loopStep = 0; tSize previewSize; long bytesPerRow; long oldBytesPerRow = 0; unsigned char* sourceDataBuffer = NULL; long sourceDataBufferSize = 0; unsigned char* destinationDataBuffer = NULL; long destinationDataBufferSize = 0; unsigned char* buf1 = NULL; unsigned char* buf2 = NULL; long workBufSize = 0; void* sourceRow = NULL; void* targetRow = NULL; char* destFilePrefix = "sample_"; char* fooDestName; char* fooStrBuilder; tSize orginSizeTmp; orginSizeTmp.width = orginalImgSize.width; orginSizeTmp.height = orginalImgSize.height; previewSize = imageSizeForStep(1, &orginSizeTmp); long width = (long)previewSize.width; long height = (long)previewSize.height; int errorCode = 0; errorCode = createBitmapBufferFromFile(srcImgPath, previewSize, &bytesPerRow, &sourceDataBufferSize, &buf1); if(errorCode != 1) { retValue = errorCode; goto cleanup; } workBufSize = sourceDataBufferSize; buf2 = tjAlloc(workBufSize); if(buf2 == NULL) { retValue = -1; goto cleanup; } else { memset(buf2,0,workBufSize); } sourceDataBuffer = buf1; fooDestName = strcat(destDir, destFilePrefix); fooStrBuilder = strcat(fooDestName, "1_"); fooDestName = strcat(fooStrBuilder, destFileName); success = saveBitmapBufferImage(sourceDataBuffer, &previewSize, fooDestName, IMAGE_COMPRESS_QUALITY); if(success <= 0) { retValue = -1; goto cleanup; } cleanup: if(sourceDataBuffer) { tjFree(sourceDataBuffer); } if(destinationDataBuffer) { tjFree(destinationDataBuffer); } return retValue; } 

The Java part to start compression.

 private void invokeCompress(ArrayList<PictureItem> picturesToCompress) { if(picturesToCompress != null && picturesToCompress.size() > 0) { for(int i=0; i<picturesToCompress.size(); i++) { String srcPicturePath = picturesToCompress.get(i).getSrcImg(); String destDir = "/storage/emulated/0/1_TEST_FOLDER/"; String destFileName = getRandomString(4)+".jpg"; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(srcPicturePath, options); try { ndkCall.compress(srcPicturePath, destDir, destFileName, options.outWidth, options.outHeight); } catch(Exception e) { e.printStackTrace(); } } } } 

What am I doing wrong???

Thanks a lot!

PS Sorry for the bad English!

+4
source share
2 answers

It looks good to me. Are you convinced that the sources of libjpegturbo are reliable and stable?

+3
source

Your code looks fine, it is well written and handles errors in order. But from what I see, the problem may be either an error in the external library, but you can check it by unloading (or uninit) and reloading and reusing the library and then encoding the next file.

You can also try an older version of lib to see if it works.

The second problem may be a pointer that is not set to NULL after freeing and begins to write bad data to memory. You should use tools like valgrind, or match all your mallocs on your own page and populate them with readonly memory pages (see Mprotect), and then when you write on a bad page, the program will segfail and you will see a problem.

I am not an expert in this library, but I double-check the documentation and sample code. Perhaps there is something that needs to be called between 2 files or after each of them, and the first of them is just luck.

Also: try changing the order of the files you encode, maybe this will help.

+2
source

Source: https://habr.com/ru/post/1498354/


All Articles