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とサンプルファイル” をダウンロード
コメント