iPhoneでリアルタイムにOpenCVで動画像解析-フレーム読み出し速度を見てみる-
OpenCVでリアルタイムに動画像処理をする目的で、ローカルにあるH.264な動画からのフレーム読み出しがどの程度の負荷かを調べてみた。
動画撮影ができないiPhone 3Gは対象外として、iPhone4と3GS、それぞれについて調べた。
読み込む動画は、iPodライブラリにある標準カメラアプリで撮影した横向きホームボタン右側の長さ20秒の動画。これをサイズ変更等一切せずに、単にフレームを読み込んでいく。設定できるパラメータは、フレームのピクセルフォーマットだけ。ドキュメントには、 kCVPixelFormatType_32BGRA か kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange を使えとある。
まず、AVURLAssetから生バッファ読み出しをするための処理コードはこんな感じ。サンプルコードそのまま。outputSettingsを設定するだけ:
switch (pixelFormat_) { case 0: pf = kCVPixelFormatType_32BGRA; break; case 1: pf = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; break; case 2: pf = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; break; } // get track reader output // see CVPixelBuffer.h for available options // setting AVVideoSettings.h or <CoreVideo/CVPixelBuffer.h> NSDictionary* outputSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt:pf], (id)kCVPixelBufferPixelFormatTypeKey, nil]; [videoOutput_ release]; videoOutput_ = [[AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:outputSettings] retain]; if(videoOutput_ == nil) { NSLog(@"%s AVAssetReaderTrackOutput initWithTrack failed for the video track.", __func__); return; } if(![reader_ canAddOutput:videoOutput_]) { NSLog(@"%s [reader canAddOutput:moviewOutput] returns false. AVAssetReaderTrackOutput can not be add to the AVAssetReader.", __func__); return; } [reader_ addOutput:videoOutput_]; // start reading if(![reader_ startReading]) { NSLog(@"%s AVAssetReader can not start reading.", __func__); return; }
読みだしたフレームデータをOpenCVに渡すところがポイント。このサンプルはOpenCVをリンクしていないので、該当部分をコメントアウトしているが、まずIplImageの構造体だけを確保して、取得したフレームデータのポインタを設定して、利用している。バッファ割り当てもメモリコピーもしないから、おそらくメモリ最小かつ最速の方法だと思う。IplImage構造体の確保もサイズ設定も1度だけでいいけど、コメントアウト箇所を1つにまとめるために、こんな書き方になっている。
このサンプルコードは、動画像処理で色情報は使わない、輝度だけ欲しいので、ピクセルフォーマットに420YpCbCr8BiPlanarVideoRangeを指定して、輝度部分だけを渡している。
-(void)process { //IplImage *iplimage; CMSampleBufferRef buf; while(1){ // get buffer pointer buf = [videoOutput_ copyNextSampleBuffer]; if(buf) { // lock the pointer CVImageBufferRef framebuf = CMSampleBufferGetImageBuffer(buf); CVPixelBufferLockBaseAddress(framebuf, 0); //create IplImage and process it //uint8_t *bufferBaseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(framebuf, 0); //uint8_t *bufferBaseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(framebuf); /* if (bufferBaseAddress) { //size_t width = CVPixelBufferGetWidth(framebuf); //size_t height = CVPixelBufferGetHeight(framebuf); iplimage = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, 1); iplimage->imageData = (char*)bufferBaseAddress; // image processing cvReleaseImageHeader(&iplimage); */ // unlock framebuffer, release buffer CVPixelBufferUnlockBaseAddress(framebuf, 0); CFRelease(buf); } else { break; // buf == NULL } } }
使ったサンプル動画は、iPhone4は、カメラアプリで横向きホームボタン右方向で撮影した、1280x720の30fpsな動画。iPhone3GSは、640x480の30fpsな動画で、いづれも長さは20秒。
結果は:
- | 32BGRA | Full Range | Video Range |
iPhone 4 | 9.9秒 | 130秒 | 7.1秒 |
iPhone 3GS | 7.2秒 | 42.9秒 | 5.4秒 |
32BGRAがけっこう速い。輝度だけならVideo Rangeを使えばOK。Full Rangeは絶望的に遅い。多分CPUで再計算しているからだろう。
リアルタイムでできるのは、GPUを使わない限り、微分程度の前処理位かなと思う。