2017-12-11 7 views
1

を繰り返し888私はImageは、OpenCVの行列にカメラ2 APIを使用してImageReaderから受け取ったとCameraBridgeViewBase、より具体的に機能deliverAndDrawFrameを使用して画面に表示変換しようとしています。読者のImageFormatYUV_420_888です。私が理解する限り、各ピクセルのグレースケール値を持つYプレーンと、4つのピクセルごとに1つおきにU/Vを持つUプレーンがあります。しかし、この画像を表示しようとすると、画像が繰り返して90度回転したように見えます。以下のコードは、(ちょうどRGBA、今のところありませんグレースケール)OpenCVの行列にYUVデータを置くことになっている:アンドロイドImageReaderのYUV 420データ

/** 
* Takes an {@link Image} in the {@link ImageFormat#YUV_420_888} and puts it into a provided {@link Mat} in rgba format. 
* 
* @param yuvImage {@link Image} in the {@link ImageFormat#YUV_420_888} format. 
*/ 
public static void yuv420888imageToRgbaMat(final Image yuvImage, final Mat rgbaMat) { 

    final Image.Plane 
      Yp  = yuvImage.getPlanes()[0], 
      UandVp = yuvImage.getPlanes()[1]; 

    final ByteBuffer 
      Ybb  = Yp .getBuffer(), 
      UandVbb = UandVp.getBuffer(); 

    Ybb .get(mYdata , 0, 480*640  ); 
    UandVbb.get(mUandVData, 0, 480*640/2 - 8); 

    for (int i = 0; i < 640*480; i++) { 
     for (int j = 0; j < 4; j++) { 
      mRawRGBAFrameData[i + 640*480*j] = mYdata[i]; 
     } 
     mRawRGBAFrameData[i*4 ] = mYdata[i]; 
     mRawRGBAFrameData[i*4+1] = mYdata[i]; 
     mRawRGBAFrameData[i*4+2] = mYdata[i]; 
     mRawRGBAFrameData[i*4+3] = -1; 
    } 
} 

ここでOpenCVのフレームのために私のコードです:

private class CameraFrame implements CvCameraViewFrame { 

    private Mat mRgba; 

    @Override 
    public Mat gray() { 

     return null; 
    } 

    @Override 
    public Mat rgba() { 

     mRgbaMat.put(0, 0, mRawRGBAFrameData); 

     return mRgba; 
    } 

    public CameraFrame(final Mat rgba) { 

     super(); 

     mRgba = rgba; 
    } 
} 

ためのコードフレーム描画受信:

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { 

    @Override 
    public void onImageAvailable(ImageReader reader) { 

     final Image yuvImage = reader.acquireLatestImage(); 

     yuv420888imageToRgbaMat(yuvImage, mRgbaMat); 

     deliverAndDrawFrame(mFrame); 

     yuvImage.close(); 
    } 
}; 

および、この画像読取装置を製造するためのコードである:

mRgbaMat = new Mat(mFrameHeight, mFrameWidth, CvType.CV_8UC4); 

mFrame = new CameraFrame(mRgbaMat); 

mImageReader = ImageReader.newInstance(mFrameWidth, mFrameHeight, ImageFormat.YUV_420_888, 1); 

mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); 

AllocateCache(); 

これは、アレイの初期化である:

protected static byte[] mRawRGBAFrameData = new byte[640*480*4], mYdata = new byte[640*480], mUandVData = new byte[640*480/2]; 

注意:mFrameWidth 480とmFrameHeightでは、640の一つ奇妙なことは、ImageReaderそれから受信Imageための高さと幅は寸法が反転していることです。 https://i.stack.imgur.com/lcdzf.pngここ

これにより画像の代わりyuv420888imageToRgbaMathttps://i.stack.imgur.com/T2MOI.png

for (int i = 0; i < 640*480; i++) { 
    mRawRGBAFrameData[i] = mYdata[i]; 
} 

に我々はデータYのフレームに繰り返し、いくつかの理由のためであることが確認できている:ここでは

は、上記のコードを持つ画像でありますこれは実際の見栄えのよい画像を与える。

答えて

0

カメラ2 APIでOpenCVを使用しようとするのと同じ問題を抱える人にとって、私は解決策を思いついた。私が発見した最初の事は、ImageReaderが提供しているByteBufferにパディングがあるという事実でした。そのため、あなたがそれを考慮しなければ、出力に歪みが生じる可能性があります。私が選択した別のものは、自分でSurfaceViewを作成し、CameraViewBaseの代わりにBitmapを使用して描画し、それまでのところうまくいきました。 OpenCVには、BGR行列をとり、それをアンドロイドBitmapに変換する関数Util.matToBitmapがあります。 ImageReaderによって供給される最初の2つの情報を、YUV420としてフォーマットされたOpenCVの1つのチャネル行列に入れ、Imgproc.cvtColorImgproc.COLOR_YUV420p2BGRで使用して、BGR行列を取得します。知っておくべき重要なことは、画像のY平面が完全なピクセルを持ちますが、2番目のUV平面には1〜4つのYピクセルをマッピングするインターリーブピクセルがあるため、UV平面の全長はY平面の半分です。 hereを参照してください。とにかく、ここでいくつかのコードは次のとおりです。行列

