web-dev-qa-db-ja.com

OpenCVでYUVファイルからフレームを読み取る方法は?

OpenCVでYUVファイルからフレームを読み取る方法は?

11
Jason

バイナリファイルからYUVNV21ストリームを読み取るための非常に単純なpythonコードを作成しました。

import cv2
import numpy as np

class VideoCaptureYUV:
    def __init__(self, filename, size):
        self.height, self.width = size
        self.frame_len = self.width * self.height * 3 / 2
        self.f = open(filename, 'rb')
        self.shape = (int(self.height*1.5), self.width)

    def read_raw(self):
        try:
            raw = self.f.read(self.frame_len)
            yuv = np.frombuffer(raw, dtype=np.uint8)
            yuv = yuv.reshape(self.shape)
        except Exception as e:
            print str(e)
            return False, None
        return True, yuv

    def read(self):
        ret, yuv = self.read_raw()
        if not ret:
            return ret, yuv
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_NV21)
        return ret, bgr


if __name__ == "__main__":
    #filename = "data/20171214180916RGB.yuv"
    filename = "data/20171214180916IR.yuv"
    size = (480, 640)
    cap = VideoCaptureYUV(filename, size)

    while 1:
        ret, frame = cap.read()
        if ret:
            cv2.imshow("frame", frame)
            cv2.waitKey(30)
        else:
            break
8
xianyanlin

前述のように、YUVフォーマットには多くの種類があります。

http://www.fourcc.org/yuv.php

OpenCVでYUV形式からRGBに変換するのは非常に簡単です。

  1. そのフレームデータに適したサイズの1次元OpenCVマットを作成します
  2. 目的の寸法と3チャンネルのRGBデータ用の空のマットを作成します
  3. 最後に、cvtColorを使用して、正しい変換フラグ列挙型を使用して2つのマット間で変換します

YV12形式のYUVバッファの例を次に示します。

Mat mYUV(height + height/2, width, CV_8UC1, (void*) frameData);
Mat mRGB(height, width, CV_8UC3);
cvtColor(mYUV, mRGB, CV_YUV2RGB_YV12, 3);

重要なトリックは、RGBマットの寸法を定義することです変換します。

5
Aaron Becker

[〜#〜] update [〜#〜]ここに新しいバージョンのコードがあります: https://github.com/ chelyaev/opencv-yuv

singleYUV 4:2:0平面画像ファイルを読み取るコードを投稿しています。これはほとんどのYUVファイルに直接適用できます(同じFILEオブジェクトから読み取りを続けます)。これに対する例外は、 ヘッダーを持つYUVファイル (通常、*.y4m拡張子)。このようなファイルを処理する場合は、次の2つのオプションがあります。

  1. 以下のコードを使用する前に、FILEオブジェクトのヘッダーデータを使用する独自の関数を記述してください
  2. * .y4m画像からヘッダーを削除します(ffmpegまたは同様のツールを使用)。これは最も単純なので、私が好むオプションです。

また、他の形式のYUVフォーマット(非平面、異なるクロマデシメーション)では機能しません。 @Stephaneが指摘したように、そのような形式はたくさんあります(そして、それらのほとんどには識別ヘッダーがありません)。これが、OpenCVがそのままではそれらをサポートしていない理由です。

しかし、それらの操作はかなり簡単です。

  • 画像とその寸法から始めます(これはYUVファイルを読み取るときに必要です)
  • 輝度と彩度を3つの別々の画像に読み込む
  • クロマデシメーションを補正するために、クロマ画像を2倍にアップスケールします。 実際にはいくつかのクロマデシメーションを補正する方法があります。アップサンプリングは最も簡単です
  • YUV画像に結合します。 RGBが必要な場合は、cvCvtColorを使用できます。

最後に、コード:

IplImage * 
cvLoadImageYUV(FILE *fin, int w, int h)
{
    assert(fin);

    IplImage *py      = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *pu      = cvCreateImage(cvSize(w/2,h/2), IPL_DEPTH_8U, 1);
    IplImage *pv      = cvCreateImage(cvSize(w/2,h/2), IPL_DEPTH_8U, 1);
    IplImage *pu_big  = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *pv_big  = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 1);
    IplImage *image   = cvCreateImage(cvSize(w,    h), IPL_DEPTH_8U, 3);
    IplImage *result  = NULL;

    assert(py);
    assert(pu);
    assert(pv);
    assert(pu_big);
    assert(pv_big);
    assert(image);

    for (int i = 0; i < w*h; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        py->imageData[i] = (unsigned char) j;
    }

    for (int i = 0; i < w*h/4; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        pu->imageData[i] = (unsigned char) j;
    }

    for (int i = 0; i < w*h/4; ++i)
    {
        int j = fgetc(fin);
        if (j < 0)
            goto cleanup;
        pv->imageData[i] = (unsigned char) j;
    }

    cvResize(pu, pu_big, CV_INTER_NN);
    cvResize(pv, pv_big, CV_INTER_NN);
    cvMerge(py, pu_big, pv_big, NULL, image);

    result = image;

cleanup:
    cvReleaseImage(&pu);
    cvReleaseImage(&pv);

    cvReleaseImage(&py);
    cvReleaseImage(&pu_big);
    cvReleaseImage(&pv_big);

    if (result == NULL)
        cvReleaseImage(&image);

    return result;
}
3
mpenkov

