Camera tips

From Android Wiki

Revision as of 08:39, 31 January 2013 by Ldo (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, searcha

Camera preview images are returned by default in a format known as “NV21”. This is a form of YUV encoding where colour information is sampled at a lower resolution than luminance (brightness), taking advantage of the fact that our eyes are not as sensitive to colour detail as they are to lightness detail.

Here are a pair of useful routines, one for determining how many bytes are necessary to hold an NV21-encoded image, while the other decodes the pixels to the usual Android ARGB Bitmap format. The latter will also apply a specified rotation (in whole multiples of 90°) to the image while decoding, which is necessary because the camera orientation and screen orientation may not match.

    public static int NV21DataSize
      (
        int Width,
        int Height
      )
      /* returns the size of a data buffer to hold an NV21-encoded image
        of the specified dimensions. */
      {
        return
                Width * Height
            +
                ((Width + 1) / 2) * ((Height + 1) / 2) * 2;
      } /*NV21DataSize*/

    public static void DecodeNV21
      (
        int SrcWidth, /* dimensions of image before rotation */
        int SrcHeight,
        byte[] Data, /* length = NV21DataSize(SrcWidth, SrcHeight) */
        int Rotate, /* [0 .. 3], angle is 90° * Rotate clockwise */
        int Alpha, /* set as alpha for all decoded pixels */
        int[] Pixels /* length = Width * Height */
      )
      /* decodes NV21-encoded image data, which is the default camera preview image format. */
      {
        final int AlphaMask = Alpha << 24;
      /* Rotation involves accessing either the source or destination pixels in a
        non-sequential fashion. Since the source is smaller, I figure it's less
        cache-unfriendly to go jumping around that. */
        final int DstWidth = (Rotate & 1) != 0 ? SrcHeight : SrcWidth;
        final int DstHeight = (Rotate & 1) != 0 ? SrcWidth : SrcHeight;
        final boolean DecrementRow = Rotate > 1;
        final boolean DecrementCol = Rotate == 1 || Rotate == 2;
        final int LumaRowStride = (Rotate & 1) != 0 ? 1 : SrcWidth;
        final int LumaColStride = (Rotate & 1) != 0 ? SrcWidth : 1;
        final int ChromaRowStride = (Rotate & 1) != 0 ? 2 : SrcWidth;
        final int ChromaColStride = (Rotate & 1) != 0 ? SrcWidth : 2;
        int dst = 0;
        for (int row = DecrementRow ? DstHeight : 0;;)
          {
            if (row == (DecrementRow ? 0 : DstHeight))
                break;
            if (DecrementRow)
              {
                --row;
              } /*if*/
            for (int col = DecrementCol ? DstWidth : 0;;)
              {
                if (col == (DecrementCol ? 0 : DstWidth))
                    break;
                if (DecrementCol)
                  {
                    --col;
                  } /*if*/
                final int Y = 0xff & (int)Data[row * LumaRowStride + col * LumaColStride]; /* [0 .. 255] */
              /* U/V data follows entire luminance block, downsampled to half luminance
                resolution both horizontally and vertically */
              /* decoding follows algorithm shown at
                <http://www.mail-archive.com/android-developers@googlegroups.com/msg14558.html>,
                except it gets red and blue the wrong way round */
                final int Cr =
                    (0xff & (int)Data[SrcHeight * SrcWidth + row / 2 * ChromaRowStride + col / 2 * ChromaColStride]) - 128;
                      /* [-128 .. +127] */
                final int Cb =
                    (0xff & (int)Data[SrcHeight * SrcWidth + row / 2 * ChromaRowStride + col / 2 * ChromaColStride + 1]) - 128;
                      /* [-128 .. +127] */
                Pixels[dst++] =
                        AlphaMask
                    |
                            Math.max
                              (
                                Math.min
                                  (
                                    (int)(
                                            Y
                                        +
                                            Cr
                                        +
                                            (Cr >> 1)
                                        +
                                            (Cr >> 2)
                                        +
                                            (Cr >> 6)
                                    ),
                                    255
                                  ),
                                  0
                              )
                        <<
                            16 /* red */
                    |
                            Math.max
                              (
                                Math.min
                                  (
                                    (int)(
                                            Y
                                        -
                                            (Cr >> 2)
                                        +
                                            (Cr >> 4)
                                        +
                                            (Cr >> 5)
                                        -
                                            (Cb >> 1)
                                        +
                                            (Cb >> 3)
                                        +
                                            (Cb >> 4)
                                        +
                                            (Cb >> 5)
                                    ),
                                    255
                                  ),
                                0
                              )
                        <<
                            8 /* green */
                    |
                        Math.max
                          (
                            Math.min
                              (
                                (int)(
                                        Y
                                    +
                                        Cb
                                    +
                                        (Cb >> 2)
                                    +
                                        (Cb >> 3)
                                    +
                                        (Cb >> 5)
                                ),
                                255
                              ),
                            0
                          ); /* blue */
                if (!DecrementCol)
                  {
                    ++col;
                  } /*if*/
              } /*for*/
            if (!DecrementRow)
              {
                ++row;
              } /*if*/
          } /*for*/
      } /*DecodeNV21*/

See Also

Personal tools