24

I'm looking to rotate a YUV frame preview recieved from a Preview Callblack, so far I've founded this post which cointains an algorithm to rotate the frame preview but is messing the preview image camera pixels rotated

another way to rotate the image will be creating a jpg out of the YUV image, create a bitmap, rotate a bitmap and obtaining the byte array of the bitmap, but I really need the format in YUV (NV21).

FYI. the reason I'm asking this is because I have a camera app that supports rotation, but the frame previews are coming back in landscape mode only.

Community
  • 1
  • 1
Francisco
  • 243
  • 1
  • 2
  • 5
  • 1
    I've also come across this problem and ended up using the solution you mention using `Bitmap` to rotate the image and use JPEG instead. Indeed setting the display orientation does not affect the buffer received by `PreviewCallback` as mentioned by the documentation: _This does not affect the order of byte array passed in onPreviewFrame(byte[], Camera), JPEG pictures, or recorded videos._ – bvidal Mar 19 '13 at 12:33
  • Hi. Did you managed to solve this? Many thanks. – Paul May 07 '13 at 18:23

3 Answers3

20

The following method can rotate a YUV420 byte array 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
  • 687
  • 5
  • 10
  • Thanks it really helps me! but i have a little problem When i apply this code snippet on my data bytes my image rotate to (-90). i want to rotate to +90. or in simple words need to rotate in opposite direction can you please help me out thanks! – Usama Oct 03 '14 at 06:40
  • @Usama: did you find a solution for rotating it the other way? I'm stuck here as well. Thanks. – Mihai Mar 12 '15 at 18:38
  • Yes i call same function 3 time,pass return data as a argument to the next call and exchange width height parameters.. – Usama Mar 12 '15 at 18:57
  • image is rotated clockwise 90deg but color mismatch. I'm in doubt whether buffer I received (from external lib) is YUV or not. If I dump the received buffer directly to a file and play with ffplay its playing fine ffplay -v info -f rawvideo -video_size 320x240 yuv.bin – toyvenu Jun 19 '20 at 11:49
8

Here are the options to turn a different corner (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);
}
Qulery
  • 173
  • 3
  • 11
  • 3
    Your method to rotate 270º it's applying a mirror effect on the image. The right solution should be : `return rotateYuv420Degree180(rotateYuv420Degree90(data, imageWidth, imageHeight), imageWidth, imageHeight);` There is no sense to rotate 90º in a different way that your original `rotateYuv420Degree90` method – Francisco Durdin Garcia Sep 27 '17 at 10:37
  • 1
    note that these methods are pretty heavy and may impact performance significantly, especially when you call two like above suggested by Francisco. may be fine for taking picture/single frame, but "live processing" (e.g. when streaming) won't be possible, especially on slower/older devices. these methods may be also written in pure/native C/C++ (NDK) or RenderScript code, few times more efficient – snachmsm Aug 28 '20 at 08:34
1

Here's how I did it

This block of code is setup elsewhere

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

This is the method that does the work

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

compress the YUVImage into a JPEG so that bitmap can handle it. Rotate the bitmap and then export it. To get it back to a JPEG which is how I wanted it I used this line

image.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
Steven
  • 3,010
  • 1
  • 15
  • 15
  • This may cause artefacts and will be slow since there is an extra JPEG encode/decode step. Wouldn't recommend for anything else than testing. – Sami Kuhmonen Nov 03 '15 at 07:44
  • Too slow, see Sami comments above – Guilherme Campos Hazan Mar 08 '19 at 21:11
  • It's simple to implement and understand making it easier to deploy than some of the other answer. It is a tiny bit slower than the absolute optimal solution but for images that may only be rotated once in their lives, it's perfectly acceptable. – Steven Mar 13 '19 at 13:52