使用 libjpegturbo 压缩批量图片时出现奇怪的结果
Posted
技术标签:
【中文标题】使用 libjpegturbo 压缩批量图片时出现奇怪的结果【英文标题】:Strange results while compressing batch of pictures with libjpegturbo 【发布时间】:2013-08-22 10:22:55 【问题描述】:首先,我(想要)做什么: 压缩和缩小一批图片(jpg)。 让我们假设原始图片的尺寸为 1600w x 1200h。 现在,我想要一份 1600x1200 的压缩副本以及另一份 800x600 和 400x300 的压缩副本。
我用什么: 我正在使用 libJpegTurob 来实现这一点。如果 LibJpegTurob 有问题,我尝试使用 android 给定的方法。
已经尝试过: 首先,我使用了从 Tom Gall (https://github.com/jberkel/libjpeg-turbo) 移植的 Java Wrapper。
在我开始使用超过 4mb 的图片之前,一切都很好(在 nexus 4 上)。 基本上发生的是 android 抛出 OutOfMemory 异常。当我使用较小的图片(~1-2mb)但又一张又一张地压缩时,就会发生这种情况。
在诸如 nexus 等内存较低的预算设备上运行后,情况变得更加糟糕。 低堆导致的问题,这就是我的想法。
那么,我想,我必须用 c 来做。 只要我在预算设备上使用小于 3mb 的图片,内存问题似乎就解决了。 在 nexus 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();
我做错了什么???
非常感谢!!
附:抱歉英语不好!
【问题讨论】:
本机代码...您是否尝试使用sbrk(incrementcount)
来增加您的堆?
试过这个,但也没有用。
【参考方案1】:
对我来说看起来不错。您是否确保 libjpegturbo 源代码有效且稳定?
【讨论】:
【参考方案2】:您的代码看起来不错,写得很好并且可以处理错误。但据我所知,问题可能是外部库中的错误,您可以通过卸载(或 uninit)以及重新加载和重新初始化库然后对下一个文件进行编码来测试它。
您也可以尝试使用旧版本的 lib,看看它是否有效。
第二个问题可能是指针在释放后未设置为 NULL 并开始将坏数据写入内存。您应该使用 valgrind 之类的工具,或者将所有 malloc 映射到它自己的页面中,并用只读内存页面填充它们(参见:mprotect),然后当您在错误页面中写入时,程序将出现 segfail 并且您会看到问题。
我不是这个库的专家,但请仔细检查文档和示例代码。也许需要在两个文件之间或每个文件之后调用一些东西,而第一个工作只是纯粹的运气。
另外:尝试更改您编码的文件的顺序,也许会有所帮助。
【讨论】:
以上是关于使用 libjpegturbo 压缩批量图片时出现奇怪的结果的主要内容,如果未能解决你的问题,请参考以下文章