少なくとも現在のバージョンでは、それは不可能だと思います。もちろん、それはそれほど難しいことではありませんが、次のような興味深い機能ではありません。

  • OpenCVは通常、RGB形式のWebカメラストリーム、または表示目的でRGBに直接デコードされるコード化ファイルで機能します。
  • OpenCVはコンピュータービジョン専用であり、YUVはコーディングコミュニティよりも一般的ではありません。
  • さまざまなYUV形式があり、それらを実装するには多くの作業が必要になります。

ただし、cvCvtColor()を使用して変換することは可能です。これは、とにかく興味深いことを意味します。

1

将来の参考のために:私は @ xianyanlinの素晴らしい答え をPython 3.に変換しました。以下のコードは、Raspberry Piカメラから取得したビデオで機能し、正しい色を出力しているようです。アスペクト比。

警告:高さ*幅の解像度を指定するためにnumpy形式を使用します。 1080 * 1920、480 * 640。

class VideoCaptureYUV:
    def __init__(self, filename, size):
        self.height, self.width = size
        self.frame_len = self.width * self.height * 3 // 2
        self.f = open(filename, 'rb')
        self.shape = (int(self.height*1.5), self.width)

    def read_raw(self):
        try:
            raw = self.f.read(self.frame_len)
            yuv = np.frombuffer(raw, dtype=np.uint8)
            yuv = yuv.reshape(self.shape)
        except Exception as e:
            print(str(e))
            return False, None
        return True, yuv

    def read(self):
        ret, yuv = self.read_raw()
        if not ret:
            return ret, yuv
        bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_I420, 3)
        return ret, bgr
0
Kaloyan Pashov

同じ問題が発生しました。私の解決策は1です。1つのyuvフレーム(I420など)を文字列オブジェクト「yuv」に読み取ります。 2.yuvフレームをBGR24形式に変換します。私はそれをするためにlibyuvを使います。 libyuv関数のpythonラッパーを作成するのは簡単です。これで、BGR24形式の別の文字列オブジェクト「bgr」を取得できます。3。numpy.fromstringを使用して、「bgr」文字列から画像オブジェクトを取得します。オブジェクト。画像オブジェクトの形状を変更する必要があります。

以下は参考のための簡単なyuvビューアです。

import cv2
# below is the extension wrapper for libyuv
import yuvtorgb
import numpy as np

f = open('i420_cif.yuv', 'rb')

w = 352
h = 288
size = 352*288*3/2

while True:
    try:
        yuv = f.read(size)
    except:
        break
    if len(yuv) != size:
        f.seek(0, 0)
        continue

    bgr = yuvtorgb.i420_to_bgr24(yuv, w, h)

    img = np.fromstring(bgr, dtype=np.uint8)
    img.shape = h,w,3

    cv2.imshow('img', img)

    if cv2.waitKey(50) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()
0
Yi Wang