OpenCVでYUVファイルからフレームを読み取る方法は?
バイナリファイルから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
前述のように、YUVフォーマットには多くの種類があります。
OpenCVでYUV形式からRGBに変換するのは非常に簡単です。
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マットの寸法を定義することです前変換します。
[〜#〜] update [〜#〜]ここに新しいバージョンのコードがあります: https://github.com/ chelyaev/opencv-yuv
singleYUV 4:2:0平面画像ファイルを読み取るコードを投稿しています。これはほとんどのYUVファイルに直接適用できます(同じFILE
オブジェクトから読み取りを続けます)。これに対する例外は、 ヘッダーを持つYUVファイル (通常、*.y4m
拡張子)。このようなファイルを処理する場合は、次の2つのオプションがあります。
FILE
オブジェクトのヘッダーデータを使用する独自の関数を記述してくださいffmpeg
または同様のツールを使用)。これは最も単純なので、私が好むオプションです。また、他の形式のYUVフォーマット(非平面、異なるクロマデシメーション)では機能しません。 @Stephaneが指摘したように、そのような形式はたくさんあります(そして、それらのほとんどには識別ヘッダーがありません)。これが、OpenCVがそのままではそれらをサポートしていない理由です。
しかし、それらの操作はかなり簡単です。
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;
}
少なくとも現在のバージョンでは、それは不可能だと思います。もちろん、それはそれほど難しいことではありませんが、次のような興味深い機能ではありません。
ただし、cvCvtColor()
を使用して変換することは可能です。これは、とにかく興味深いことを意味します。
将来の参考のために:私は @ 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
同じ問題が発生しました。私の解決策は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()