Странные результаты при сжатии партии изображений с помощью libjpegturbo

Во-первых, что я (хочу) делать: сжимать и масштабировать партию изображений (jpg). Предположим, что исходное изображение имеет размеры 1600w x 1200h. Теперь я хочу иметь одну сжатую копию 1600×1200 и еще 800×600 и 400×300.

Что я использую: для этого я использую libJpegTurob. Если у LibJpegTurob есть некоторые проблемы, я пытаюсь использовать андроидные данные.

Уже пробовал: во-первых, я использовал Java Wrapper портирован из Tom Gall ( https://github.com/jberkel/libjpeg-turbo ).

Это было довольно хорошо (на nexus 4), пока я не начну использовать картинки размером более 4 МБ. Что в основном произошло, так это андроид выкидывать исключения OutOfMemory. Это произошло, когда я использовал меньшие снимки (~ 1-2 МБ), но сжимал один за другим.

Это стало даже худшим после того, как он запускал это на бюджетных устройствах с более низкой памятью, такой как nexus s. Проблема, вызванная низкой кучей, вот что я думаю.

Ну, тогда, подумал я, я должен сделать это с. Проблемы с памятью, по-видимому, решены, поскольку я использовал изображения меньшие, чем 3mb на бюджетном устройстве. На ядре 4 я мог даже сжать изображение> 15mb.

Это изображение src. Введите описание изображения здесь

Но теперь … проблема. Первое сжатое изображение выглядит хорошо Введите описание изображения здесь

Но все остальные выглядят так Введите описание изображения здесь или это Введите описание изображения здесь

Это произошло до тех пор, пока я сохраняю выбранные снимки и сжимаю их.

Теперь код.

Именно здесь произошло масштабирование и сжатие

#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; } 

Часть Java для начала сжатия.

 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(); } } } } 

Что я делаю неправильно ???

Большое спасибо!!

PS Извините за плохой английский!

Выглядит хорошо для меня. Вы убедились, что источники libjpegturbo действительны и стабильны?

Ваш код выглядит нормально, он хорошо написан и обрабатывает ошибки в порядке. Но из того, что я вижу, проблема может быть либо ошибкой во внешней lib, но вы можете проверить это путем разгрузки (или uninit) и перезагрузки и повторного использования библиотеки, а затем кодирования следующего файла.

Вы также можете попробовать более старую версию lib, чтобы узнать, работает ли она.

Вторая проблема может быть указателем, не установленным в NULL после бесплатного и начинающим записывать плохие данные в память. Вы должны использовать такие инструменты, как valgrind, или сопоставлять все свои malloc на своей собственной странице и заполнять их страницами readonly memory (см. Mprotect), а затем, когда вы пишете на плохой странице, программа будет segfail, и вы увидите проблему.

Я не являюсь экспертом в этой библиотеке, но дважды проверяю документацию и код примера. Возможно, есть что-то, что нужно вызывать между 2-мя файлами или после каждого из них, а первый из них – просто удача.

Также: попробуйте изменить порядок файлов, которые вы кодируете, возможно, это поможет.