I have a byte array returned by the camera in input.
I need to scale down my image to make its size equal to 500KB. I am trying to achieve this using a Bitmap, but I cannot find how to get the proper compression
value.
public static byte[] compressCapture(byte[] capture) {
// How to get the right compression value ?
int compression = 50;
Bitmap bitmap = BitmapFactory.decodeByteArray(capture, 0, capture.length);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, compression, outputStream);
return outputStream.toByteArray();
}
The problem is that the compression is not proportional to the image size, meaning that compression
equals to 50 will not divide the image size by 2.
Is there a way to get a fixed image size in output that does not depend on the focus, or the smartphone model ?
EDIT :
I do not want to use a File (I work with confidential data).
UPDATE 01/16/2018
The simplest approach is to use BitmapFactory.Options.inSampleSize
to decode the byte array and compress it at the same time. Here is the doc
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; // If you want an image four times smaller than the original
Bitmap decoded = BitmapFactory.decodeByteArray(data, 0, data.length, options);
OLD ANSWER, PLEASE DON'T USE THIS
Since there is apparently no way to achieve this in one shot, I implemented an iterative process to reach a given size in KB.
I start with a compression coefficient equal to 80, and if it is not enough I decrease this coefficient and I retry until I get a size below my threshold.
static COMPRESSION_PERCENTAGE_START = 80;
static IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS = 3;
static IMAGE_COMPRESSION_STEP_PERCENT = 5;
// For logging
static IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS = 5;
static byte[] compressCapture(byte[] capture, int maxSizeKB) {
long maxSizeByte = ((long) maxSizeKB) * 1_000;
if (capture.length <= maxSizeByte) return capture;
byte[] compressed = capture;
// Chosen arbitrarily so that we can compress multiple times to reach the expected size.
int compression = COMPRESSION_PERCENTAGE_START;
Bitmap bitmap = BitmapFactory.decodeByteArray(capture, 0, capture.length);
ByteArrayOutputStream outputStream;
int iterations = 0;
while (compressed.length > maxSizeByte) {
// Just a counter
iterations++;
compression -= IMAGE_COMPRESSION_STEP_PERCENT;
outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, compression, outputStream);
compressed = outputStream.toByteArray();
}
if (iterations > IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS) {
Timber.w("Compression process has iterated more than expected, with " + iterations + " iterations.");
}
return compressed;
}
Here is the output size for an original size of 1_871_058 bytes
It does the job for me but please be careful if you use that, it certainly needs some fine tuning and I just tested it on a given smartphone model.