Как сохранить правильное повернутое изображение с помощью фронтальной камеры camerax?

Я использую CameraX для разработки своего приложения для Android, в котором, когда я делаю снимок в ландшафтном или портретном режиме, снимаемое и сохраняемое изображение является зеркальным.

Я знаю, что ПЕРЕДНЯЯ камера работает точно так же. Но что делать, если я хочу сохранить снимок в том виде, в котором он был сделан?

Вот код buildUseCase(), который я использую:

private fun buildUseCases() {

        val screenAspectRatio = Rational(width, height)
        val screenTargetRotation = display.rotation

        //Preview
        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(screenAspectRatio)
            setTargetRotation(screenTargetRotation)
            setLensFacing(lensFacing)
        }.build()

        preview = AutoFitPreviewBuilder.build(previewConfig, this)
        //End - Preview


        // Set up the capture use case to allow users to take photos
        val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
            setTargetAspectRatio(screenAspectRatio)
            setTargetRotation(screenTargetRotation)
            setLensFacing(lensFacing)
            setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY)
        }.build()


        imageCapture = ImageCapture(imageCaptureConfig)
    }

Пожалуйста, помогите мне с тем, что нужно изменить, чтобы получить правильное изображение.

Примечание. Камера направлена ​​ПЕРЕДНЯЯ ориентация в альбомном режиме.


person Shubham Agrawal    schedule 24.01.2020    source источник


Ответы (2)


Вам необходимо прочитать данные EXIF ​​созданного изображения и написать свои собственные пользовательские контроллеры в соответствии с требованиями и потребностями. На большинстве устройств Android и iOS это нормально, что захваченные изображения поворачиваются, и с этим нужно обращаться соответственно. В большинстве устройств ориентация камеры по умолчанию установлена ​​в альбомный режим, поэтому даже если вы делаете снимок в портретном режиме, он поворачивается на 90 градусов.

Из данных EXIF ​​вы можете получить степень поворота изображения или его зеркального отражения, а затем вы можете обработать его в бэкэнде.

Чтобы повернуть изображение, вы можете попробовать

private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException 
{
    ExifInterface ei = new ExifInterface(selectedImage.getPath());
    int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            return rotateImage(img, 90);
        case ExifInterface.ORIENTATION_ROTATE_180:
            return rotateImage(img, 180);
        case ExifInterface.ORIENTATION_ROTATE_270:
            return rotateImage(img, 270);
        default:
            return img;
    }
 }

private static Bitmap rotateImage(Bitmap img, int degree)
{
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
    img.recycle();
    return rotatedImg;
}

Для проблемы с переворачиванием изображения вы можете попробовать это

public static Bitmap flip(Bitmap src, int type) 
{
     // create new matrix for transformation
     Matrix matrix = new Matrix();
     matrix.preScale(-1.0f, 1.0f);

     // return transformed image
     return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
 }

А затем установите изображение в ImageView как

imgPreview.setImageBitmap(flip(bitmap)); 
person Blu    schedule 24.01.2020
comment
Привет! Есть ли способ использовать этот ответ, не сохраняя изображение. Другими словами: использовать ExifInterface и указать путь к нему из ImageProxy? Если я понимаю из этого ответа, вы сохраняете изображение, а затем используете Exif, чтобы получить информацию о вращении, верно? Что делать, если я просто хочу сделать снимок без сохранения, но проблема с поворотом не устранена. Спасибо! - person DM developing; 28.01.2021
comment
Будьте осторожны при использовании img.recycle();. Это вызвало у меня очень странную ошибку, которую трудно отследить, потому что в дальнейшем я использовал растровое изображение src в моем проекте. Я получил следующую ошибку: 2021-04-28 10:47:30.073 13573-15598/APPNAME A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x70 in tid 15598 (pool-7-thread-1), pid 13573 (ev.APPNAME ) - person Raul Lucaciu; 28.04.2021

Не совсем ответ, но может пригодиться, если вам нужно прочитать информацию EXIF ​​из байта [] вместо уже распакованного растрового изображения.

 /**
 * Returns the degrees, counted clockwise, from a byte[] instead of a already saved file.<br>
 *
 * @param jpeg the JPEG image byte[]
 * @return Exif orientation as either <b>0</b>, <b>90</b>, <b>180</b> or <b>270</b>
 */
public static int getExifOrientation(byte[] jpeg) {
    if (jpeg == null) {
        return 0;
    }

    int offset = 0;
    int length = 0;

    // ISO/IEC 10918-1:1993(E)
    while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
        int marker = jpeg[offset] & 0xFF;

        // Check if the marker is a padding.
        if (marker == 0xFF) {
            continue;
        }
        offset++;

        // Check if the marker is SOI or TEM.
        if (marker == 0xD8 || marker == 0x01) {
            continue;
        }
        // Check if the marker is EOI or SOS.
        if (marker == 0xD9 || marker == 0xDA) {
            break;
        }

        // Get the length and check if it is reasonable.
        length = pack(jpeg, offset, 2, false);
        if (length < 2 || offset + length > jpeg.length) {
            Log.e(TAG, "Invalid JPEG length");
            return 0;
        }

        // Break if the marker is EXIF in APP1.
        if (marker == 0xE1 && length >= 8 &&
                pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
                pack(jpeg, offset + 6, 2, false) == 0) {
            offset += 8;
            length -= 8;
            break;
        }

        // Skip other markers.
        offset += length;
        length = 0;
    }

    // JEITA CP-3451 Exif Version 2.2
    if (length > 8) {
        // Identify the byte order.
        int tag = pack(jpeg, offset, 4, false);
        if (tag != 0x49492A00 && tag != 0x4D4D002A) {
            Log.e(TAG, "Invalid JPEG EXIF byte order");
            return 0;
        }
        boolean littleEndian = (tag == 0x49492A00);

        // Get the offset and check if it is reasonable.
        int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
        if (count < 10 || count > length) {
            Log.e(TAG, "Invalid JPEG EXIF offset");
            return 0;
        }
        offset += count;
        length -= count;

        // Get the count and go through all the elements.
        count = pack(jpeg, offset - 2, 2, littleEndian);
        while (count-- > 0 && length >= 12) {
            // Get the tag and check if it is orientation.
            tag = pack(jpeg, offset, 2, littleEndian);
            if (tag == 0x0112) {
                // We do not really care about type and count, do we?
                int orientation = pack(jpeg, offset + 8, 2, littleEndian);
                switch (orientation) {
                    case 1:
                        return 0;
                    case 3:
                        return 180;
                    case 6:
                        return 90;
                    case 8:
                        return 270;
                }
                Log.i(TAG, "Unsupported EXIF orientation");
                return 0;
            }
            offset += 12;
            length -= 12;
        }
    }

    Log.i(TAG, "EXIF Orientation not found");
    return 0;
}

private static int pack(byte[] bytes, int offset, int length,
                        boolean littleEndian) {
    int step = 1;
    if (littleEndian) {
        offset += length - 1;
        step = -1;
    }

    int value = 0;
    while (length-- > 0) {
        value = (value << 8) | (bytes[offset] & 0xFF);
        offset += step;
    }
    return value;
}
person Shark    schedule 24.01.2020