MPOファイルの表示プログラム

Cudavmapからの流れで、MFCなWindowsプログラムを紹介します。

拡張子が.mpoなファイルはその昔FujiFilmから発売されていた、3Dカメラによって記録されたものです。カメラは、型番がFinePix REAL 3D W1で、

このような外観をしており。二つのレンズで同時に左右それぞれの画像を記録し、後で立体視できるような画像ファイルを記録するものでした。現物もどこかにあるはずですが、発掘できていません。これが出力するファイルを見るためのプログラムはいくつかありますが、Visual C++でMFCを使って書いた習作プログラムが発掘されたので、それについて書きます。

プログラム名を頼りに、NASから”発掘”しました。タイムスタンプからは、2014年くらいの製品ですから、すでに10年以上前のものですね。アイコンからMFCベースで作ったものと分かりますね。幸いにしてソースが残っていましたので、ソリューションを開いてみます。当然ながら、

ということになりますね。無事Buildできました。ということは改修可能ということですね。動かして見ましょうかね。

ここで左上のMPOを開くをクリックすると、ファイル選択ダイアログになります。

ファイルを選択すると、プレビュー画像が表示されるところがミソです。(ま必須の機能ですが…..)

さらに”開く”で。

デフォルトでは”交差法”で眺めるレイアウトになりますね。平行法に切り替えることもできます。画面のサイズが小さい範囲であれば、平行法でも観ることができますが、左右の画像の間隔が左右の眼球の間隔より大きくなると、交差法でしか立体視できません。

最初に紹介したように10年以上前でもすでにMFCを使うWindows上のGUIプログラム開発はいささか古いものでしたが、筆者はMFCの習作のつもりで作りました。別記事でも書きましたが、ダイアログベースのプログラムならば、それほど開発に苦労はしないと思いました。で、ソースの肝心な部分だけ開示します。まず”*.mpo”なファイルを二つのjpegに分離する部分。

void CmposhowDlg::OnBnClickedButton1()
{
	// TODO: Add your control notification handler code here
	CString filename,basename;
	CString r = L"_R.jpg";
	CString l = L"_L.jpg";
	CFile	file;
	const int	M= 1024*1024;
	char* internal_buf = new char[M];


	UpdateData();	// for m_xvRadio
	sm_xvRadio = m_xvRadio;

	CCustomFileDialog myDLG(TRUE,NULL,NULL,
		OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
		_T("ステレオ画像ファイル(*.mpo)|*.mpo|ステレオ画像ファイル(*.jps)|*.jps||"));
	if(myDLG.DoModal()!=IDOK)
		return;


	filename = myDLG.GetPathName();

	//processFile(filename,m_img1,m_img2);
		IStream	*buf1,*buf2;
	IPicture *lpiPicture1,*lpiPicture2;


		ifstream mpoFile (filename, ios::in|ios::binary|ios::ate);
		// make sure we can open file
		if (mpoFile.is_open()) {
			// read the image data into a buffer
			mpoFile.rdbuf()->pubsetbuf(internal_buf, M);
			fileSize = (int)mpoFile.tellg();
			stereoData = new char[fileSize];
			mpoFile.seekg(0, ios::beg);
			mpoFile.read(stereoData, fileSize);
			mpoFile.close();

			// start of the next image
			char startOfImage[8] = { 'F', 'F', 'D', '8', 'F', 'F', 'E', '1' };
			int imageBreak;

			// find the break point between the image pairs
			for (int i = 0; i < fileSize; i += 4) {
				// get block into a c string
				char block[255];
				// extract the characters
				sprintf(block, "%.2X%.2X%.2X%.2X", (unsigned int)(unsigned char)stereoData[i], (unsigned int)(unsigned char)stereoData[i + 1], (unsigned int)(unsigned char)stereoData[i + 2], (unsigned int)(unsigned char)stereoData[i + 3]);
				// check for start of image
				if (strncmp(block, startOfImage, 8) == 0) {
					// remember where the image starts
					imageBreak = i;
				}
				fileloaded = TRUE;
			}
			// filename to basename

			int	x = filename.Find(L".");
			basename = filename.Left(x);

			BYTE*	lpfData1 = (BYTE *)GlobalAlloc(GPTR, imageBreak);
			BYTE*	lpfData2 = (BYTE *)GlobalAlloc(GPTR, fileSize - imageBreak);

			CreateStreamOnHGlobal((HGLOBAL)lpfData1, FALSE, &buf1);
			CreateStreamOnHGlobal((HGLOBAL)lpfData2, FALSE, &buf2);

			if (m_xvRadio) {	// 交差法
				buf1->Write(stereoData, imageBreak, NULL);
				buf2->Write(stereoData + imageBreak, fileSize - imageBreak, NULL);
			}
			else {	// 平行法
				buf2->Write(stereoData, imageBreak, NULL);
				buf1->Write(stereoData + imageBreak, fileSize - imageBreak, NULL);
			}

			//OleLoadPicture(buf1,imageBreak,TRUE,IID_IPicture,(LPVOID*)&lpiPicture1);
			m_img1.Destroy();
			//m_img1.Load(L"C:\\test1.jpg");
			m_img1.Load(buf1);

			//img1.Save(L"test1.jpg");


			//buf2->Write(stereoData + imageBreak, fileSize-imageBreak,NULL);
			//OleLoadPicture(buf2,fileSize-imageBreak,TRUE,IID_IPicture,(LPVOID*)&lpiPicture2);
			m_img2.Destroy();
			m_img2.Load(buf2);
			//img2.Save(L"test2.jpg");

			if (m_jpegsave) {
				CString leftname = basename + l;
				CString rightname = basename + r;
				if (m_xvRadio) {// 交差法
					m_img1.Save(rightname);
					m_img2.Save(leftname);
				}
				else {
					m_img2.Save(rightname);
					m_img1.Save(leftname);
				}
			}
			fileloaded = TRUE;

			oldmethod = m_xvRadio;

		}

//	m_img1.Destroy();
//	m_img2.Destroy();
	Invalidate();
}