m_BGRMat = new Mat(Constants.VISION_IMAGE_HEIGHT, Constants.VISION_IMAGE_WIDTH, CvType.CV_8UC3); 
m_Yuv420FrameMat = new Mat(Constants.VISION_IMAGE_HEIGHT * 3/2, Constants.VISION_IMAGE_WIDTH, CvType.CV_8UC1); 

すべてのフレームの

初期化:ここで

// Convert image to YUV 420 matrix 
ImageUtils.imageToMat(image, m_Yuv420FrameMat, m_RawFrameData, m_RawFrameRowData); 
// Convert YUV matrix to BGR matrix 
Imgproc.cvtColor(m_Yuv420FrameMat, m_BGRMat, Imgproc.COLOR_YUV420p2BGR); 
// Flip width and height then mirror vertically 
Core.transpose(m_BGRMat, m_BGRMat); 
Core.flip(m_BGRMat, m_BGRMat, 0); 
// Draw to Surface View 
m_PreviewView.drawImageMat(m_BGRMat); 

は420行列をYUVへの変換である:

/** 
* Takes an Android {@link Image} in the {@link ImageFormat#YUV_420_888} format and returns an OpenCV {@link Mat}. 
* 
* @param image {@link Image} in the {@link ImageFormat#YUV_420_888} format 
*/ 
public static void imageToMat(final Image image, final Mat mat, byte[] data, byte[] rowData) { 
    ByteBuffer buffer; 
    int rowStride, pixelStride, width = image.getWidth(), height = image.getHeight(), offset = 0; 
    Image.Plane[] planes = image.getPlanes(); 
    if (data == null || data.length != width * height) data = new byte[width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888)/8]; 
    if (rowData == null || rowData.length != planes[0].getRowStride()) rowData = new byte[planes[0].getRowStride()]; 
    for (int i = 0; i < planes.length; i++) { 
     buffer = planes[i].getBuffer(); 
     rowStride = planes[i].getRowStride(); 
     pixelStride = planes[i].getPixelStride(); 
     int 
      w = (i == 0) ? width : width/2, 
      h = (i == 0) ? height : height/2; 
     for (int row = 0; row < h; row++) { 
      int bytesPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888)/8; 
      if (pixelStride == bytesPerPixel) { 
       int length = w * bytesPerPixel; 
       buffer.get(data, offset, length); 
       // Advance buffer the remainder of the row stride, unless on the last row. 
       // Otherwise, this will throw an IllegalArgumentException because the buffer 
       // doesn't include the last padding. 
       if (h - row != 1) 
        buffer.position(buffer.position() + rowStride - length); 
       offset += length; 
      } else { 
       // On the last row only read the width of the image minus the pixel stride 
       // plus one. Otherwise, this will throw a BufferUnderflowException because the 
       // buffer doesn't include the last padding. 
       if (h - row == 1) 
        buffer.get(rowData, 0, width - pixelStride + 1); 
       else 
        buffer.get(rowData, 0, rowStride); 
       for (int col = 0; col < w; col++) 
        data[offset++] = rowData[col * pixelStride]; 
      } 
     } 
    } 
    mat.put(0, 0, data); 
} 

そして最後に、図表

/** 
* Given an {@link Mat} that represents a BGR image, draw it on the surface canvas. 
* use the OpenCV helper function {@link Utils#matToBitmap(Mat, Bitmap)} to create a {@link Bitmap}. 
* 
* @param bgrMat BGR frame {@link Mat} 
*/ 
public void drawImageMat(final Mat bgrMat) { 
    if (m_HolderReady) { 
     // Create bitmap from BGR matrix 
     Utils.matToBitmap(bgrMat, m_Bitmap); 
     // Obtain the canvas and draw the bitmap on top of it 
     final SurfaceHolder holder = getHolder(); 
     final Canvas canvas = holder.lockCanvas(); 
     canvas.drawBitmap(m_Bitmap, null, new Rect(0, 0, m_HolderWidth, m_HolderHeight), null); 
     holder.unlockCanvasAndPost(canvas); 
    } 
} 

この方法でうまくいきますが、それを行う最も良い方法は、OpenGLレンダリングコンテキストを設定し、マトリックスを表示するためのシンプルなシェーダを書き込むことです。

関連する問題