Поворот байтового массива YUV на Android

Я хочу повернуть предварительный просмотр кадра YUV, полученный из Preview Callblack, до сих пор я создал этот пост, который содержит алгоритм поворота предварительного просмотра кадра, но портит изображение предварительного просмотра пиксели камеры повернуты

Другой способ повернуть изображение - создать jpg из изображения YUV, создать растровое изображение, повернуть растровое изображение и получить массив байтов растрового изображения, но мне действительно нужен формат в YUV (NV21).

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


person Francisco    schedule 05.01.2013    source источник
comment
Я также столкнулся с этой проблемой и в конечном итоге использовал решение, которое вы упомянули, используя Bitmap, чтобы повернуть изображение и вместо этого использовать JPEG. Действительно, установка ориентации дисплея не влияет на буфер, полученный PreviewCallback, как указано в документации: Это не влияет на порядок байтового массива, переданного в onPreviewFrame (byte [], Camera), изображения JPEG или записанные видео. < / я>   -  person bvidal    schedule 19.03.2013
comment
Привет. Удалось ли вам это решить? Большое спасибо.   -  person Paul    schedule 07.05.2013


Ответы (3)


Следующий метод позволяет повернуть массив байтов YUV420 на 90 градусов.

private byte[] rotateYUV420Degree90(byte[] data, int imageWidth, int imageHeight) 
{
    byte [] yuv = new byte[imageWidth*imageHeight*3/2];
    // Rotate the Y luma
    int i = 0;
    for(int x = 0;x < imageWidth;x++)
    {
        for(int y = imageHeight-1;y >= 0;y--)                               
        {
            yuv[i] = data[y*imageWidth+x];
            i++;
        }
    }
    // Rotate the U and V color components 
    i = imageWidth*imageHeight*3/2-1;
    for(int x = imageWidth-1;x > 0;x=x-2)
    {
        for(int y = 0;y < imageHeight/2;y++)                                
        {
            yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+x];
            i--;
            yuv[i] = data[(imageWidth*imageHeight)+(y*imageWidth)+(x-1)];
            i--;
        }
    }
    return yuv;
}

(Обратите внимание, что это может работать, только если ширина и высота имеют коэффициент 4)

person Johannes    schedule 02.04.2013
comment
Спасибо, мне действительно помогло! но у меня небольшая проблема. Когда я применяю этот фрагмент кода к байтам данных, мое изображение поворачивается до (-90). я хочу повернуть на +90. или простыми словами нужно вращать в противоположном направлении, не могли бы вы помочь мне, спасибо! - person Usama; 03.10.2014
comment
@ Усама: вы нашли решение, как повернуть его в другую сторону? Я тоже застрял здесь. Спасибо. - person Mihai; 12.03.2015
comment
Да, я вызываю ту же функцию 3 раза, передаю возвращаемые данные в качестве аргумента для следующего вызова и обмениваюсь параметрами ширины и высоты. - person Usama; 12.03.2015
comment
изображение повернуто на 90 градусов по часовой стрелке, но цвета не совпадают. Я сомневаюсь, что буфер, который я получил (из внешней библиотеки), является YUV или нет. Если я выгружаю полученный буфер прямо в файл и играю с ffplay, он отлично воспроизводит ffplay -v info -f rawvideo -video_size 320x240 yuv.bin - person toyvenu; 19.06.2020

Вот варианты поворота на другой угол (90, 180, 270):

public static byte[] rotateYUV420Degree90(byte[] data, int imageWidth, int imageHeight) {
    byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
    // Rotate the Y luma
    int i = 0;
    for (int x = 0; x < imageWidth; x++) {
        for (int y = imageHeight - 1; y >= 0; y--) {
            yuv[i] = data[y * imageWidth + x];
            i++;
        }
    }
    // Rotate the U and V color components
    i = imageWidth * imageHeight * 3 / 2 - 1;
    for (int x = imageWidth - 1; x > 0; x = x - 2) {
        for (int y = 0; y < imageHeight / 2; y++) {
            yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
            i--;
            yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth)
                    + (x - 1)];
            i--;
        }
    }
    return yuv;
}