上記でカスタムダイアログで片方の画像をプレビューで表示する部分。

void CCustomFileDialog::OnFileNameChange()
{
	//CEdit* pEdit = (CEdit*)GetDlgItem(IDC_STATIC);
	//pEdit->SetWindowText(GetPathName());
	HDC	hDC;
	CRect	WindowRect,SrcRect;
//	CImage	m_img1;
	CWnd*	m_pictt;
	CString filename,basename;
	int	fileSize;
	//CString ftype = ".mpo";
	char	*stereoData;
	//CString r = L"_R.jpg";
	//CString l = L"_L.jpg";


	CFile	file;
	const int	M= 1024*1024;
	char* internal_buf = new char[M];
	int	imageBreak;

	m_pictt = GetDlgItem(IDC_STATIC);
		filename = GetPathName();

	if( filename.Find(_T(".mpo")) >= 0 ) {	// mpo file?
	//processFile(filename,m_img1,m_img2);
	//IPicture *lpiPicture1,*lpiPicture2;
		mpotype = 1;

		ifstream mpoFile (filename, ios::in|ios::binary|ios::ate);
		// make sure we can open file
		if (mpoFile.is_open()) {
			// read the image data into a buffer
			mpoFile.rdbuf()->pubsetbuf(internal_buf,M);
			fileSize = (int)mpoFile.tellg();
			stereoData = new char [fileSize];
			mpoFile.seekg (0, ios::beg);
			mpoFile.read (stereoData, fileSize);
			mpoFile.close();

			// start of the next image
			char startOfImage [8]= { 'F', 'F', 'D', '8', 'F', 'F', 'E', '1' };
			// find the break point between the image pairs
			for(int i=0; i < fileSize; i+=4){
				// get block into a c string
				char block[255];
				// extract the characters
				sprintf(block, "%.2X%.2X%.2X%.2X", (unsigned int)(unsigned char)stereoData[i], (unsigned int)(unsigned char)stereoData[i+1], (unsigned int)(unsigned char)stereoData[i+2], (unsigned int)(unsigned char)stereoData[i+3]);
				// check for start of image
				if(strncmp(block, startOfImage, 8) == 0){
					// remember where the image starts
					imageBreak = i;
					//break;	// no need further investigation
				}
			}
		}
		else
			return;

			BYTE*	lpfData1 = (BYTE *)GlobalAlloc(GPTR,imageBreak);
			BYTE*	lpfData2 = (BYTE *)GlobalAlloc(GPTR,fileSize-imageBreak);

			CreateStreamOnHGlobal((HGLOBAL)lpfData1,FALSE,&buf1);
			CreateStreamOnHGlobal((HGLOBAL)lpfData2,FALSE,&buf2);

//	0 -> imageBreak ....Left image
//  imageBreak -> End ..Right Image

			if( !sm_xvRadio ){	// 交差法
				buf1->Write(stereoData, imageBreak,NULL);
				buf2->Write(stereoData + imageBreak, fileSize-imageBreak,NULL);
			}
			else {	// 平行法
				buf2->Write(stereoData, imageBreak,NULL);
				buf1->Write(stereoData + imageBreak, fileSize-imageBreak,NULL);
			}			
			//OleLoadPicture(buf1,imageBreak,TRUE,IID_IPicture,(LPVOID*)&lpiPicture1);
			m_img1.Destroy();
			//m_img1.Load(L"C:\\test1.jpg");
			m_img1.Load(buf1);

			//img1.Save(L"test1.jpg");
			

			//buf2->Write(stereoData + imageBreak, fileSize-imageBreak,NULL);
			//OleLoadPicture(buf2,fileSize-imageBreak,TRUE,IID_IPicture,(LPVOID*)&lpiPicture2);
			m_img2.Destroy();
			m_img2.Load(buf2);
			
			
		SrcRect.SetRect(0,0,m_img1.GetWidth(),m_img1.GetHeight());

		hDC = m_pictt->GetWindowDC()->GetSafeHdc();
		m_pictt->GetClientRect(WindowRect);
		SetStretchBltMode(hDC,HALFTONE);
		m_img1.StretchBlt(hDC,WindowRect,SrcRect,SRCCOPY);
		delete stereoData;
		delete internal_buf;
	}
	else {	// jps
		mpotype = 0;	// not mpo

		ifstream mpoFile (filename, ios::in|ios::binary|ios::ate);
		// make sure we can open file
		if (mpoFile.is_open()) {
			// read the image data into a buffer
			mpoFile.rdbuf()->pubsetbuf(internal_buf,M);
			fileSize = (int)mpoFile.tellg();
			stereoData = new char [fileSize];
			mpoFile.seekg (0, ios::beg);
			mpoFile.read (stereoData, fileSize);
			mpoFile.close();



		}
		else
			return;

		// read into one large buffer

			BYTE*	lpfData1 = (BYTE *)GlobalAlloc(GPTR,fileSize);

			CreateStreamOnHGlobal((HGLOBAL)lpfData1,FALSE,&buf1);

//	0 -> imageBreak ....Left image
//  imageBreak -> End ..Right Image
			buf1->Write(stereoData,fileSize,NULL);
			img0.Destroy();
			img0.Load(buf1);

			
	// 左画像のみ見せる			
		//SrcRect.SetRect(0,0,img0.GetWidth()/2,img0.GetHeight());
		SrcRect.SetRect(0, 0, img0.GetWidth(), img0.GetHeight());
		hDC = m_pictt->GetWindowDC()->GetSafeHdc();
		m_pictt->GetClientRect(WindowRect);
		SetStretchBltMode(hDC,HALFTONE);
		img0.StretchBlt(hDC,WindowRect,SrcRect,SRCCOPY);
		delete stereoData;
		delete internal_buf;


	}

}

C++ Builder CEに移植したら、ソースコードを公開します。今時MFCでもないでしょうか?でも昔のプログラムをメンテする必要はありますから、Visual Studioで扱うことはできるようですね。ちなみに、ファイル選択ダイアログで拡張子がjpsなファイルもプレビューまではできますが、画像表示はできないようです。一応その状態のバイナリとサンプルのmpoファイルをダウンロードできるようにしておきます。

“mposhow.exeとサンプルファイル” をダウンロード

コメント