13

I want to do some image processing to the pixels gotten from the camera.

The problem is that the pixels from the camera are rotated 90 degrees.

Im getting the pixels inside the method onPreviewFrame(byte[] data, Camera camera)

I tried camera.setDisplayOrientation(90); and it displays the video in the correct orientation but I am still receiving the rotated pixels as stated in the documentation:

This does not affect the order of byte array passed in Android.Hardware.Camera.IPreviewCallback.OnPreviewFrame(Byte[], Android.Hardware.Camera), JPEG pictures, or recorded videos.

I also tried:

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

but that did not work.

I'm using android 2.2

enter image description here

Top image shows the SurfaceView when using camera.setDisplayOrientation(90);

The second image is gotten inside onPreviewFrame(byte[] data, Camera camera) from the data array. As you can see the data array comes rotated.

Enrique
  • 9,920
  • 7
  • 47
  • 59
  • I would strongly recommend to simply use `libyuv`. On Android, it will automatically choose a NEON implementation which delivers a huge improvement. – Alex Cohn Sep 24 '20 at 07:37

4 Answers4

8

While I'm a bit late to the party, here's a method I wrote that others might find useful to rotate a YUV420sp (NV21) image. None of the other methods I saw on SO seemed to actually do it.

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;
}
jake
  • 81
  • 1
  • 2
  • caution: This seems to originate from https://github.com/SMSSecure/SMSSecure/blob/master/src/org/smssecure/smssecure/util/BitmapUtil.java and the project is tagged GPLv3. Tools looking for GPL infringements can flag your program if you use this. – laalto Nov 25 '15 at 12:05
  • it originated from https://github.com/WhisperSystems/Signal-Android/blob/master/src/org/thoughtcrime/securesms/util/BitmapUtil.java source: I wrote it and put it there. – jake Nov 26 '15 at 23:17
  • Thanks, seems like the first hit project is a fork of your project. Anyway, there's GPLv3 and tools looking for GPL violations can flag code using this snippet. (For me: I was reviewing a piece of proprietary code where this snippet stood out and started googling where it comes from. It has now been removed from the proprietary codespace.) – laalto Nov 27 '15 at 07:35
  • `output[uOut] = (byte)(0xff & yuv[uIn]);` `ArrayIndexOutOFBoundsException` :( – Volodymyr Kulyk Oct 19 '16 at 11:09
  • while decoding this rotated byte array to bitmap it returns null? can you please help me on this – Shashwat Gupta Jan 18 '18 at 13:41
6

Maybe you can perform your image processing on the sideways frame, and only worry about rotation when you display it (which you have already done). If not, you need to rotate the frame the old fashioned way. I found some code somewhere (with a little modification) that might help:

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

This is easy because the createBitmap method supports a matrix transform: http://developer.android.com/reference/android/graphics/Bitmap.html#createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean)

chug2k
  • 5,106
  • 2
  • 25
  • 30
Matt Montag
  • 7,105
  • 8
  • 41
  • 47
4

If you are receiving a YUV420 byte array then the following method can rotate this by 90 degree.

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

(Note that this might only work if the width and height is a factor of 4)

Johannes
  • 737
  • 5
  • 10
2

You can rotate the raw data as follows:

// 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];
}

Where 'width' and 'height' are the pre-set sizes for your preview image using:

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

You can then use your rotated data accordingly.

I've only used this method for QR Code scanning and it appears to work perfectly. Not sure how it might effect other applications.

Scott
  • 2,593
  • 1
  • 22
  • 23
  • As long as the preview image you receive matches the width and height values then you shouldn't be getting an out of bound exception. This it the exact code I'm using at the moment and works great. – Scott Dec 20 '12 at 23:27
  • Raw data is getting rotated fine but the image quality goes down, after using your piece of code i am not able get proper image quality what i was getting before, can you explain some more points on rotating part or can given me any useful link for the same. Your help will be highly appreciated. – Daud Arfin Mar 02 '13 at 12:38
  • Hey Arfin, I haven't seen any change in image quality from using this code. Perhaps there is something else in your process causing the problem? All this code is doing is moving the pixels from their landscape position to a portrait one. – Scott Mar 05 '13 at 05:22
  • first thanks for reply, I guess because of size mismatch image quality is going down after rotation but as you already explained preview size should be same what exactly we have set to the camera and I am doing the same, I don't know what else is going wrong den?? Do you have any tutorial or links on pixel rotation part ?? – Daud Arfin Mar 05 '13 at 06:11
  • sorry to interrupt you again, if I want to rotate pixels by angle ex. 90, 180, 270 is it possible to do so ?? – Daud Arfin Mar 14 '13 at 06:47
  • You would be best using the method in the below answer for that (using matrix.postRotate...), if you want flexibility with the chosen angle. – Scott Mar 14 '13 at 22:23
  • I don't wanna go for bitmap decoding, the same way what you have rotated the byte array I want to rotate it by 90, 180 and 270.... if possible please let me know how to do it. – Daud Arfin Mar 15 '13 at 06:40
  • This only rotates the Y component of the image (the brightness), not the U and V (the colour information). Hence for a QR code this may be ok, but that could be the reason for the decreased image quality. – Laky Apr 27 '14 at 07:06
  • Correctly rotates a YuvImage from inside onPreviewFrame. – slott May 29 '15 at 10:58
  • This only rotates the Y (luminance) channel. Perfect for QR codes and many other CV applications that only use grayscale. – Alex Cohn Sep 24 '20 at 07:41