private static byte[] rotateYUV420Degree180(byte[] data, int imageWidth, int imageHeight) {
    byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
    int i = 0;
    int count = 0;
    for (i = imageWidth * imageHeight - 1; i >= 0; i--) {
        yuv[count] = data[i];
        count++;
    }
    i = imageWidth * imageHeight * 3 / 2 - 1;
    for (i = imageWidth * imageHeight * 3 / 2 - 1; i >= imageWidth
            * imageHeight; i -= 2) {
        yuv[count++] = data[i - 1];
        yuv[count++] = data[i];
    }
    return yuv;
}

public static byte[] rotateYUV420Degree270(byte[] data, int imageWidth,
                                     int imageHeight) {
    byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
    int nWidth = 0, nHeight = 0;
    int wh = 0;
    int uvHeight = 0;
    if (imageWidth != nWidth || imageHeight != nHeight) {
        nWidth = imageWidth;
        nHeight = imageHeight;
        wh = imageWidth * imageHeight;
        uvHeight = imageHeight >> 1;// uvHeight = height / 2
    }
    // ??Y
    int k = 0;
    for (int i = 0; i < imageWidth; i++) {
        int nPos = 0;
        for (int j = 0; j < imageHeight; j++) {
            yuv[k] = data[nPos + i];
            k++;
            nPos += imageWidth;
        }
    }
    for (int i = 0; i < imageWidth; i += 2) {
        int nPos = wh;
        for (int j = 0; j < uvHeight; j++) {
            yuv[k] = data[nPos + i];
            yuv[k + 1] = data[nPos + i + 1];
            k += 2;
            nPos += imageWidth;
        }
    }
    return rotateYUV420Degree180(yuv, imageWidth, imageHeight);
}
person Qulery    schedule 30.09.2016
comment
Ваш метод поворота на 270º - это применение зеркального эффекта к изображению. Правильным решением должно быть: return rotateYuv420Degree180(rotateYuv420Degree90(data, imageWidth, imageHeight), imageWidth, imageHeight); Нет смысла вращать на 90 ° иначе, чем ваш исходный rotateYuv420Degree90 метод. - person Francisco Durdin Garcia; 27.09.2017
comment
обратите внимание, что эти методы довольно тяжелые и могут значительно повлиять на производительность, особенно когда вы вызываете два, как указано выше, предложенных Франциско. может подойти для фотосъемки / одиночного кадра, но обработка в реальном времени (например, при потоковой передаче) будет невозможна, особенно на более медленных / старых устройствах. эти методы также могут быть написаны на чистом / родном C / C ++ (NDK) или коде RenderScript, что в несколько раз эффективнее - person snachmsm; 28.08.2020

Вот как я это сделал

Этот блок кода настроен в другом месте

    Camera.Size size
    Rect rectangle = new Rect();
    rectangle.bottom = size.height;
    rectangle.top = 0;
    rectangle.left = 0;
    rectangle.right = size.width;

Это метод, который делает работу

    private Bitmap rotateBitmap(YuvImage yuvImage, int orientation, Rect rectangle)
    {
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    yuvImage.compressToJpeg(rectangle, 100, os);

    Matrix matrix = new Matrix();
    matrix.postRotate(orientation);
    byte[] bytes = os.toByteArray();
    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    return Bitmap.createBitmap(bitmap, 0 , 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

сжать YUVImage в JPEG, чтобы растровое изображение могло его обработать. Поверните растровое изображение, а затем экспортируйте его. Чтобы вернуть его в формат JPEG, как я и хотел, я использовал эту строку

image.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
person Steven    schedule 26.11.2013
comment
Это может вызвать артефакты и будет медленным, поскольку есть дополнительный этап кодирования / декодирования JPEG. Не рекомендую ни для чего другого, кроме тестирования. - person Sami Kuhmonen; 03.11.2015
comment
Слишком медленно, см. Комментарии Сами выше - person Guilherme Campos Hazan; 09.03.2019
comment
Его просто реализовать и понять, что упрощает развертывание, чем некоторые другие ответы. Это немного медленнее, чем абсолютно оптимальное решение, но для изображений, которые можно повернуть только один раз в жизни, это вполне приемлемо. - person Steven; 13.03.2019