C++でアルファチャンネル付きAVIを書き出す方法 サイコン情報書き込みに役立ちそうです


サイコン(サイクルコンピュータ)から走行情報を一秒単位のCSVファイルに変換して、それをAVIに書き込めば高価なソフト不要で走行動画に斜度や心拍情報などを表示できます。とりあえずは、文字の部分は透けずに、背景の緑の部分だけをある程度透けるようなAVIファイルを作成しています。

とりあえずはアルファチャンネル付きのAVIが出力されていますので、ある程度のプログラミングができる人であれば動画に重ねる情報を作るソフトなどを作成できそうです。

手持ちのテキスト情報を時系列に変換したり、気温データなどを動画に重ねて表示するなどの応用例が考えられます。とりあえずは単に数字をカウントアップするだけのサンプルファイルです。

  1. #include <windows.h>
  2. #include <vfw.h>
  3. #pragma comment (lib, "vfw32.lib")
  4. const int cWidth = 300;
  5. const int cHeight = 200;
  6. //実行すると100までカウントアップするアルファチャンネル付きAVI動画を作成します。複数レイヤで正確なマスクを作成します。
  7. //g++ .\outCntAviA2.cpp -mwindows -lvfw32
  8. //Bチャンネルの情報をアルファチャンネルとして独自バッファへ保存
  9. void makeAlpha(LPDWORD lpPixel,int width,int height,unsigned char *buf){
  10.     
  11.         unsigned char alphaValue;
  12.         
  13.         for(int y=0;y<height;y++){
  14.     for(int x=0;x<width;x++){
  15.     int memadr = x+y*width;
  16.                 unsigned char *data = (unsigned char *)&lpPixel[memadr];
  17.     buf[memadr] = *data;
  18.             }
  19.         }
  20. }
  21. //独自バッファへ保存されたアルファチャンネルを実際のアルファチャンネルへコピー
  22. void loadAlpha(LPDWORD lpPixel,int width,int height,unsigned char *buf){
  23.     
  24.         
  25.         unsigned char alphaValue;
  26.         
  27.         for(int y=0;y<height;y++){
  28.     for(int x=0;x<width;x++){
  29.     int memadr = x+y*width;
  30.                 unsigned char *pTest=(unsigned char *)&lpPixel[memadr];
  31.    pTest +=3; //アドレスを3つ増分(アルファチャンネルにアクセス)
  32.                 *pTest = buf[memadr]; //本物のアルファチャンネルに独自アルファチャンネルをコピー
  33.                 }
  34.         }
  35. }
  36. int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
  37. {
  38.     PAVIFILE pfile;
  39.     PAVISTREAM pavi;
  40.     AVISTREAMINFO si;
  41.     LPAVISTREAMINFO lpsi = &si;
  42.     HDC hdcMem;
  43.     HBITMAP hbmpMem, hbmpMemPrev;
  44.     DWORD i;
  45.     TCHAR szBuf[32];
  46.     LPVOID lpBits;
  47.     BITMAPV5HEADER bmiHeader;    
  48.     
  49.     
  50.     unsigned char * savealpha; //アルファチャンネル保存用
  51.     
  52.     //作業用描画領域
  53.     HDC hdcWork,hdcMask;
  54.     HBITMAP hdcWorkBitmap,hdcMaskBitmap,hdcWorkBitmapPrev,hdcMaskBitmapPrev;
  55.     
  56.     AVIFileInit();
  57.     if (AVIFileOpen(&pfile, TEXT("video.avi"), OF_CREATE | OF_WRITE, NULL) != 0) {
  58.         MessageBox(NULL, TEXT("Fail file open."), TEXT("OK"), MB_OK);
  59.         AVIFileExit();
  60.         return 0;
  61.     }
  62.     ZeroMemory(&si, sizeof(AVISTREAMINFO));
  63.     si.fccType = streamtypeVIDEO;
  64.     si.fccHandler = comptypeDIB;
  65.     si.dwScale = 1;
  66.     si.dwRate = 2;
  67.     si.dwLength = 0;
  68.     si.dwQuality = (DWORD)-1;
  69.     SetRect(&si.rcFrame, 0, 0, cWidth,cHeight);
  70.     if (AVIFileCreateStream(pfile, &pavi, &si) != 0) {
  71.         MessageBox(NULL, TEXT("Fail open stream."), TEXT("OK"), MB_OK);
  72.         AVIFileRelease(pfile);
  73.         AVIFileExit();
  74.         return 0;
  75.     }
  76.     ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
  77.     bmiHeader.bV5Size = sizeof(BITMAPINFOHEADER);
  78.     bmiHeader.bV5Width = lpsi->rcFrame.right;
  79.     bmiHeader.bV5Height = lpsi->rcFrame.bottom;
  80.     bmiHeader.bV5Planes = 1;
  81.     bmiHeader.bV5BitCount = 32;
  82.     bmiHeader.bV5Compression = BI_RGB;
  83.     bmiHeader.bV5SizeImage = bmiHeader.bV5Height * bmiHeader.bV5Width * 4;
  84.     if (AVIStreamSetFormat(pavi, 0, &bmiHeader, sizeof(BITMAPINFOHEADER)) != 0){
  85.         MessageBox(NULL, TEXT("Fail Init BMI Header."), TEXT("OK"), MB_OK);
  86.     }
  87.     savealpha = new unsigned char[cWidth*cHeight];
  88.     
  89.     hdcMem = CreateCompatibleDC(NULL);
  90.     hbmpMem = CreateDIBSection(NULL, (LPBITMAPINFO)&bmiHeader, DIB_RGB_COLORS, &lpBits, NULL, 0);
  91.     hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);
  92.     
  93.     //作業用領域確保
  94.     hdcWork = CreateCompatibleDC(hdcMem);
  95.     hdcWorkBitmap = CreateCompatibleBitmap(hdcMem,cWidth,cHeight);
  96.     hdcWorkBitmapPrev = (HBITMAP)SelectObject(hdcWork,hdcWorkBitmap);
  97.     
  98.     hdcMask =CreateCompatibleDC(NULL);
  99.     hdcMaskBitmap = CreateCompatibleBitmap(hdcMem,cWidth,cHeight);
  100.     hdcMaskBitmapPrev = (HBITMAP)SelectObject(hdcMask,hdcMaskBitmap);
  101.     
  102.     //バックグラウンドモードを透過に設定
  103.     SetBkMode(hdcWork, TRANSPARENT);
  104.     SetBkMode(hdcMask, TRANSPARENT);
  105.     //テキスト描画カラーを設定
  106.     SetTextColor(hdcWork, RGB(255, 0, 0));    //純粋にこの色が描画される
  107.     SetTextColor(hdcMask, RGB(0, 0, 255));    //アルファチャンネル用として描画される(青チャンネルをアルファとして利用)
  108.     
  109.     HGDIOBJ hFont = CreateFontW(100, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
  110.         CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"UI Gothic");
  111.     //仮領域とマスク領域にフォントを設定+初期設定されたフォントを保管
  112.     HGDIOBJ hWorkFontOld = SelectObject(hdcWork, hFont);
  113.     HGDIOBJ hMaskFontOld = SelectObject(hdcMask, hFont);
  114.     
  115.     
  116.     HBRUSH hBrush = CreateSolidBrush(RGB(30, 255, 30));    //背景色描画用
  117.     HBRUSH hBrushMask = CreateSolidBrush(RGB(0, 0, 150));//アルファチャネル用背景
  118.     for (int fCnt = 0; fCnt < 1000; fCnt++) {
  119.         wsprintf(szBuf, TEXT("%d"), fCnt);
  120.         FillRect(hdcWork, &lpsi->rcFrame, hBrush); //仮領域へ描画
  121.         FillRect(hdcMask, &lpsi->rcFrame, hBrushMask); //マスク領域へ描画
  122.         DrawText(hdcWork, szBuf, -1, &lpsi->rcFrame, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //仮領域へ文字出力
  123.         DrawText(hdcMask, szBuf, -1, &lpsi->rcFrame, DT_CENTER | DT_VCENTER | DT_SINGLELINE); //マスク領域へ文字出力
  124.         
  125.         BitBlt(hdcMem,0,0,cWidth,cHeight,hdcMask,0,0,SRCCOPY); //とりあえずマスク領域をメイン領域へコピー
  126.         makeAlpha((LPDWORD)lpBits,cWidth,cHeight,savealpha); //メイン領域の描画情報を独自のアルファチャンネルへコピー
  127.         
  128.         BitBlt(hdcMem,0,0,cWidth,cHeight,hdcWork,0,0,SRCCOPY); //裏描画領域をメイン領域へコピー
  129.         loadAlpha((LPDWORD)lpBits,cWidth,cHeight,savealpha);    //独自のアルファチャンネルからメイン領域のアルファチャンネルへコピー
  130.         AVIStreamWrite(pavi, fCnt, 1, lpBits, bmiHeader.bV5SizeImage, AVIIF_KEYFRAME, NULL, NULL);    //AVIファイルに一コマ書き込み
  131.     }
  132.     
  133.     delete[] savealpha;    //アルファチャンネル保存領域を開放
  134.     
  135.     //hdcで掴んでいるフォント手放して、それぞれ元のフォントを掴む
  136.     SelectObject(hdcWork, hWorkFontOld);
  137.     SelectObject(hdcMask, hMaskFontOld);
  138.     
  139.     //フォントを削除
  140.     DeleteObject(hFont);
  141.     //ブラシの削除    
  142.     DeleteObject(hBrushMask);
  143.     DeleteObject(hBrush);
  144.     
  145.     SelectObject(hdcWork, hdcWorkBitmapPrev);
  146.     DeleteObject(hdcWorkBitmap);
  147.     DeleteDC(hdcWork);
  148.     
  149.     SelectObject(hdcMask, hdcWorkBitmapPrev);
  150.     DeleteObject(hdcMaskBitmap);
  151.     DeleteDC(hdcMask);
  152.     
  153.     SelectObject(hdcMem, hbmpMemPrev);
  154.     DeleteObject(hbmpMem);
  155.     DeleteDC(hdcMem);
  156.     AVIStreamRelease(pavi);
  157.     AVIFileRelease(pfile);
  158.     AVIFileExit();
  159.     MessageBox(NULL, TEXT("Done."), TEXT("OK"), MB_OK);
  160.     
  161.     return 0;
  162. }

簡単に手持ちのC,C++の処理に進捗バーを実装する方法

 C言語やC++で何か処理を書ける人は多いですが、Windowプログラムとなると実装方法が難しいです。VisualStudioのプロジェクトの雛形にボタンを配置し、そこから手持ちのコードを起動すれば動きそうです。 しかし、その方法では「未応答」状態になったり、ウィンドウの操作すら...