相机像素旋转

Posted

技术标签:

【中文标题】相机像素旋转【英文标题】:camera pixels rotated 【发布时间】:2011-10-14 18:25:53 【问题描述】:

我想对从相机得到的像素做一些图像处理。

问题是来自相机的像素旋转了 90 度。

我正在获取方法onPreviewFrame(byte[] data, Camera camera)中的像素

我尝试了camera.setDisplayOrientation(90);,它以正确的方向显示视频,但我仍然收到文档中所述的旋转像素:

这不影响传入的字节数组的顺序 android.Hardware.Camera.IPreviewCallback.OnPreviewFrame(Byte[], Android.Hardware.Camera)、JPEG 图片或录制的视频。

我也试过了:

parameters.setRotation(90);
camera.setParameters(parameters);

但这没有用。

我使用的是安卓 2.2

上图显示使用camera.setDisplayOrientation(90);时的SurfaceView

第二张图片从data 数组中获取到onPreviewFrame(byte[] data, Camera camera)。如您所见,data 数组已旋转。

【问题讨论】:

我强烈建议使用libyuv。在 Android 上,它会自动选择一个带来巨大改进的 NEON 实现。 【参考方案1】:

虽然我参加聚会有点晚了,但这是我写的一种方法,其他人可能会发现它对旋转 YUV420sp (NV21) 图像很有用。我在 SO 上看到的其他方法似乎都没有真正做到这一点。

public static byte[] rotateNV21(final byte[] yuv,
                                final int width,
                                final int height,
                                final int rotation)

  if (rotation == 0) return yuv;
  if (rotation % 90 != 0 || rotation < 0 || rotation > 270) 
    throw new IllegalArgumentException("0 <= rotation < 360, rotation % 90 == 0");
  

  final byte[]  output    = new byte[yuv.length];
  final int     frameSize = width * height;
  final boolean swap      = rotation % 180 != 0;
  final boolean xflip     = rotation % 270 != 0;
  final boolean yflip     = rotation >= 180;

  for (int j = 0; j < height; j++) 
    for (int i = 0; i < width; i++) 
      final int yIn = j * width + i;
      final int uIn = frameSize + (j >> 1) * width + (i & ~1);
      final int vIn = uIn       + 1;

      final int wOut     = swap  ? height              : width;
      final int hOut     = swap  ? width               : height;
      final int iSwapped = swap  ? j                   : i;
      final int jSwapped = swap  ? i                   : j;
      final int iOut     = xflip ? wOut - iSwapped - 1 : iSwapped;
      final int jOut     = yflip ? hOut - jSwapped - 1 : jSwapped;

      final int yOut = jOut * wOut + iOut;
      final int uOut = frameSize + (jOut >> 1) * wOut + (iOut & ~1);
      final int vOut = uOut + 1;

      output[yOut] = (byte)(0xff & yuv[yIn]);
      output[uOut] = (byte)(0xff & yuv[uIn]);
      output[vOut] = (byte)(0xff & yuv[vIn]);
    
  
  return output;

【讨论】:

警告:这似乎来自github.com/SMSSecure/SMSSecure/blob/master/src/org/smssecure/…,并且该项目被标记为 GPLv3。如果你使用它,寻找 GPL 侵权的工具可以标记你的程序。 源自github.com/WhisperSystems/Signal-Android/blob/master/src/org/… 来源:我写的放在那里。 谢谢,第一个热门项目似乎是您项目的一个分支。无论如何,有 GPLv3 和寻找 GPL 违规的工具可以使用这个 sn-p 标记代码。 (对我来说:我正在审查一段专有代码,其中这个 sn-p 脱颖而出,并开始在谷歌上搜索它的来源。它现在已从专有代码空间中删除。) output[uOut] = (byte)(0xff &amp; yuv[uIn]); ArrayIndexOutOFBoundsException :( 在将此旋转字节数组解码为位图时返回 null?你能帮我解决这个问题吗【参考方案2】:

也许您可以在横向框架上执行图像处理,并且只在显示它时担心旋转(您已经完成了)。如果没有,您需要以老式方式旋转框架。我在某处找到了一些可能有帮助的代码(稍作修改):

// load the origial bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
       R.drawable.android);

int width = bitmap.width();
int height = bitmap.height();

// create a matrix for the manipulation
Matrix matrix = new Matrix();
// rotate the Bitmap 90 degrees (counterclockwise)
matrix.postRotate(90);

// recreate the new Bitmap, swap width and height and apply transform
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                  width, height, matrix, true);

这很简单,因为 createBitmap 方法支持矩阵变换: http://developer.android.com/reference/android/graphics/Bitmap.html#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)

【讨论】:

不工作的位图在 onpreviewframe 方法中返回 null【参考方案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 倍时才有效)

【讨论】:

不工作!请帮我解决这个问题 ArrayIndexOutOFBoundsException【参考方案4】:

您可以按如下方式旋转原始数据:

// Rotate the data for Portait Mode
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) 
    for (int x = 0; x < width; x++)
        rotatedData[x * height + height - y - 1] = data[x + y * width];

其中“宽度”和“高度”是预览图像的预设尺寸,使用:

Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(width, height);
camera.setParameters(parameters);

然后您可以相应地使用您的旋转数据。

我只将这种方法用于 QR 码扫描,并且看起来效果很好。不确定它会如何影响其他应用程序。

【讨论】:

只要您收到的预览图像与宽度和高度值匹配,那么您就不应该遇到越界异常。这是我目前使用的确切代码,效果很好。 原始数据旋转良好,但图像质量下降,使用您的代码后,我无法获得以前获得的正确图像质量,您能否解释一下旋转部分的更多要点或者可以给我任何有用的链接。我们将非常感谢您的帮助。 嘿 Arfin,我没有看到使用此代码对图像质量有任何改变。也许您的过程中还有其他原因导致问题?这段代码所做的就是将像素从横向位置移动到纵向位置。 首先感谢您的回复,我猜是因为尺寸不匹配,旋转后图像质量会下降,但正如您已经解释的那样,预览尺寸应该与我们为相机设置的完全相同,我也在做同样的事情,我不知道还有什么问题 den ?你有关于像素旋转部分的教程或链接吗?? 很抱歉再次打扰您,如果我想按角度旋转像素 ex。 90、180、270可以这样吗??

以上是关于相机像素旋转的主要内容,如果未能解决你的问题,请参考以下文章

如何不用标定板来确定相机的外参矩阵

记录相机成像原理

相机旋转时游戏对象旋转

检测 Microsoft Surface 上的相机旋转?

实现一个复杂的基于旋转的相机

三个JS:如何旋转物体而不是旋转相机?