Android - dimensione e comprima um bitmap

Eu estou trabalhando em um aplicativo Android, que tem recurso de captura de câmera e upload de fotos. Se o dispositivo tiver uma câmera de alta resolução, o tamanho da imagem capturada será muito grande (1 a 3 MB ou mais).
Como o aplicativo precisará fazer o upload dessa imagem para o servidor, eu preciso compactar a imagem antes de fazer o upload. Se a câmera capturou uma foto em resolução total de 1920x1080, por exemplo, a saída ideal é manter uma proporção de 16: 9 da imagem, comprimi-la para uma imagem de 640x360 para reduzir a qualidade da imagem e torná-la menor em bytes.

Aqui está o meu código (referenciado no google):

/**
 * this class provide methods that can help compress the image size.
 *
 */
public class ImageCompressHelper {

/**
 * Calcuate how much to compress the image
 * @param options
 * @param reqWidth
 * @param reqHeight
 * @return
 */
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

    final int halfHeight = height / 2;
    final int halfWidth = width / 2;

    // Calculate the largest inSampleSize value that is a power of 2 and keeps both
    // height and width larger than the requested height and width.
    while ((halfHeight / inSampleSize) > reqHeight
            && (halfWidth / inSampleSize) > reqWidth) {
        inSampleSize *= 2;
    }
}

return inSampleSize;
}

/**
 * resize image to 480x800
 * @param filePath
 * @return
 */
public static Bitmap getSmallBitmap(String filePath) {

    File file = new File(filePath);
    long originalSize = file.length();

    MyLogger.Verbose("Original image size is: " + originalSize + " bytes.");

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);

    // Calculate inSampleSize based on a preset ratio
    options.inSampleSize = calculateInSampleSize(options, 480, 800);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;

    Bitmap compressedImage = BitmapFactory.decodeFile(filePath, options);

    MyLogger.Verbose("Compressed image size is " + sizeOf(compressedImage) + " bytes");

    return compressedImage;
}

O problema com o código acima é:

Não é possível manter a proporção, o código está forçando a imagem a ser redimensionada para 480x800. Se o usuário capturar uma imagem em outra proporção, a imagem não ficará boa após a compactação.

Não funciona bem. O código sempre alterará o tamanho da imagem para 7990272 bytes, independentemente do tamanho original do arquivo. Se o tamanho da imagem original já é muito pequeno, ela ficará grande (meu resultado de teste tira uma foto da minha parede, que é praticamente monocromática):

Original image size is: 990092 bytes.
Compressed image size is 7990272 bytes

Estou perguntando se há uma sugestão de uma maneira melhor de compactar a foto para que ela possa ser carregada sem problemas?

questionAnswers(2)

yourAnswerToTheQuestion