在 Android 上旋转 YUV 字节数组

Posted

技术标签:

【中文标题】在 Android 上旋转 YUV 字节数组【英文标题】:Rotate an YUV byte array on Android 【发布时间】:2012-12-19 12:44:45 【问题描述】:

我正在寻找旋转从 Preview Callblack 收到的 YUV 帧预览,到目前为止,我已经创建了这篇文章,其中包含一种算法来旋转帧预览,但正在弄乱预览图像 camera pixels rotated

另一种旋转图像的方法是从YUV图像中创建一个jpg,创建一个位图,旋转一个位图并获得位图的字节数组,但我确实需要YUV(NV21)中的格式。

仅供参考。我问这个的原因是因为我有一个支持旋转的相机应用程序,但帧预览仅以横向模式返回。

【问题讨论】:

我也遇到过这个问题,最终使用了您提到的解决方案,使用Bitmap 来旋转图像并改用JPEG。确实设置显示方向不会影响PreviewCallback 接收到的缓冲区,如文档所述:这不会影响 onPreviewFrame(byte[], Camera)、JPEG 图片或录制的视频中传入的字节数组的顺序. 嗨。你设法解决了这个问题吗?非常感谢。 【参考方案1】:

以下方法可以将 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 的情况下才有效)

【讨论】:

谢谢它真的帮助了我!但我有一个小问题当我在我的数据字节上应用这个代码 sn-p 时,我的图像旋转到(-90)。我想旋转到+90。或者简单来说需要向相反的方向旋转,你能帮我吗,谢谢! @Usama:你找到了另一种方式旋转它的解决方案吗?我也被困在这里。谢谢。 是的,我调用相同的函数 3 次,将返回数据作为参数传递给下一次调用并交换宽度高度参数.. 图像顺时针旋转 90 度,但颜色不匹配。我怀疑我收到的缓冲区(来自外部库)是否是 YUV。如果我将接收到的缓冲区直接转储到文件中并使用 ffplay 播放它的播放效果很好 ffplay -v info -f rawvideo -video_size 320x240 yuv.bin【参考方案2】:

以下是转向不同拐角(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);

【讨论】:

您旋转 270º 的方法是在图像上应用镜像效果。正确的解决方案应该是:return rotateYuv420Degree180(rotateYuv420Degree90(data, imageWidth, imageHeight), imageWidth, imageHeight); 以与您原来的 rotateYuv420Degree90 方法不同的方式旋转 90º 是没有意义的 请注意,这些方法非常繁重,可能会显着影响性能,尤其是当您像上面由 Francisco 建议的那样调用两个时。可能适合拍照/单帧,但无法进行“实时处理”(例如流式传输时),尤其是在较慢/较旧的设备上。这些方法也可以用纯/原生 C/C++ (NDK) 或 RenderScript 代码编写,效率高几倍【参考方案3】:

我是这样做的

这段代码在别处设置

    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);

【讨论】:

这可能会导致伪影并且会很慢,因为有一个额外的 JPEG 编码/解码步骤。除了测试,不推荐其他任何东西。 太慢了,见上面的萨米族cmets 它很容易实现和理解,比其他一些答案更容易部署。它比绝对最佳解决方案慢一点,但对于一生中可能只旋转一次的图像,这是完全可以接受的。

以上是关于在 Android 上旋转 YUV 字节数组的主要内容,如果未能解决你的问题,请参考以下文章

在 onPictureTaken 之后旋转 JPEG 的字节数组

Android:三星设备自动旋转图像

在android上的opencv中创建yuv mat

如何在 Android 中有效地动态操作 YUV 相机帧?

Android音视频视频数据存储方式YUV

33. 搜索旋转排序数组-字节跳动高频题