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を使わない限り、微分程度の前処理位かなと思う。