何らかの処理を行うために、カメラフィードを取り、それをRGBに変換するアプリを作成しています。
NV21 Yuv形式を使用する古いカメラの実装で正常に動作します。私が抱えている問題は、新しいYuv形式、YUV_420_888です。 NV21(YUV_420_SP)形式ではなくYUV_420_888 yuv形式を送信する新しいCamera2 Apiで、画像がRGBに正しく変換されなくなりました。
YUV_420_888をRGBに変換する方法を教えてください。
ありがとう
私のアプローチでは、OpenCVマットと https://Gist.github.com/camdenfullmer/dfd83dfb0973663a7974 のスクリプトを使用します
まず、上のリンクのコードを使用して、YUV_420_888画像をマットに変換します。
* mImageは、ImageReader.OnImageAvailableListenerで取得する私のImageオブジェクトです。
Mat mYuvMat = imageToMat(mImage);
public static Mat imageToMat(Image image) {
ByteBuffer buffer;
int rowStride;
int pixelStride;
int width = image.getWidth();
int height = image.getHeight();
int offset = 0;
Image.Plane[] planes = image.getPlanes();
byte[] data = new byte[image.getWidth() * image.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8];
byte[] 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;
int 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);
if (h - row != 1) {
buffer.position(buffer.position() + rowStride - length);
}
offset += length;
} else {
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 mat = new Mat(height + height / 2, width, CvType.CV_8UC1);
mat.put(0, 0, data);
return mat;
}
1チャンネルのYUVマットがあります。 BGR(まだRGBではない)画像の新しいマットを定義:
Mat bgrMat = new Mat(mImage.getHeight(), mImage.getWidth(),CvType.CV_8UC4);
私はOpenCVの学習を始めたばかりなので、おそらくこれは4チャンネルのマットである必要はなく、代わりに3チャンネルでもかまいませんが、私にとってはうまくいきます。次に、色の変換方法を使用して、yuvマットをbgrマットに変更します。
Imgproc.cvtColor(mYuvMat, bgrMat, Imgproc.COLOR_YUV2BGR_I420);
これで、輪郭、色、円などのすべての画像処理を実行できます。画像を画面に印刷するには、画像をビットマップに変換する必要があります。
Mat rgbaMatOut = new Mat();
Imgproc.cvtColor(bgrMat, rgbaMatOut, Imgproc.COLOR_BGR2RGBA, 0);
final Bitmap bitmap = Bitmap.createBitmap(bgrMat.cols(), bgrMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(rgbaMatOut, bitmap);
すべての画像処理を別のスレッドで行っているので、ImageViewを設定するには、UIスレッドでこれを行う必要があります。
runOnUiThread(new Runnable() {
@Override
public void run() {
if(bitmap != null) {
mImageView.setImageBitmap(bitmap);
}
}
});
Camera2 YUV_420_888からJavaのRGB Mat(opencv)
@Override
public void onImageAvailable(ImageReader reader){
Image image = null;
try {
image = reader.acquireLatestImage();
if (image != null) {
byte[] nv21;
ByteBuffer yBuffer = mImage.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = mImage.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = mImage.getPlanes()[2].getBuffer();
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
nv21 = new byte[ySize + uSize + vSize];
//U and V are swapped
yBuffer.get(nv21, 0, ySize);
vBuffer.get(nv21, ySize, vSize);
uBuffer.get(nv21, ySize + vSize, uSize);
Mat mRGB = getYUV2Mat(nv21);
}
} catch (Exception e) {
Log.w(TAG, e.getMessage());
}finally{
image.close();// don't forget to close
}
}
public Mat getYUV2Mat(byte[] data) {
Mat mYuv = new Mat(image.getHeight() + image.getHeight() / 2, image.getWidth(), CV_8UC1);
mYuv.put(0, 0, data);
Mat mRGB = new Mat();
cvtColor(mYuv, mRGB, Imgproc.COLOR_YUV2RGB_NV21, 3);
return mRGB;
}
このスクリプトを使用してみましたか? this 質問に対して yydcdut が投稿した回答です
https://github.com/pinguo-yuyidong/Camera2/blob/master/camera2/src/main/rs/yuv2rgb.rs
使用してくださいShyam Kumarの答えは私の電話には正しくありませんが、DanielWięcekの答えは正しいです。両方1200。
私の評判は3なので、コメントせずに回答を投稿できます。
上記の「imageToMat」関数より約10倍高速なのは次のコードです。
Image image = reader.acquireLatestImage();
...
Mat yuv = new Mat(image.getHeight() + image.getHeight() / 2, image.getWidth(), CvType.CV_8UC1);
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
final byte[] data = new byte[buffer.limit()];
buffer.get(data);
yuv.put(0, 0, data);
...
image.close();
そのため、まったく同じ問題が発生しました。OnPreviewFrame()から古いYUV_420_SP形式のbyte []データを取得して、RGBに変換するコードがありました。
ここで重要なのは、byte []の「古い」データがYYYYYY ... CrCbCrCbCrCbであり、Camera2 APIからの「新しい」データが3つのプレーンに分割されることです:0 = Y、1 = Cb、2 = Cr 。、各バイト[]を取得できる場所。そのため、新しいデータを「古い」形式に一致する単一の配列として並べ替えるだけで、既存のtoRGB()関数に渡すことができます。
Image.Plane[] planes = image.getPlanes(); // in YUV220_888 format
int acc = 0, i;
ByteBuffer[] buff = new ByteBuffer[planes.length];
for (i = 0; i < planes.length; i++) {
buff[i] = planes[i].getBuffer();
acc += buff[i].capacity();
}
byte[] data = new byte[acc],
tmpCb = new byte[buff[1].capacity()] , tmpCr = new byte[buff[2].capacity()];
buff[0].get(data, 0, buff[0].capacity()); // Y
acc = buff[0].capacity();
buff[2].get(tmpCr, 0, buff[2].capacity()); // Cr
buff[1].get(tmpCb, 0, buff[1].capacity()); // Cb
for (i=0; i<tmpCb.length; i++) {
data[acc] = tmpCr[i];
data[acc + 1] = tmpCb[i];
acc++;
}
..そして今、data []は古いYUV_420_SPと同じようにフォーマットされています。
(何年経っても、それが誰かを助けることを願っています。)