2013年9月21日土曜日

256*256pixlのアイコンを取得する方法

環境:windows7 x64 Visual C++ 2012

サンプル載せておきます。例によってhttp://www.wgag.net/winapi/のスケルトンプログラムをベースにします。

実行風景
Cドライブのアイコンなのですが、人のアイコンも一緒に表示されるのは、なんとかできたような気もするのですが、もう覚えていないのでそのままにして載せました。

では、サンプルをどうぞ。

#include 

#define _AFXDLL
#include //AfxGetInstanceHandle

#include 

#include 
#include //ExtractIconに必要//「#include 」より下に書かないとエラー……。
#include 





const TCHAR szWndClass[] = _T("TestProgram");

LRESULT CALLBACK WndProc(
    HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// The entry point for the application
int WINAPI WinMain(
    HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = szWndClass;

    if (!RegisterClass(&wc)) return FALSE;

    HWND hWnd = CreateWindow(
        szWndClass, _T("Title"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL,
        hInstance, NULL);

    if (!hWnd) return FALSE;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
 {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

// Processes messages for the main window
LRESULT CALLBACK WndProc(
    HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 HDC hdc;
    PAINTSTRUCT ps;
    switch (msg) {
 case WM_PAINT:

  SHFILEINFO sfi;
  if (!SHGetFileInfo(_T("C:\\"), 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX))//「SHGFI_SYSICONINDEX」iIconの取得に必要
   return NULL;
  

  // 画像リストを取得する
  IImageList *pImageList;
  if (SHGetImageList(SHIL_LAST , IID_IImageList, (void**)&pImageList) != S_OK)//下位互換性を考えるなら「SHIL_JUMBO」と直接指定するよりも「SHIL_LAST」なのかな?(まあもうXPはサポートが切れるが……)
   return NULL;

  // アイコンを取得する
  if (pImageList->GetIcon(sfi.iIcon, SHGFI_ICON|SHGFI_SELECTED, &sfi.hIcon) != S_OK)//ここのフラグをきちんと設定しないと「SHIL_JUMBO」の時に正しい?マスクを取得できない?ためにバグる。
   sfi.hIcon = NULL;

  // 画像リストを解放する
  pImageList->Release();

  hdc = BeginPaint(hWnd, &ps);
  
  DrawIconEx(hdc,0,0,sfi.hIcon,256,256,0,NULL, DI_MASK);
  DrawIconEx(hdc,256,0,sfi.hIcon,256,256,0,NULL, DI_IMAGE);
  DrawIconEx(hdc,0,256,sfi.hIcon,256,256,0,NULL, DI_NORMAL);
  DrawIconEx(hdc,256,256,sfi.hIcon,256,256,0,NULL,DI_IMAGE | DI_MASK);
  
  EndPaint(hWnd, &ps);

  DestroyIcon(sfi.hIcon);
  
  break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}
 なんか微妙に表示がバグっているのでファイルでも置いておきます。(Web系は分からないのです。)
http://ux.getuploader.com/ADMIS/download/2/File_Manager_ICON_Ver0.4.zip

2013年9月20日金曜日

Bloggerにソースコードを乗せる方法

------------------------------------------------------------------------------------------------------------------------
「Syntax Highlighter Scripts Generator」を使う方法……だったのですが、
バグる事があるのでお勧めしません。
2013_11_18_月:追記からのHTMLタグを使う方法をお勧めします。
------------------------------------------------------------------------------------------------------------------------

今まで適当に澄ましていたけど、余りにも見難いのでなんとかしてみた。
取りあえず、ググってhttp://k-1-ne-jp.blogspot.jp/2012/10/syntaxhihglighter.htmlを参考にする。

えーっとまずはhttp://www.way2blogging.org/widget-generators/syntax-highlighter-scripts-generator
にアクセスして……
必要な言語を選択。

クリップボードにスクリプトをコピー。

Bloggerに戻って、


テンプレート⇒HTMLの編集。

「12行目ぐらいにある<title>タグの後に挿入する」との事だから後ろに挿入したのだけど、これは<title></title>の中に入れる必要があるのか?(取りあえず外で動いたのでそのまま放置) 
  ⇒やってみたらバグったのでこれで正解かと思われます。

それから記事の新しい記事の作成で

<pre class="brush: c++;">
//ここにソースコードを挿入する。
int main(void){
 return 0;
}
</pre>

を挿入(c++の所の言語名を変えれば対応できるはず……)
つまり、作成からHTMLへ移動して……

挿入すると
//ここにソースコードを挿入する。

int main(void){

 return 0;

}


のように表示される……ハズです。

------------------------------------------------------------------------------------------------------------------------
2013_11_18_月:追記
っという方法でいいかと思っていたのですが、この方法だと、
例えば
#include <stdio.h>
void main(void){
        //コード
}
を表示させようとすると

<pre class="brush: c++;">
#include <stdio.h>
void main(void){
//コード
}
</pre>
#include 
void main(void){
 //コード
}
のようにバグります
ついでに言えば、インターネットの接続が上手く行かない場合、
普通のテキストとして表示される事も多いです。

また、正直に言って設定するのも面倒だし、行番号は表示されてもtabは半角スペース一個扱いになって見難いです。

しかし、バグっている以上は仕方がないのでこれを使うのは諦めます。

解決策:
1.取りあえず行数の表示は諦める。
2.「Syntax Highlighter Scripts Generator」の代わりに簡単なHTMLのタグを使う。

使用するタグ

<textarea cols="72" rows="90" style="height: 2690px; width: 800px;" wrap="off">
//ここにコードを挿入。
</textarea>

height⇒縦の長さ
width⇒横の長さ
の二つだけ設定すれば大丈夫です。

新しい記事の作成で、作成からHTMLへ移動してしたところに挿入すればOKです。
単なるタグなので、「Syntax Highlighter Scripts Generator」のように面倒な設定は不要です。

ただし、「Syntax Highlighter Scripts Generator」のようにウィンドウのサイズを自動で決めてはくれないので、手動で「height」と「width」を決定する必要があります。ただし、「width」については一度決めてしまえば、その値をメモしておいて次の時に見返せば済みます。

3.tabは半角スペース一個扱いになるので、ブログに書き込む前にtabを半角スペース8個に変換しておく。
標準でtabは半角スペース8個分として表示されるので変換しておきます。
この時、まさか手動では追いつかないので、エディタの置換機能を使用します。
私はterapadで置換しています。

------------------------------------------------------------------------------------------------------------------------

実例:
#include <stdio.h>
void main(void){
//コード
}
のままだとtabがスペース一個のままなので各々エディタで置換して、
#include <stdio.h>
void main(void){
  //コード
}
Bloggerの編集モードをHTMLに変えて下記のコードを挿入
<textarea cols="72" rows="90" style="height: 56px; width: 800px;" wrap="off">
#include <stdio.h>
void main(void){
  //コード
}</textarea>
すると、

のように表示できます。
今回の場合は一文字が14pixなのでheight=14*(4行)=56pxになります。

文字のピクセル数は調べれば出てくるのでしょうが、私はPrintScreenからペイントにコピーして
ペイントでピクセル数を数えました。

ちなみに
<textarea cols="72" rows="90" style="height: 56px; width: 800px;" wrap="off">
#include <stdio.h>
void main(void){
  //コード
}
</textarea>
とすると、最後に空白が一行追加されるので注意してください。

<textarea></textarea>について分からないこ事があれば取りあえずGoogleで検索してください。
Googleで検索で検索する!
  Top  Direct Show 関連 > Direct Show で取得する解像度を変更する方法

追記2013.10.06.日
このページのコード、具体的には処理した画像をディスプレイに表示する部分にバグを発見しました。

修正するには

SetDIBitsToDevice(
 hdc , 650 , 0 ,
 pVideoInfoHeader->bmiHeader.biWidth , pVideoInfoHeader->bmiHeader.biHeight ,
 0 , 0 , 0 , pVideoInfoHeader->bmiHeader.biHeight ,
 rgbValues , &bmpInfo , DIB_RGB_COLORS
);




SetStretchBltMode(hdc , COLORONCOLOR);
//これを挿入しないと「StretchDIBits」による縮小時に発生するノイズが酷い。
StretchDIBits(
 hdc ,
 650 , 0 ,
 640 , 360 ,
 0 , 0 ,
 pVideoInfoHeader->bmiHeader.biWidth , pVideoInfoHeader->bmiHeader.biHeight ,
 rgbValues , &bmpInfo , DIB_RGB_COLORS,SRCCOPY
);


とする事で修正されます。
修正済みのコードは後程……。


まずは前回コードからの変更点
 //解像度を指定する。// http://www.mvision.co.jp/WebHelpIM/_RESOURCE/MvCap_03.html // http://hisoap.jugem.jp/?eid=140 // http://d.hatena.ne.jp/shibusawa_1/20080909/1220963798 //
 IAMStreamConfig *pConfig = NULL;
 pCapture->FindInterface(&PIN_CATEGORY_CAPTURE ,0,pbf,IID_IAMStreamConfig,(void**)&pConfig);
 //pCapture->FindInterface(&PIN_CATEGORY_PREVIEW ,0,pbf,IID_IAMStreamConfig,(void**)&pConfig);

 int iCount=0, iSize=0;
 hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);//ピンがサポートするフォーマット機能の数を取得
 if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))// VIDEO_STREAM_CONFIG_CAPS構造体かサイズを確認
 {
  for (int iFormat = 0; iFormat < iCount; iFormat++)
  {
   VIDEO_STREAM_CONFIG_CAPS scc;    // ビデオ フォーマット構造体AM_MEDIA_TYPE 
   AM_MEDIA_TYPE *p_am_media_type;//am_media_typeの実体は既に宣言しているのだが、ポインタで欲しいと言うので仕方がないのでポインタを宣言したものの、何とかならないかな……。
   p_am_media_type = &am_media_type;
   hr = pConfig->GetStreamCaps(iFormat, &p_am_media_type,  reinterpret_cast(&scc));//フォーマット機能セットを取得

   //VIDEO_STREAM_CONFIG_CAPS構造体で、動画対応で最大の大きさである1280*720となる場合を選択する。
   if(SUCCEEDED(hr) && (int)scc.InputSize.cx==1920 && (int)scc.InputSize.cy==1080){//「if(p_am_media_type->lSampleSize==2764800){」としてもOK //1280*720*3==2764800より。*3しているのはRGBで三色あるから。(MEDIASUBTYPE_RGB24の場合、MEDIASUBTYPE_RGB32の場合は1280*720*4==3686400)
   //「LifeCam Studio for Business Win USB Port 50/60 Hz 5WH-00003」を使った場合
   //960:544だと31fpsくらい出る。
   //1280:720だと9fpsくらい出る。←なんでだ?
   //1920:1080だと16.1fpsくらい出る。
   //部屋が暗かったりすると値が出ない気がするし、なんかバラバラ……。
    //選択された中で条件に合う物を「SetFormat」する

    if((p_am_media_type->majortype == MEDIATYPE_Video)   //たぶん取得したフォーマット機能セットが期待した物である事を確認している。
    && (p_am_media_type->subtype == MEDIASUBTYPE_RGB24)   //期待したフォーマット機能セットでなかった場合はfor文でそのうち引っかかる
    && (p_am_media_type->formattype == FORMAT_VideoInfo)  //このif文は無くても動く。単に皆やってるから入れてみただけ。(必要の無い所にまでフレームレートを設定する必要もないので……)
    && (p_am_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
    && (p_am_media_type->pbFormat != NULL))
    {
     // VIDEOINFOHEADERの取得
    // hr=pConfig->GetFormat(&p_am_media_type);//これをやるとバグる。何故かってp_am_media_typeはGetStreamCapsで既に取得済み。
     VIDEOINFOHEADER *pVih;
     pVih = (VIDEOINFOHEADER *)p_am_media_type->pbFormat;

     // 解像度の変更
     //pVih->bmiHeader.biWidth=1280;//最初から1280*720の場合を選択しているので不要(ブレークポイントで止めるのに役立つくらい……)
     //pVih->bmiHeader.biHeight=720;//最初から1280*720の場合を選択しているので不要//これを実行してるサイトがあった気がするけど上手く行かない(たぶんやり方が間違っている)。実際にはAM_MEDIA_TYPEが期待する値の時SetFormatする事で解像度を変更できる。// http://www.freeml.com/directshow/2478/latest //を参照

     //フレームレートの設定
     //pVih->AvgTimePerFrame = 30;//カメラの最高値に合せておく。
            //性能を越えて100とかにしても初期設定と思われる16fpsくらいしか出ない。//ここの値に関わらず気まぐれなので放置
            //レートを下げる事くらいはできるかも……。(レートはCPU依存との話もあるが……これ如何に)

     // キャストして代入
     p_am_media_type->pbFormat=(BYTE*)pVih;

     //SetFormatでpbFormatの変更を適用
     pConfig->SetFormat(p_am_media_type);
    }
   }
  }
 }
}
とまあ、こんな感じになる。
一応全体のコードも貼っておく。取得する解像度をFullHDに設定してあるが、そのまま表示されても大きすぎるので、内部での画像処理がFullHDに対応しただけで、実際のディスプレイへの表示は3分の1に縮小表示している。
//MessageBox(0, _T("I'm here!"), _T("Message"), MB_OK);
/*
バグ報告、
最大化した時、Directshowで描写している方の画面(と言うかもう一つのウィンドウ)の位置が追随しないためズレる
⇒解決方法、「case WM_PAINT:」の時、に「pVWindow->SetWindowPosition(0, 0, 640, 480);」を実行すればいいのだが、
  このまま実行すると、再描写が重なるので、今している右側に表示する処理をウィンドウを新しく作ってそちらで処理させる
  必要があるが、面倒なので後回し。

終了時にMessageでOKをクリックして終了しようとするとCPU使用率が上昇する。
⇒Ver1.2からの症状。どうやら解放した後の「case WM_PAINT:」の時に暴走している。Messageで「OK」を押しても処理してもプログラムが終了せずに、解放したリソースにアクセス
  しようとするのが原因だが、最終的な実装で解決するはずなので放置

重心の描写時に、ターゲットが存在しないとCPU使用率が上がる
⇒SoTPが0の時0で割り算しているため
⇒解決
*/


/*
http://hisoap.jugem.jp/?eid=140
のサンプルほぼそのまま

花形として
http://www.geekpage.jp/programming/directshow/renderfile.php
http://marupeke296.com/DSW_No5_FullScreenProgram.html
とかが参考になる。(結構似たような事をしてる)

http://wisdom.sakura.ne.jp/system/winapi/win32/win118.html
を参考にしてビットマップを右側に描写している。

http://tmp.junkbox.info/index.php?e=24
を参考にしてカラーフィルタを実装した。(ただしC#(涙))

http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93
を参考に、RGBからHSVへ変換し色判定を行う。
*/

#include 
#include 

#include 
#include 
//#include //mallocに必要?
//#include //memcpy関数に必要?
#include //sin,cos,tanの三角関数などの計算に必要


#define WM_CAPTURE WM_APP+1

#pragma comment(lib,"strmiids.lib")

//===============================================
#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__
#include  // SampleGrabber用
/*
#include におけるエラー
「fatal error C1083: include ファイルを開けません。'qedit.h': No such file or directory」
の解決方法

#include の直前に
「
#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__
」
を追加する。すなわち、
「
#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__
#include 
」
とする。

また、
VisualStudio2012のメニューバーから
デバッグ(D)→***のプロパティ...
→構成プロパティ→全般→全般→プラットフォームツールセット
に置いて、
「Visual Studio 2012 (v110)」から「Visual Studio 2012 - Windows XP (v110_xp)」に変更する。

さらに、
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include
に「qedit.h」を(古いSDKもしくはインターネットから拾ってきて)追加する。
注意:この場合の「qedit.h」は無編集状態のもの。(エラー回避のためのコメントアウトをしていないもの)


追記
「C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include」
に「qedit.h」を(古いSDKもしくはインターネットから拾ってきて)追加すれば、「Visual Studio 2012 (v110)」のままでOK
*/
//===============================================

//グローバル変数
const TCHAR szWndClass[] = _T("TestProgram");
static BITMAPINFO bmpInfo;

#define M_PI 3.14159265358979 /* 円周率 */

VIDEOINFOHEADER *pVideoInfoHeader;
BYTE *grabBuffer; //void *grabBuffer = NULL;でも良かったみたい。
     //&grabBuffer//grabBufferのアドレス
     // grabBuffer//画像の先頭アドレス
     //*grabBuffer//画像
unsigned char * rgbValues; //画像処理用の動的バッファのポインタ//unsignedにしないとバグる。具体的には「水色っぽい部分を見つけたら」「if ( rgbValues[p] > rgbValues[p + 1] && rgbValues[p] > rgbValues[p + 2]*2 )」などと判定する時にバグる。

//プロトタイプ宣言               // http://www.geocities.jp/ky_webid/cpp/language/038.html によれば、c++では必ずプロトタイプ宣言をしなくてはいけないらしい。(しないとリンクエラー「error LNK2001」になる)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
int DirectShow_for_USB_Camera(HWND hWnd);         
class cameraCB;                //これは無くてもエラー吐いてない……良く分からん……。
void ConvertRGBtoHSV(unsigned char * rgbValues,unsigned int *i,double *H,double *S,double *V);

// http://miraiware.net/memo/dshow-capture.html //
class cameraCB : public ISampleGrabberCB//ISampleGrabberCBクラスを継承
 {
 public:
 cameraCB(){};
 ~cameraCB(){};
 HWND hWnd;
 
 STDMETHODIMP_(ULONG) AddRef() { return 2; }//インターフェイスの参照カウントを 1 ずつ増やす。
 STDMETHODIMP_(ULONG) Release() { return 1; }//インターフェイスの参照カウントを 1 ずつ減少させる。
 
 /**
 * インターフェースの生成
 */
 STDMETHODIMP QueryInterface(REFIID riid,void **ppv)
 {
  return E_NOINTERFACE;
 }
 
 /**
 * バッファサンプリング コールバック関数
 * @param dblSampleTime サンプル時間
 * @param pSample IMediaSample
 */
 STDMETHODIMP SampleCB( double dblSampleTime, IMediaSample* pSample ) {
  return S_OK;
 }

 /**
 * バッファサンプリング コールバック関数
 * @param dblSampleTime サンプル時間
 * @param pBuff バッファ
 * @param lBufSize バッファサイズ
 */
 STDMETHODIMP BufferCB( double dblSampleTime, BYTE* pBuff, long lBufSize ){
  grabBuffer = pBuff;

  static int ii=0;
  ii++;
  //SendMessage( hWnd, WM_CAPTURE, (WPARAM)pBuff, (LPARAM)lBufSize );

  InvalidateRect(hWnd, NULL, FALSE);// 再描画を促す
  return S_OK;
 }
};

// The entry point for the application
int WINAPI WinMain(
 HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPSTR lpCmdLine, int nCmdShow)
{
 WNDCLASS wc;
 wc.style         = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc   = WndProc;
 wc.cbClsExtra    = 0;
 wc.cbWndExtra    = 0;
 wc.hInstance     = hInstance;
 wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
 wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
 wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
 wc.lpszMenuName  = NULL;
 wc.lpszClassName = szWndClass;

 if (!RegisterClass(&wc)) return FALSE;
 
 HWND hWnd = CreateWindow(
  szWndClass, _T("Title"),
  WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT, CW_USEDEFAULT,
  CW_USEDEFAULT, CW_USEDEFAULT,
  NULL, NULL,
  hInstance, NULL);
 
 if (!hWnd) return FALSE;

 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);

 DirectShow_for_USB_Camera(hWnd);

 MSG msg;
 while (GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 free(rgbValues);
 return (int) msg.wParam;
}

// Processes messages for the main window
LRESULT CALLBACK WndProc(
 HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 switch (msg) {
 case WM_PAINT:{

  // Bitmapをbyte[]へコピー
  memcpy(rgbValues, grabBuffer, (pVideoInfoHeader->bmiHeader.biSizeImage));
  
  //1pixel辺りの力(質量×重力加速度?)を1として計算する。
  //ターゲットとするピクセルの合計「Sum of target pixels」//物理で言う質量の合計
  unsigned int SoTP=0;
  //原点(画面の左上)(0,0)、を中心として時計回りに回転しようとする力(モーメント)の合計「Sum of Power」
  unsigned int xSoP=0;
  unsigned int ySoP=0;
  //重心「Center of gravity」
  unsigned int xCoG=0;//重心のx座標
  unsigned int yCoG=0;//重心のy座標

  unsigned int i=0;
  for (unsigned int y = 0; y < (unsigned int)(pVideoInfoHeader->bmiHeader.biHeight); y++)
  {
  for (unsigned int x = 0; x < (unsigned int)(pVideoInfoHeader->bmiHeader.biWidth ); x++)
  {
   double H=0;//0~360°
   double S=0;//0.0~1.0
   double V=0;//0.0~1.0//にするのが当然だが、計算コストの問題で0~255

   //RGBからHSVへ変換し色判定を行う。// http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93 //
   ConvertRGBtoHSV(&rgbValues[0], &i, &H, &S, &V);
   

   /*
   次の値に大体合わせてみた。
   R G B
   81 83 123
   82 84 141
   112 152 255
   116 158 255

   H=60*2/42+240=2.857+240=242.857
   S=42/123=0.3414

   H=60*40/143+240=256
   S=143/255=0.56
   */

   // 水色っぽい部分を見つけたら
   //if (rgbValues[i] > rgbValues[i + 1] && rgbValues[i] > rgbValues[i + 2]*2)
   if( 200<=H && H<=260 && 0.2bmiHeader.biHeight)-(ySoP/SoTP);//なんか良く分からんけどこのままだと画面の左下が原点(0,0)となってしまうので、次の表示のためにも原点(0,0)を画面の左上に変換しておく。

   flag=TRUE;

   // http://ja.wikipedia.org/wiki/%E4%B8%89%E8%A7%92%E6%B8%AC%E9%87%8F //
   //角度の導出   //カメラ同士の距離を18.8cmとして計算していく。
   //y:角度、x:座標
   //  y=((90-60)/320)*x+60
   //⇔y=(3/32)*x+60
   double theta1;//θ1[°]//右カメラ
   double theta2;//θ2[°]//左カメラ
   theta1 = ((0.09375)*xCoG)+60;//(3/32)だと何故か上手く計算してくれないので(0.09375)に書き直している。
   theta2 = (0.09375)*320+60;//取りあえずカメラが一台しか無いので固定値(つまり、ターゲットが、もう一台のカメラ(右カメラ)があるべき位置の真正面上を移動すると仮定して距離を計算していく)

   //ここで注意したいのはsin,cos,tanは度数法(度(°))では無く弧度法(ラジアン(rad))である事。
   //そのため上記の度数法で表している角度を弧度法に変換する。(一気に弧度法で計算していないのはデバックでブレークポイントを設置した時に弧度法で言われても分度器で角度を測れないため。必要が無い場合は計算をまとめて良い)
   //M_PIは自分で定義する必要がある。
   double rTheta1;//θ1[rad]//右カメラ
   double rTheta2;//θ2[rad]//左カメラ
   rTheta1 = M_PI*theta1/180;//[rad]=2π*[°]/360⇔[rad]=π*[°]/180
   rTheta2 = M_PI*theta2/180;

   //ターゲットとの距離を導出
   double dVertical; //垂直距離「     Vertical distance」//単位は[cm]//カメラとカメラを結ぶ直線にターゲットから垂線を下ろした時、その降ろした垂線の距離
   double dHorizontal; //水平距離「   Horizontal distance」//単位は[cm]//カメラとカメラを結ぶ直線にターゲットから垂線を下ろした時、に直線の交点とカメラとカメラの間の中点との距離
   double dStraight; //直線距離「Straight-line distance」//単位は[cm]//カメラとカメラの中点とターゲットとの間の直線距離
   dVertical = (double)(18.8)*(double)sin(rTheta1)*(double)sin(rTheta2)/(double)sin(rTheta1+rTheta2);//三角測量と同様の方法で導出
   dHorizontal = (dVertical/tan(rTheta1))-((18.8)/2);//三角関数より導出
   dStraight = sqrt(   (dVertical*dVertical)+(dHorizontal*dHorizontal)   );//三平方より導出

   //int xxx;//ブレークポイントを設定でき無かったので意味は無いけど追加
   //xxx=0;
  }
  else
  {
   flag=FALSE;
  }



  //memcpy(grabBuffer, rgbValues, (pVideoInfoHeader->bmiHeader.biSizeImage));   //これをやると画面が両方真っ赤になる。のでダメ

  //背景の画像(右側の赤と黒の画像)を描写する
  HDC hdc;
  PAINTSTRUCT ps;
  hdc = BeginPaint (hWnd, &ps);

  //そのまま表示すると大き過ぎるので3分の1に縮小して表示する//640:480だと1920:1080に対してアスペクト比が変わるので640:360とする。
  SetDIBitsToDevice(
   hdc , 650 , 0 ,
   640 , 360 ,
   0 , 0 , 0 , 360 ,
   rgbValues , &bmpInfo , DIB_RGB_COLORS
  );

  if(flag){
   //重心の位置を描写する
   HPEN hPen;  // 論理ペンへのハンドル
   hPen = CreatePen(PS_SOLID, 1, RGB(0, 0xFF, 0));
   SelectObject(hdc, hPen);      // 論理ペンを登録
   
   MoveToEx(hdc, 650+xCoG/3, 0, NULL);  // (20, 20) に移動
   LineTo(hdc, 650+xCoG/3, (pVideoInfoHeader->bmiHeader.biHeight )/3);         // (200, 20) まで直線を描画
   MoveToEx(hdc, 650, yCoG/3, NULL);  // (20, 20) に移動
   LineTo(hdc, 650+(pVideoInfoHeader->bmiHeader.biWidth )/3, yCoG/3);         // (20, 200) まで直線を描画

   DeleteObject(hPen);           // 論理ペンを破棄
  }

  EndPaint( hWnd, &ps );
  return 0;
  }

 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 default:
  return DefWindowProc(hWnd, msg, wParam, lParam);
 }
}



int DirectShow_for_USB_Camera(HWND hWnd){
 HRESULT hr;
 
 // --- COMの初期化
 hr = CoInitialize(NULL);
 if(FAILED(hr)){
  printf("CoInitialize Failed!\n");
  exit(1);//return 1;と同義
 }

 // --- 1.フィルタグラフマネージャ作成(DirectShow の中心的なオブジェクト)
 IGraphBuilder *pGraph = NULL;
 CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraph);
        // http://marupeke296.com/DSW_No5_FullScreenProgram.html //② フィルタグラフマネージャの取得
        // フィルタグラフマネージャの取得
        //
        // DirectShowのプログラムは実質ここから始まります。まずはフィルタグラフを管理するフィルタグラフマネージャを作成します。取得するインターフェイスはIGraphBuilderです。このインターフェイスはWindows自体が提供するためCoCreateInstance関数というグローバルな関数を用いて生成します。
        //
        //IGraphBuilder *pGB = NULL;
        //HRESULT hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGB);
        // CoCreateInstance関数はCOMとして登録されているインターフェイスを生成して返してくれます。
        //第1引数にインターフェイスを含むクラスIDを指定します。IGraphBuilderを取得するときに指定するIDはCLSID_FilterGraphと決まっているので、無条件で上のように与えます。
        //第2引数はCOMオブジェクトの内包状態を指定するフラグなのですが、殆どのCOMはNULLでOK。
        //第3引数は生成するCOMの実装ソースがあるDLLの所在を表します。IGraphBuilderについてはDLLがローカルにありますのでインプロセスであるCLSCTX_INPROC_SERVERを指定します。
        //第4引数に欲しいインターフェイスの固有IDを指定します。IGraphBuilderインターフェイスを表すIDはIID_IGraphBuilderです。
        //関数が成功すると第5引数にIGraphBuilderインターフェイスへのポインタが返ります。
        //
        //この処理はもう決まりきっていますので、変更のしようがありません。「IGraphBuilderの取得の仕方はこうだ!」と決めてしまってよいでしょう。
        //
        // IGraphBuilderの役目はフィルタグラフにかかわるインターフェイスの提供、およびフィルタグラフの管理です。


 // --- 2.システムデバイス列挙子の作成(接続されているデバイスを調べるためのオブジェクト) // ⑥ キャプチャデバイス(USBカメラ等)を選択するため,システムデバイス列挙子を作成する.つまり,PCに接続されているデバイスを列挙するってこと.
 ICreateDevEnum *pDevEnum = NULL;
 CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void **)&pDevEnum);
 
 // 列挙子の取得 // ⑦ 列挙したキャプチャデバイスのカテゴリ列挙子を取得する.音声なのか,映像なのか,とかそういうこと.ビデオキャプチャの場合は,CLSID_VideoInputDeviceCategoryを指定すればよい.
 IEnumMoniker *pClassEnum = NULL;
 pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
 if(pClassEnum == NULL){
  printf("Device does not exist!\n");
  pDevEnum->Release();
  CoUninitialize();
  return 1;
 }
 pDevEnum->Release(); // 以後に不要なメモリーをリリース

 IBaseFilter *pbf = NULL;
 IMoniker *pMoniker = NULL;
 ULONG cFetched;    //ULONGはCOMの型……らしいのだが……???
 if(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK){    // IEnumMoniker.Next メソッド(http://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.comtypes.ienummoniker.next(v=vs.85).aspx)←ハイパーリンクがバグってる……。
                  // Next(1, &pMoniker, &cFetched)
                  //返されるモニカの数,列挙されたモニカへの参照を格納,列挙されたモニカの実数への参照を格納

  pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pbf); // 最初のモニカをフィルタオブジェクトにバインド
  pMoniker->Release();
 }
 pClassEnum->Release(); // 以後に不要なメモリーをリリース
 
 pGraph->AddFilter(pbf, L"USB Camera Capture"); // グラフにフィルタを追加
        // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑥ ソースフィルタの作成と登録
        // IGraphBuilder::AddSourceFilter関数を使用した場合は自動的にフィルタグラフへの登録が行われるので、改めてIGraphBuilder::AddFilter関数を呼ぶ必要はない。
        // ファイル名に関して重要な注意があります。第2引数の型は「LPCWSTR」になっています。これはワイド文字(例えばunicode)というやつです。私たちが以前まで普通に使っていたのはマルチバイト文字でして、これは「LPCSTR」です。マルチバイト文字とワイド文字は互換性がありません。ですから、ワイド文字がコンパイラオプションに設定されていない環境では、文字をプログラマが明示的にワイド文字に変換する必要があります。ただリテラル(ダブルクォーテーションで囲まれた文字)で定義する場合は「L接頭子」を付けると常にワイド文字として扱われます。
 
 //============================================================================================
 // http://www.geekpage.jp/programming/directshow/samplegrabber.php //BitMapキャプチャ用フィルタの用意

 IBaseFilter *pSampleGrabberFilter;
 // SampleGrabber(Filter)を生成
 CoCreateInstance(CLSID_SampleGrabber,
      NULL,
      CLSCTX_INPROC,
      IID_IBaseFilter,
      (LPVOID *)&pSampleGrabberFilter);

 ISampleGrabber *pSampleGrabber;
 // FilterからISampleGrabberインターフェースを取得します
 pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (LPVOID *)&pSampleGrabber);

 AM_MEDIA_TYPE am_media_type;
 // SampleGrabberを接続するフォーマットを指定。
 // ここがポイントです。
 // ここの指定の仕方によりSampleGrabberの挿入箇所を
 // 決定できます。このサンプルのような指定をすると
 // 画面出力の寸前でサンプルを取得できます。
 ZeroMemory(&am_media_type, sizeof(am_media_type));
 am_media_type.majortype = MEDIATYPE_Video;
 am_media_type.subtype = MEDIASUBTYPE_RGB24;
 am_media_type.formattype = FORMAT_VideoInfo;
 pSampleGrabber->SetMediaType(&am_media_type);

 // GraphにSampleGrabber Filterを追加
 pGraph->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
 //============================================================================================

 // --- 3.キャプチャグラフ作成
 ICaptureGraphBuilder2 *pCapture = NULL;
 CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **) &pCapture);
 pCapture->SetFiltergraph(pGraph); // フィルタグラフをキャプチャグラフに組み込む//ICaptureGraphBuilder2インターフェイスの初期化
             //
             //http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑦ ICaptureGraphBuilder2インターフェイスの取得
             // フィルタの接続は意外と面倒なものなのですが、これに関してヘルパーインターフェイスが用意されています。「ICaptureGraphBuilder2」というインターフェイスは、フィルタの接続をある程度自動化してくれまして、使い勝手がとても良いものです。ここではこれを使うことにしましょう。このインターフェイスの取得にもCoCreateInstance関数を用います。
             // 取得したICaptureGraphBuilder2インターフェイスは初期化が必要なのですが、これはとても簡単でして先に取得したIGraphBuilderインターフェイスをICaptureGraphBuilder2::SetFiltergraph関数に渡すだけです。
// pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pbf, NULL, NULL); // キャプチャグラフの設定、グラバをレンダリング出力に設定//キャプチャ画像を表示するためのビデオプレビューグラフを作成する
 pCapture->RenderStream(NULL, NULL, pbf, pSampleGrabberFilter, NULL);//上記に変わってこのように第三引数以外全てNULLに指定してもなんとかなったりする。(キャプチャするには第4引数が必要)
             // 
 //注意:ここの第4引数に「pSampleGrabberFilter」 // http://www.crystal-creation.com/software/technical-information/library/directshownet/filter-graph/capture-graph-builder2.htm#contents-3
 //     を指定して、経由するフィルタを明示する。 // int RenderStream(
 //     これをしないとキャプチャを取れない。  //    DsGuid PinCategory,       // ピン カテゴリ      //接続元と先が明らかである場合はNULL指定によりIntelligent Connectに接続を任せることも出来ます。
             //    DsGuid MediaType,         // 出力ピンのメディア タイプ   //NULL指定をすると、すべてIntelligent Connectに任せます。
             //    object pSource,           // 接続先のフィルタ      //ここは必ず指定する必要があります。
             //    IBaseFilter pfCompressor, // 経由するフィルタ (圧縮フィルタなど) //NULLが指定できます。
             //    IBaseFilter pfRenderer    // シンク フィルタ (レンダラなど)  //NULLを指定した場合、関数はデフォルトの入力フィルタへ接続を試みる。
             //   );
             //
             // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑧ フィルタの接続 を参照
 

 //============================================================================================


 //解像度を指定する。// http://www.mvision.co.jp/WebHelpIM/_RESOURCE/MvCap_03.html // http://hisoap.jugem.jp/?eid=140 // http://d.hatena.ne.jp/shibusawa_1/20080909/1220963798 //
 IAMStreamConfig *pConfig = NULL;
 pCapture->FindInterface(&PIN_CATEGORY_CAPTURE ,0,pbf,IID_IAMStreamConfig,(void**)&pConfig);
 //pCapture->FindInterface(&PIN_CATEGORY_PREVIEW ,0,pbf,IID_IAMStreamConfig,(void**)&pConfig);

 int iCount=0, iSize=0;
 hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);//ピンがサポートするフォーマット機能の数を取得
 if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))// VIDEO_STREAM_CONFIG_CAPS構造体かサイズを確認
 {
  for (int iFormat = 0; iFormat < iCount; iFormat++)
  {
   VIDEO_STREAM_CONFIG_CAPS scc;    // ビデオ フォーマット構造体AM_MEDIA_TYPE 
   AM_MEDIA_TYPE *p_am_media_type;//am_media_typeの実体は既に宣言しているのだが、ポインタで欲しいと言うので仕方がないのでポインタを宣言したものの、何とかならないかな……。
   p_am_media_type = &am_media_type;
   hr = pConfig->GetStreamCaps(iFormat, &p_am_media_type,  reinterpret_cast(&scc));//フォーマット機能セットを取得

   //VIDEO_STREAM_CONFIG_CAPS構造体で、動画対応で最大の大きさである1280*720となる場合を選択する。
   if(SUCCEEDED(hr) && (int)scc.InputSize.cx==1920 && (int)scc.InputSize.cy==1080){//「if(p_am_media_type->lSampleSize==2764800){」としてもOK //1280*720*3==2764800より。*3しているのはRGBで三色あるから。(MEDIASUBTYPE_RGB24の場合、MEDIASUBTYPE_RGB32の場合は1280*720*4==3686400)
   //「LifeCam Studio for Business Win USB Port 50/60 Hz 5WH-00003」を使った場合
   //960:544だと31fpsくらい出る。
   //1280:720だと9fpsくらい出る。←なんでだ?
   //1920:1080だと16.1fpsくらい出る。
   //部屋が暗かったりすると値が出ない気がするし、なんかバラバラ……。
    //選択された中で条件に合う物を「SetFormat」する

    if((p_am_media_type->majortype == MEDIATYPE_Video)   //たぶん取得したフォーマット機能セットが期待した物である事を確認している。
    && (p_am_media_type->subtype == MEDIASUBTYPE_RGB24)   //期待したフォーマット機能セットでなかった場合はfor文でそのうち引っかかる
    && (p_am_media_type->formattype == FORMAT_VideoInfo)  //このif文は無くても動く。単に皆やってるから入れてみただけ。(必要の無い所にまでフレームレートを設定する必要もないので……)
    && (p_am_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
    && (p_am_media_type->pbFormat != NULL))
    {
     // VIDEOINFOHEADERの取得
    // hr=pConfig->GetFormat(&p_am_media_type);//これをやるとバグる。何故かってp_am_media_typeはGetStreamCapsで既に取得済み。
     VIDEOINFOHEADER *pVih;
     pVih = (VIDEOINFOHEADER *)p_am_media_type->pbFormat;

     // 解像度の変更
     //pVih->bmiHeader.biWidth=1280;//最初から1280*720の場合を選択しているので不要(ブレークポイントで止めるのに役立つくらい……)
     //pVih->bmiHeader.biHeight=720;//最初から1280*720の場合を選択しているので不要//これを実行してるサイトがあった気がするけど上手く行かない(たぶんやり方が間違っている)。実際にはAM_MEDIA_TYPEが期待する値の時SetFormatする事で解像度を変更できる。// http://www.freeml.com/directshow/2478/latest //を参照

     //フレームレートの設定
     //pVih->AvgTimePerFrame = 30;//カメラの最高値に合せておく。
            //性能を越えて100とかにしても初期設定と思われる16fpsくらいしか出ない。//ここの値に関わらず気まぐれなので放置
            //レートを下げる事くらいはできるかも……。(レートはCPU依存との話もあるが……これ如何に)

     // キャストして代入
     p_am_media_type->pbFormat=(BYTE*)pVih;

     //SetFormatでpbFormatの変更を適用
     pConfig->SetFormat(p_am_media_type);
    }
   }
  }
 }


 //============================================================================================


 // http://www.geekpage.jp/programming/directshow/samplegrabber.php //画像の縦横のpixelとデータサイズの取得
 // 接続情報取得。
 // この処理はRenderFileによりGraphが構成された後に
 // 行う必要があります。
 pSampleGrabber->GetConnectedMediaType(&am_media_type);
 pVideoInfoHeader = (VIDEOINFOHEADER *)am_media_type.pbFormat;

 // ビデオ ヘッダーには、ビットマップ情報が含まれる。
 // ビットマップ情報を BITMAPINFO 構造体にコピーする。
 CopyMemory(&bmpInfo.bmiHeader, &(pVideoInfoHeader->bmiHeader), sizeof(BITMAPINFOHEADER));// http://www.greenwood.co.jp/~k-aki/article/directshow_movie/directshow_movie.html //のdirectshow2.cpp// http://www.greenwood.co.jp/~k-aki/article/directshow_movie/directshow2/directshow2.cpp //

 //// 画像(映像)の幅と高さを表示
 //// サンプルをわかりやすくするために表示しているだけなので、
 //// 必ず必要というわけではありません。
 //printf("size = %dx%d\n",
 // pVideoInfoHeader->bmiHeader.biWidth,
 // pVideoInfoHeader->bmiHeader.biHeight);

 //pVideoInfoHeader->bmiHeader.biWidth=1280;
 //pVideoInfoHeader->bmiHeader.biHeight=720;


 //// データサイズを表示
 //// これも説明のために表示しています。
 //printf("sample size = %d\n",
 // am_media_type.lSampleSize);

 // Grabを行う事を設定            //キャプチャをするかしないか選択
 // SetBufferSamplesを行わないとバッファから
 // データを取得できません。
 // 不必要に負荷をかけたくない場合にはFALSEにしておいて、
 // データを取得したくなったら、TRUEに変える
 // という方法もできます。
 hr = pSampleGrabber->SetBufferSamples(FALSE);//データを取得するか?「TRUE」or「FALSE」//コールバック関数を用いて連続キャプチャするので、FALSEで問題ない。//http://miraiware.net/memo/dshow-capture.html
 pSampleGrabber->SetOneShot(FALSE);

 // プレビュー開始時
 cameraCB *cb = new cameraCB();
 cb->hWnd = hWnd;//これが無いとウィンドウを識別できない……。
 pSampleGrabber->SetCallback(cb,1);
 //pSampleGrabber->SetCallback(this,1);
 
 // プレビュー停止時
 //pSampleGrabber->SetCallback(NULL, 1);
 //============================================================================================


 // http://ugd555.fc2web.com/frame.html //⑤
 // IVideoWindowインタフェース(ビデオウィンドウの設定)
 IVideoWindow *pVWindow=NULL;
 hr = pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVWindow);
 // http://ugd555.fc2web.com/frame.html // ⑫
 // プレビューウィンドウの設定
 hr = pVWindow->put_Owner((OAHWND)hWnd);
 //hr = pVWindow->put_Owner(NULL);とすると子ウィンドウを切り離す事ができる。
 hr = pVWindow->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);//WS_CHILD:子ウィンドウを作成します。|WS_CLIPCHILDREN:親ウィンドウの内部で描画するときに、子ウィンドウが占める領域を除外します。親ウィンドウを作成するときに使います。//WS_CLIPCHILDREN不要かと思ったけど、無いとウィンドウを消したとき?(うる覚え……)にバグる。
// RECT rc;
// GetClientRect(hWnd, &rc);//親ウィンドウのサイズを取得
// pVWindow->SetWindowPosition(0, 0, rc.right, rc.bottom);
 //pVWindow->SetWindowPosition(0, 0, pVideoInfoHeader->bmiHeader.biWidth, pVideoInfoHeader->bmiHeader.biHeight);
 pVWindow->SetWindowPosition(0, 0, 640, 360);//そのまま表示すると大き過ぎるので3分の1に縮小して表示する//640:480だと1920:1080に対してアスペクト比が変わるので640:360とする。
 hr = pVWindow->put_Visible(OATRUE);//ウィンドウを表示する。//無くても動くけど、たぶんあった方が良いって感じのものかな?//ShowWindow()関数に相当する物だと言う事にしておく。
 
 //画像処理用の動的メモリを確保
 //rgbValues = (unsigned char *)malloc(640*480*3 * sizeof(unsigned char));//unsignedにしないとバグる。具体的には「水色っぽい部分を見つけたら」「if ( rgbValues[p] > rgbValues[p + 1] && rgbValues[p] > rgbValues[p + 2]*2 )」などと判定する時にバグる。
 rgbValues = (unsigned char *)malloc((pVideoInfoHeader->bmiHeader.biWidth)*(pVideoInfoHeader->bmiHeader.biHeight)*3 * sizeof(unsigned char));

 // --- 5.キャプチャ開始
 IMediaControl *pMC = NULL;
 pGraph->QueryInterface(IID_IMediaControl, (LPVOID *)&pMC);  // MediaControlインターフェース取得(グラフの開始・停止を制御するインターフェイス)
 pMC->Run();
      // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑩ フィルタグラフの実行
      // フィルタグラフの接続が全て終了し、表示サイズを正しく指定すれば、いつでも動画を再生することができます。再生を担当するインターフェイスは、IGraphBuilderから生成できるIMediaControlインターフェイスです。
 
 // --- 6.終了待ち
 MessageBox(NULL, TEXT("Click to stop."), TEXT("Message"), MB_OK);
 // getch()で待つのはだめ、レンダリング表示のためにシステムに実行を返す必要がある

 // --- 終了処理
 pMC->Stop();
 hr = pVWindow->put_Visible(OAFALSE);

 pMC->Release();    // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑪ 終了処理
 pVWindow->Release();  // 解放順番はきっと厳密ではないのだろうと思うのですが、一応作成順と逆に解放しておきます。

 // プレビュー停止時
 pSampleGrabber->SetCallback(NULL, 1);//これをしないと正しく終了できない。「ハンドルされない例外が 0x56F067C7 (qedit.dll) で発生しました(Image_Processing_Ver0.7.exe 内): 0xC0000005: 場所 0xFEEEFEF6 の読み取り中にアクセス違反が発生しました。」と言ってエラーを吐く。
 delete cb;

 pCapture->Release();
 pSampleGrabber->Release();
 pSampleGrabberFilter->Release();
 pbf->Release();
 pGraph->Release();

 CoUninitialize(); // COMを終了

 return 0;
}


//RGBからHSVへ変換する関数// http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93 //を元に実装
void ConvertRGBtoHSV(unsigned char * rgbValues,unsigned int *i,double *H,double *S,double *V){
 if( rgbValues[*i+2] > rgbValues[*i+1] && rgbValues[*i+2] > rgbValues[*i] )//赤>緑&&赤>青
 {
  //赤>緑&&赤>青
  if( rgbValues[*i+1] > rgbValues[*i] )//緑>青
  {
   //赤>緑>青
   *H = 60*(  ((double)rgbValues[*i+1]-(double)rgbValues[*i])/((double)rgbValues[*i+2]-(double)rgbValues[*i])    ) + 0;//60*(G-B)/(MAX-MIN)+0
   *S = ((double)rgbValues[*i+2]-(double)rgbValues[*i])/(double)rgbValues[*i];//(MAX-MIN)/MAX
   *V =  (double)rgbValues[*i+2];//MAX
  }
  else
  {
   //赤>青>緑
   *H = 60*(  ((double)rgbValues[*i]-(double)rgbValues[*i+1])/((double)rgbValues[*i+2]-(double)rgbValues[*i+1])  ) + 0;//60*(B-G)/(MAX-MIN)+0
   *S = ((double)rgbValues[*i+2]-(double)rgbValues[*i+1])/(double)rgbValues[*i+1];//(MAX-MIN)/MAX
   *V =  (double)rgbValues[*i+2];//MAX
  }
 }
 else if( rgbValues[*i+1] > rgbValues[*i] )//緑>青
 {
  //緑>青
  if( rgbValues[*i] > rgbValues[*i+2] )//青>赤
  {
   //緑>青>赤
   *H = 60*(  ((double)rgbValues[*i]-(double)rgbValues[*i+2])/((double)rgbValues[*i+1]-(double)rgbValues[*i+2])  ) + 120;//60*(B-R)/(MAX-MIN)+120
   *S = ((double)rgbValues[*i+1]-(double)rgbValues[*i+2])/(double)rgbValues[*i+2];//(MAX-MIN)/MAX
   *V =  (double)rgbValues[*i+1];//MAX
  }
  else
  {
   //緑>赤>青
   *H = 60*(  ((double)rgbValues[*i+2]-(double)rgbValues[*i])/((double)rgbValues[*i+1]-(double)rgbValues[*i])    ) + 120;//60*(R-B)/(MAX-MIN)+120
   *S = ((double)rgbValues[*i+1]-(double)rgbValues[*i])/(double)rgbValues[*i];//(MAX-MIN)/MAX
   *V =  (double)rgbValues[*i+1];//MAX
  }
 }
 else//青>緑
 {
  //青>緑>赤
  if( rgbValues[*i+1] > rgbValues[*i+2] )//緑>赤
  {
   //青>緑>赤
   *H = 60*(  ((double)rgbValues[*i+2]-(double)rgbValues[*i+1])/((double)rgbValues[*i]-(double)rgbValues[*i+2])  ) + 240;//60*(R-G)/(MAX-MIN)+240
   *S = ((double)rgbValues[*i]-(double)rgbValues[*i+2])/(double)rgbValues[*i+2];//(MAX-MIN)/MAX
   *V =  (double)rgbValues[*i];//MAX
  }
  else
  {
   //青>赤>緑
   *H = 60*(  ((double)rgbValues[*i+1]-(double)rgbValues[*i+2])/((double)rgbValues[*i]-(double)rgbValues[*i+1])  ) + 240;//60*(G-R)/(MAX-MIN)+240
   *S = ((double)rgbValues[*i]-(double)rgbValues[*i+1])/(double)rgbValues[*i+1];//(MAX-MIN)/MAX
   *V =  (double)rgbValues[*i];//MAX
  }
 }
}

}
上のコードの表示が微妙にバグっているのでサンプルも置いておきます。
余談ですが、色の判定はカメラが違ったりすると上手く行かない事があります。各自で調整が必要です。それと、「LifeCam Studio for Business Win USB Port 50/60 Hz 5WH-00003」を使うために解像度の指定を実装しましたが、色の調整は行っていない状態なので環境が同じでも上手く行かなくてもそれは仕様です^^;。
http://ux.getuploader.com/ADMIS/download/11/Image_Processing_Ver1.8.zip

  Top  > Direct Show 関連 > Direct Show で取得する解像度を変更する方法 
このブログのDirect Show 関連トップ

記事1:#include <qedit.h>におけるエラー
http://the-united-front.blogspot.jp/2013/08/include-fatal-error-c1083-include-qedit.html

記事2:Direct Show によるUSBカメラのリアルタイム画像処理
http://the-united-front.blogspot.jp/2013/09/direct-show-usb-win7-x64-visual-c-2012.html

記事3:記事2の改良版
Direct Show で取得する解像度を変更する方法
http://the-united-front.blogspot.jp/2013/09/direct-show-httpwww_20.html

記事4:「艦隊これくしょん」時報アプリ:の技術的内容
Direct Show によるMP3ファイルの再生
http://the-united-front.blogspot.jp/2013/09/mp3directshow-httpblog.html

記事5:記事3の改良版(バグ修正版)
http://the-united-front.blogspot.com/2013/10/top-2-3-directshowusb-2-httpsdocs.html

記事5:記事5の改良版(バグ修正版)
http://the-united-front.blogspot.jp/2014/05/directshowusb.html

記事6:DirectShowで取得した画像をOpenCLで画像処理する。
http://the-united-front.blogspot.jp/2014/06/openclgpgpu.html

2013年9月18日水曜日

指定した座標を順番に複数個所(最大9箇所)クリックするプログラムを書いて見ました。
おそらく連打ソフトに分類されるのかな?

実行風景

ダウンロード
        実行ファイル(良く分からない人はこれをダウンロードすれば使えます)
http://ux.getuploader.com/ADMIS/download/5/Mouse_Clicker_Ver0.2_ForDistribution.zip

Vectorにも掲載してみました。→http://www.vector.co.jp/soft/winnt/util/se504307.html

追記:2013.10.14
技術的解説、及びソースコードはこちらへどうぞ。
http://the-united-front.blogspot.jp/2013/10/mous-eclickerver0.html


Readme.txt(一部改編)
======================================================================================
Mouse_Clicker_Ver0.2
      Copyright (C) 2013 ADMIS
      http://the-united-front.blogspot.jp/
======================================================================================
1.はじめに
 このソフトウェアの使用によって発生した如何なる損失も補償できません。
 自己責任かつ法律の許す範囲で使用してください。
 このプログラムは指定された(最大9つの)座標を順にクリックし、指定時間待機したのち再びクリックする動作を繰り返すプログラムです。

2.同封されているファイルの説明
 Mouse_Clicker_Ver0.2_for_x64.exe
  64bit環境のWindows7,
  64bit環境のWindows8での動作を確認。

 Mouse_Clicker_Ver0.2_for_x86_XP.exe
  32bit環境のWindowsXP,
  32bit環境のWindows7,
  64bit環境のWindows7,
  64bit環境のWindows8での動作を確認
 良く分からない人は取りあえず「Mouse_Clicker_Ver0.2_for_x86_XP.exe」
 を使えばOKです。

3.再配布について
 禁止します。

4.使用方法
 1.クリック間隔を指定します。(半角で指定してください。また、0~9999と表示されていますが、正しくは1~9999です。0を選択すると自動で10[ms]にセットされます)
 2.クリックさせる座標を指定します。
  (1).キーボードの入力を半角の状態にします。(全角では認識しません)
  (2).マウスカーソルをクリックさせたい座標に移動させます。
  (3).キーボードの1~9を押すと押した瞬間のカーソルの位置が記憶されます。
      (この時このアプリケーションがアクティブ(選択されている状態)である必要があります)
  (4).実際にクリックさせる座標を選択します。青色の文字が表示されている部分
    をクリックして黒色にするとクリックF10を押した時にクリックする座標と
    して認識されます
 3.F10を押すと指定された座標の1~9を順にクリックし、指定されたクリック間隔だけタイマーで待機したのち再びクリックする動作を繰り返します。
 4.終了するにはESCキーを押してください。

5.バグの報告
 気持ちに余裕があれば直します。(まだクリック間隔の表示も直していないのに……)
 このページに(http://the-united-front.blogspot.jp/2013/09/9-readme.html
 コメントをお願いします。
//========================================================================


しかし……
おーとくりっか~http://www.vector.co.jp/soft/dl/winnt/util/se417864.html
の存在を知っていればこんな無駄なソフトは作らなかったな。

WindowsUpdateしたら起動できなくなった場合の対処法

PC起動直後にF8キーを連打して(別にタイミングさえ分かれば一回押しでちゃんと反応します。)詳細ブートオプションを表示する。

こんなの

そしたらセーフモードにカーソルを合わせてEnterキー



おそらくこんな感じで構成を元に戻してくれます。
以上ですが、今回問題となった疑いのある更新プログラムは


Windows 7 for x64-based Systems 用 Internet Explorer 9 の累積的なセキュリティ更新プログラム (KB2870699)
Windows 7 for x64-Based Systems 用セキュリティ更新プログラム (KB2872339)
Windows 7 for x64-Based Systems 用セキュリティ更新プログラム (KB2876315)
Windows 7 for x64-Based Systems 用更新プログラム (KB2853952)
Windows 7 for x64-Based Systems 用更新プログラム (KB2868116)

のいずれかです。(設定をいろいろ弄っているので私のPCだけかもしれませんが)



追記2013.09.20
http://jutememo.blogspot.jp/2013/09/windows-update-mactype.html
で、Updateが失敗するのは MacType が原因らしいと書いてあったが、私の場合はインストールしていない。それよりも、RAMDISKの環境変数を戻さないままwindowsUpdateして失敗した物をそのままにしているのが今になって悪さをしているのではないかと思っている。そろそろクリーンインストールが必要かもしれない。


追記2013.09.25
最近この記事へのアクセスが多いみたい。
どうやら私だけの問題では無さそうです。


半角数字を全角数字に変換するもっとも簡単な方法


char zen[][3] = { "0","1","2","3","4","5","6","7","8","9"};


として&zen[0][0]、&zen[1][0]……、&zen[9][0]と言った感じで指定していく。

char zen[][3]と「3」になっているのは、全角が2byteで、終了コード'\0'が1byte消費して合計3byteとなるためである。
複数の文字列を繋ぎあわせる方法。

char Buf[100]="";
char Buf0[100]="TEST";
char Buf1[100]="DATA";
char Buf2[100]="txt";
sprintf_s(Buf, 100,"%s%s.%s", Buf0, Buf1, Buf2);//文字列をbufにまとめる

この時bufは「"TESTDATA.txt"」となっているはずです。

 またの第二引数はBufのサイズを指定します100ではなくsizeof(Buf)でもいいかもしれませんね。
sprintfでも可能ですが、
「warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.」
みたいな感じのエラーを吐くので、この警告文に従ってsprintf_sを使いましょう。
(sprintfは危ないのでsprintf_sを代わりに使ってください、みたいな事を言っています)

sprintf_sについてMSDN(http://msdn.microsoft.com/ja-jp/library/ce3zzk1k(v=vs.90).aspx)

2013年9月17日火曜日

  Top  > Direct Show 関連 > Direct Show によるUSBカメラのリアルタイム画像処理


環境:
Win7 x64 Visual C++ 2012 、カメラはVPCSE2AJに付属のWebカメラを使用。


処理内容:
 画像を取得し、指定した色を赤で表示し、それ以外の色を黒で表示する。
 また、取得した色の重心を求め、緑の線として表示する。さらに取得した重心を元に物体までの距離を計算する(単純な三角測量。ただし、カメラが一台しか無いので、もう一つのカメラが存在すると仮定し、そのカメラから垂直な位置に物体が存在する場合のみ正しい数値を測定可能。また、計算した距離を表示する機能を実装していないのでブレークポイントを掛けて一時停止しないと分からない……。)。
 フレームレートは16fps程度ではあったが、たぶんカメラの性能の限界の問題で、PCの性能やソフトの問題では無い。(全てのフレームに処理を通すようにプログラムを組んでいるため。)これに関しては新しいカメラを入手次第試してみたい。
 
ちゃんと設定しないとダメみたいです。
追記2013.10.01
カメラの性能次第で30fpsとか出ました。
//「LifeCam Studio for Business Win USB Port 50/60 Hz 5WH-00003」を使った場合


 実装が中途半端なので、終了時の処理など、書いてはあるものの、参考にする場合は良く見た方が良いと思われます。(終了処理は書いてあるけど、実行されずに終わっていそう……)気が向いたらきちんと実装します。



余談:
 
 
最近では画像処理はOpenCVとか言う良く分からない物を使う事によって随分と簡単にできるようになったらしい。しかし、フレームレートに制限がー、とか、画素数に制限がー、とか聞こえてきた気がする(実はあまり調べていない……)。それに、良く分からないものは使いたくない。そんなこんなでDirect Showを使う事にしましたとさ……。



実行風景

暗い青色に反応するようにセットしてあります。背景を白にしていますが、もっとごちゃごちゃしていてもそこそこ上手く行きます。(単に部屋を隠したかっただけです。)



さすがに見難いのでプロジェクトを置いておきます。
http://ux.getuploader.com/ADMIS/download/10/Image_Processing_Ver1.6.zip

以下ソースコード
//=======================================================================
//MessageBox(0, _T("I'm here!"), _T("Message"), MB_OK);
/*
バグ報告、
最大化した時、Directshowで描写している方の画面(と言うかもう一つのウィンドウ)の位置が追随しないためズレる
⇒解決方法、「case WM_PAINT:」の時、に「pVWindow->SetWindowPosition(0, 0, 640, 480);」を実行すればいいのだが、
  このまま実行すると、再描写が重なるので、今している右側に表示する処理をウィンドウを新しく作ってそちらで処理させる
  必要があるが、面倒なので後回し。
終了時にMessageでOKをクリックして終了しようとするとCPU使用率が上昇する。
⇒Ver1.2からの症状。どうやら解放した後の「case WM_PAINT:」の時に暴走している。Messageで「OK」を押しても処理してもプログラムが終了せずに、解放したリソースにアクセス
  しようとするのが原因だが、最終的な実装で解決するはずなので放置
重心の描写時に、ターゲットが存在しないとCPU使用率が上がる
⇒SoTPが0の時0で割り算しているため
⇒解決
*/

/*
http://hisoap.jugem.jp/?eid=140
のサンプルほぼそのまま
花形として
http://www.geekpage.jp/programming/directshow/renderfile.php
http://marupeke296.com/DSW_No5_FullScreenProgram.html
とかが参考になる。(結構似たような事をしてる)
http://wisdom.sakura.ne.jp/system/winapi/win32/win118.html
を参考にしてビットマップを右側に描写している。
http://tmp.junkbox.info/index.php?e=24
を参考にしてカラーフィルタを実装した。(ただしC#(涙))
http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93
を参考に、RGBからHSVへ変換し色判定を行う。
*/
#include <tchar.h>
#include <windows.h>
#include <dshow.h>
#include <stdio.h>
//#include <stdlib.h>//mallocに必要?
//#include <memory.h>//memcpy関数に必要?
#include <math.h>//sin,cos,tanの三角関数などの計算に必要

#define WM_CAPTURE WM_APP+1
#pragma comment(lib,"strmiids.lib")
//===============================================
#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__
#include <qedit.h> // SampleGrabber用
/*
#include <qedit.h>におけるエラー
「fatal error C1083: include ファイルを開けません。'qedit.h': No such file or directory」
の解決方法
#include <qedit.h>の直前に

#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__

を追加する。すなわち、

#pragma include_alias( "dxtrans.h", "qedit.h" )
#define __IDxtCompositor_INTERFACE_DEFINED__
#define __IDxtAlphaSetter_INTERFACE_DEFINED__
#define __IDxtJpeg_INTERFACE_DEFINED__
#define __IDxtKey_INTERFACE_DEFINED__
#include <qedit.h>

とする。
また、
VisualStudio2012のメニューバーから
デバッグ(D)→***のプロパティ...
→構成プロパティ→全般→全般→プラットフォームツールセット
に置いて、
「Visual Studio 2012 (v110)」から「Visual Studio 2012 - Windows XP (v110_xp)」に変更する。
さらに、
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include
に「qedit.h」を(古いSDKもしくはインターネットから拾ってきて)追加する。
注意:この場合の「qedit.h」は無編集状態のもの。(エラー回避のためのコメントアウトをしていないもの)

追記
「C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include」
に「qedit.h」を(古いSDKもしくはインターネットから拾ってきて)追加すれば、「Visual Studio 2012 (v110)」のままでOK
*/
//===============================================
//グローバル変数
const TCHAR szWndClass[] = _T("TestProgram");
static BITMAPINFO bmpInfo;
#define M_PI 3.14159265358979 /* 円周率 */
VIDEOINFOHEADER *pVideoInfoHeader;
BYTE *grabBuffer; //void *grabBuffer = NULL;でも良かったみたい。
     //&grabBuffer//grabBufferのアドレス
     // grabBuffer//画像の先頭アドレス
     //*grabBuffer//画像
unsigned char * rgbValues; //画像処理用の動的バッファのポインタ//unsignedにしないとバグる。具体的には「水色っぽい部分を見つけたら」「if ( rgbValues[p] > rgbValues[p + 1] && rgbValues[p] > rgbValues[p + 2]*2 )」などと判定する時にバグる。
//プロトタイプ宣言               // http://www.geocities.jp/ky_webid/cpp/language/038.html によれば、c++では必ずプロトタイプ宣言をしなくてはいけないらしい。(しないとリンクエラー「error LNK2001」になる)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
int DirectShow_for_USB_Camera(HWND hWnd);
class cameraCB;                //これは無くてもエラー吐いてない……良く分からん……。
// http://miraiware.net/memo/dshow-capture.html //
class cameraCB : public ISampleGrabberCB//ISampleGrabberCBクラスを継承
 {
 public:
 cameraCB(){};
 ~cameraCB(){};
 HWND hWnd;

 STDMETHODIMP_(ULONG) AddRef() { return 2; }//インターフェイスの参照カウントを 1 ずつ増やす。
 STDMETHODIMP_(ULONG) Release() { return 1; }//インターフェイスの参照カウントを 1 ずつ減少させる。

 /**
 * インターフェースの生成
 */
 STDMETHODIMP QueryInterface(REFIID riid,void **ppv)
 {
//  if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown ) {
//   *ppv = (void*)static_cast(this);
//   return NOERROR;
//  }
  return E_NOINTERFACE;
 }

 /**
 * バッファサンプリング コールバック関数
 * @param dblSampleTime サンプル時間
 * @param pSample IMediaSample
 */
 STDMETHODIMP SampleCB( double dblSampleTime, IMediaSample* pSample ) {
  return S_OK;
 }
 /**
 * バッファサンプリング コールバック関数
 * @param dblSampleTime サンプル時間
 * @param pBuff バッファ
 * @param lBufSize バッファサイズ
 */
 STDMETHODIMP BufferCB( double dblSampleTime, BYTE* pBuff, long lBufSize ){
  grabBuffer = pBuff;
  //SendMessage( hWnd, WM_CAPTURE, (WPARAM)pBuff, (LPARAM)lBufSize );
  InvalidateRect(hWnd, NULL, FALSE);// 再描画を促す
  return S_OK;
 }
};
// The entry point for the application
int WINAPI WinMain(
 HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPSTR lpCmdLine, int nCmdShow)
{
 WNDCLASS wc;
 wc.style         = CS_HREDRAW | CS_VREDRAW;
 wc.lpfnWndProc   = WndProc;
 wc.cbClsExtra    = 0;
 wc.cbWndExtra    = 0;
 wc.hInstance     = hInstance;
 wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
 wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
 wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
 wc.lpszMenuName  = NULL;
 wc.lpszClassName = szWndClass;
 if (!RegisterClass(&wc)) return FALSE;

 HWND hWnd = CreateWindow(
  szWndClass, _T("Title"),
  WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT, CW_USEDEFAULT,
  CW_USEDEFAULT, CW_USEDEFAULT,
  NULL, NULL,
  hInstance, NULL);

 if (!hWnd) return FALSE;
 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);
 DirectShow_for_USB_Camera(hWnd);
 MSG msg;
 while (GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 free(rgbValues);
 return (int) msg.wParam;
}
// Processes messages for the main window
LRESULT CALLBACK WndProc(
 HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 switch (msg) {
// case WM_CAPTURE:{
  /*
  //============================================================================================
  // http://www.geekpage.jp/programming/directshow/samplegrabber.php //
  // Bitmapに保存。
  // このサンプルではキャプチャ結果を見るために
  // Bitmapに保存しています。
  //
  HANDLE fh;
  BITMAPFILEHEADER bmphdr;
  DWORD nWritten;
  memset(&bmphdr, 0, sizeof(bmphdr));
  bmphdr.bfType = ('M' << 8) | 'B';
  bmphdr.bfSize = sizeof(bmphdr) + sizeof(BITMAPINFOHEADER) + lParam;
  bmphdr.bfOffBits = sizeof(bmphdr) + sizeof(BITMAPINFOHEADER);
  //適当に手動で測った結果、334~350枚/20秒程のBitmapが保存されていたので、fps(フレーム/秒)は16.7~17.5fps程だと推測できる。(ただし……と言うべきなのか、保存先はRAM_DISK)
  char buf[100];
  char filepath[]="R:\\新しいフォルダー\\";
  char filename[]="result";
  char fileext[]="bmp";
  static int i=0;
  sprintf(buf, "%s%s%d.%s",filepath,filename, i, fileext);
 // i++;
  fh = CreateFile(buf, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  //fh = CreateFile("R:\\result.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);
  WriteFile(fh,
   &pVideoInfoHeader->bmiHeader,
   sizeof(BITMAPINFOHEADER), &nWritten, NULL);
  WriteFile(fh, (long*)wParam, lParam, &nWritten, NULL);
  CloseHandle(fh);
  //============================================================================================
  */

  /*
  static int i;
  i++;
  314~322フレーム/20秒
  15.7~16.1fps
  */

  //InvalidateRect(hWnd, NULL, FALSE);// 再描画を促す  //BufferCBに処理を移した。
  //return 0;
//  }
 case WM_PAINT:{
  ////Bitmapをbyte[]に変換する
  // 24bppRGBフォーマットで値を格納
//  char rgbValues[921600];//640*480*3=921600
  // Bitmapをbyte[]へコピー
  memcpy(rgbValues, grabBuffer, (pVideoInfoHeader->bmiHeader.biSizeImage));

  // カラーフィルター
  //for (unsigned int p = 0; p < (pVideoInfoHeader->bmiHeader.biSizeImage); p += 3)
  //{
  // // 水色っぽい部分を見つけたら
  // if ( rgbValues[p] > rgbValues[p + 1] && rgbValues[p] > rgbValues[p + 2]*2 )
  // {
  //  // 赤で塗りつぶし
  //  rgbValues[p] = 0;       // 青
  //  rgbValues[p + 1] = 0;   // 緑
  //  rgbValues[p + 2] = 255; // 赤
  // }
  // else
  // {
  //  // それ以外は黒で塗りつぶし
  //  rgbValues[p] = 0;       // 青
  //  rgbValues[p + 1] = 0;   // 緑
  //  rgbValues[p + 2] = 0;   // 赤
  // }
  //}

  //1pixel辺りの力(質量×重力加速度?)を1として計算する。
  //ターゲットとするピクセルの合計「Sum of target pixels」//物理で言う質量の合計
  unsigned int SoTP=0;
  //原点(画面の左上)(0,0)、を中心として時計回りに回転しようとする力(トルク)の合計「Sum of Power」
  unsigned int xSoP=0;
  unsigned int ySoP=0;
  //重心「Center of gravity」
  unsigned int xCoG=0;//重心のx座標
  unsigned int yCoG=0;//重心のy座標
  unsigned int i=0;
  for (unsigned int y = 0; y < (unsigned int)(pVideoInfoHeader->bmiHeader.biHeight); y++)
  {
  for (unsigned int x = 0; x < (unsigned int)(pVideoInfoHeader->bmiHeader.biWidth ); x++)
  {
   double H=0;//0~360°
   double S=0;//0.0~1.0
   double V=0;//0.0~1.0//にするのが当然だが、計算コストの問題で0~255
   //RGBからHSVへ変換し色判定を行う。// http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93 //
   if( rgbValues[i+2] > rgbValues[i+1] && rgbValues[i+2] > rgbValues[i] )//赤>緑&&赤>青
   {
    //赤>緑&&赤>青
    if( rgbValues[i+1] > rgbValues[i] )//緑>青
    {
     //赤>緑>青
     H = 60*(  ((double)rgbValues[i+1]-(double)rgbValues[i])/((double)rgbValues[i+2]-(double)rgbValues[i])    ) + 0;//60*(G-B)/(MAX-MIN)+0
     S = ((double)rgbValues[i+2]-(double)rgbValues[i])/(double)rgbValues[i];//(MAX-MIN)/MAX
     V =  (double)rgbValues[i+2];//MAX
    }
    else
    {
     //赤>青>緑
     H = 60*(  ((double)rgbValues[i]-(double)rgbValues[i+1])/((double)rgbValues[i+2]-(double)rgbValues[i+1])  ) + 0;//60*(B-G)/(MAX-MIN)+0
     S = ((double)rgbValues[i+2]-(double)rgbValues[i+1])/(double)rgbValues[i+1];//(MAX-MIN)/MAX
     V =  (double)rgbValues[i+2];//MAX
    }
   }
   else if( rgbValues[i+1] > rgbValues[i] )//緑>青
   {
    //緑>青
    if( rgbValues[i] > rgbValues[i+2] )//青>赤
    {
     //緑>青>赤
     H = 60*(  ((double)rgbValues[i]-(double)rgbValues[i+2])/((double)rgbValues[i+1]-(double)rgbValues[i+2])  ) + 120;//60*(B-R)/(MAX-MIN)+120
     S = ((double)rgbValues[i+1]-(double)rgbValues[i+2])/(double)rgbValues[i+2];//(MAX-MIN)/MAX
     V =  (double)rgbValues[i+1];//MAX
    }
    else
    {
     //緑>赤>青
     H = 60*(  ((double)rgbValues[i+2]-(double)rgbValues[i])/((double)rgbValues[i+1]-(double)rgbValues[i])    ) + 120;//60*(R-B)/(MAX-MIN)+120
     S = ((double)rgbValues[i+1]-(double)rgbValues[i])/(double)rgbValues[i];//(MAX-MIN)/MAX
     V =  (double)rgbValues[i+1];//MAX
    }
   }
   else//青>緑
   {
    //青>緑>赤
    if( rgbValues[i+1] > rgbValues[i+2] )//緑>赤
    {
     //青>緑>赤
     H = 60*(  ((double)rgbValues[i+2]-(double)rgbValues[i+1])/((double)rgbValues[i]-(double)rgbValues[i+2])  ) + 240;//60*(R-G)/(MAX-MIN)+240
     S = ((double)rgbValues[i]-(double)rgbValues[i+2])/(double)rgbValues[i+2];//(MAX-MIN)/MAX
     V =  (double)rgbValues[i];//MAX
    }
    else
    {
     //青>赤>緑
     H = 60*(  ((double)rgbValues[i+1]-(double)rgbValues[i+2])/((double)rgbValues[i]-(double)rgbValues[i+1])  ) + 240;//60*(G-R)/(MAX-MIN)+240
     S = ((double)rgbValues[i]-(double)rgbValues[i+1])/(double)rgbValues[i+1];//(MAX-MIN)/MAX
     V =  (double)rgbValues[i];//MAX
    }
   }


   /*
   次の値に大体合わせてみた。
   R G B
   81 83 123
   82 84 141
   112 152 255
   116 158 255
   H=60*2/42+240=2.857+240=242.857
   S=42/123=0.3414
   H=60*40/143+240=256
   S=143/255=0.56
   */
   // 水色っぽい部分を見つけたら
   //if (rgbValues[i] > rgbValues[i + 1] && rgbValues[i] > rgbValues[i + 2]*2)
   if( 200<=H && H<=260 && 0.2<S && S<1.0 && V<120)//ここの値をプログラムを起動したまま操作できるようにすべき(じゃないと色判定の設定に時間が掛かりすぎる)
   {
    // 赤で塗りつぶし
    rgbValues[i] = 0;       // 青
    rgbValues[i + 1] = 0;   // 緑
    rgbValues[i + 2] = 255; // 赤
    //ターゲットとするpixelの合計をカウント
    SoTP++;
    //回転しようとする力(トルク)の合計をカウント//最後に計算すると逆に計算コストがかさむ予感がするので1pixelごとに次々合計していく。
    xSoP+=x;
    ySoP+=y;
   }
   else
   {
    // それ以外は黒で塗りつぶし
    rgbValues[i] = 0;       // 青
    rgbValues[i + 1] = 0;   // 緑
    rgbValues[i + 2] = 0;   // 赤
   }
   i+=3;
  }
  }
  BOOL flag;//重心の座標を描写するかどうかのフラグ
  if(1000<SoTP)//ターゲットが50pixel以下の場合は誤差として計算しない。//かつ//0での割り算防止(0割が発生するとCPU使用率が無駄に上がる)
  {
   //重心の算出
   xCoG=xSoP/SoTP;
   yCoG=(pVideoInfoHeader->bmiHeader.biHeight)-(ySoP/SoTP);//なんか良く分からんけどこのままだと画面の左下が原点(0,0)となってしまうので、次の表示のためにも原点(0,0)を画面の左上に変換しておく。
   flag=TRUE;
   // http://ja.wikipedia.org/wiki/%E4%B8%89%E8%A7%92%E6%B8%AC%E9%87%8F //
   //角度の導出   //カメラ同士の距離を18.8cmとして計算していく。
   //y:角度、x:座標
   //  y=((90-60)/320)*x+60
   //⇔y=(3/32)*x+60
   double theta1;//θ1[°]//右カメラ
   double theta2;//θ2[°]//左カメラ
   theta1 = ((0.09375)*xCoG)+60;//(3/32)だと何故か上手く計算してくれないので(0.09375)に書き直している。
   theta2 = (0.09375)*320+60;//取りあえずカメラが一台しか無いので固定値(つまり、ターゲットが、もう一台のカメラ(右カメラ)があるべき位置の真正面上を移動すると仮定して距離を計算していく)
   //ここで注意したいのはsin,cos,tanは度数法(度(°))では無く弧度法(ラジアン(rad))である事。
   //そのため上記の度数法で表している角度を弧度法に変換する。(一気に弧度法で計算していないのはデバックでブレークポイントを設置した時に弧度法で言われても分度器で角度を測れないため。必要が無い場合は計算をまとめて良い)
   //M_PIは自分で定義する必要がある。
   double rTheta1;//θ1[rad]//右カメラ
   double rTheta2;//θ2[rad]//左カメラ
   rTheta1 = M_PI*theta1/180;//[rad]=2π*[°]/360⇔[rad]=π*[°]/180
   rTheta2 = M_PI*theta2/180;
   //ターゲットとの距離を導出
   double dVertical; //垂直距離「     Vertical distance」//単位は[cm]//カメラとカメラを結ぶ直線にターゲットから垂線を下ろした時、その降ろした垂線の距離
   double dHorizontal; //水平距離「   Horizontal distance」//単位は[cm]//カメラとカメラを結ぶ直線にターゲットから垂線を下ろした時、に直線の交点とカメラとカメラの間の中点との距離
   double dStraight; //直線距離「Straight-line distance」//単位は[cm]//カメラとカメラの中点とターゲットとの間の直線距離
   dVertical = (double)(18.8)*(double)sin(rTheta1)*(double)sin(rTheta2)/(double)sin(rTheta1+rTheta2);//三角測量と同様の方法で導出
   dHorizontal = (dVertical/tan(rTheta1))-((18.8)/2);//三角関数より導出
   dStraight = sqrt(   (dVertical*dVertical)+(dHorizontal*dHorizontal)   );//三平方より導出
   //int xxx;//ブレークポイントを設定でき無かったので意味は無いけど追加
   //xxx=0;
  }
  else
  {
   flag=FALSE;
  }

  //memcpy(grabBuffer, rgbValues, (pVideoInfoHeader->bmiHeader.biSizeImage));   //これをやると画面が両方真っ赤になる。のでダメ
  //背景の画像(右側の赤と黒の画像)を描写する
  HDC hdc;
  PAINTSTRUCT ps;
  hdc = BeginPaint (hWnd, &ps);
  //SetDIBitsToDevice(
  // hdc , 650 , 0 ,
  // pVideoInfoHeader->bmiHeader.biWidth , pVideoInfoHeader->bmiHeader.biHeight , //これをやると画面が両方真っ赤になる。のでダメ
  // 0 , 0 , 0 , pVideoInfoHeader->bmiHeader.biHeight ,
  // grabBuffer , &bmpInfo , DIB_RGB_COLORS
  //);
  SetDIBitsToDevice(
   hdc , 650 , 0 ,
   pVideoInfoHeader->bmiHeader.biWidth , pVideoInfoHeader->bmiHeader.biHeight ,
   0 , 0 , 0 , pVideoInfoHeader->bmiHeader.biHeight ,
   rgbValues , &bmpInfo , DIB_RGB_COLORS
  );
  if(flag){
   //重心の位置を描写する
   HPEN hPen;  // 論理ペンへのハンドル
   hPen = CreatePen(PS_SOLID, 1, RGB(0, 0xFF, 0));
   SelectObject(hdc, hPen);      // 論理ペンを登録

   MoveToEx(hdc, 650+xCoG, 0, NULL);  // (20, 20) に移動
   LineTo(hdc, 650+xCoG, (pVideoInfoHeader->bmiHeader.biHeight ));         // (200, 20) まで直線を描画
   MoveToEx(hdc, 650, yCoG, NULL);  // (20, 20) に移動
   LineTo(hdc, 650+(pVideoInfoHeader->bmiHeader.biWidth ), yCoG);         // (20, 200) まで直線を描画

   DeleteObject(hPen);           // 論理ペンを破棄
  }
  EndPaint( hWnd, &ps );
  return 0;
  }
 //case WM_CREATE:{
 // return 0;
 // }
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 default:
  return DefWindowProc(hWnd, msg, wParam, lParam);
 }
}

int DirectShow_for_USB_Camera(HWND hWnd){
 HRESULT hr;

 // --- COMの初期化
 hr = CoInitialize(NULL);
 if(FAILED(hr)){
  printf("CoInitialize Failed!\n");
  exit(1);//return 1;と同義
 }
 // --- 1.フィルタグラフマネージャ作成(DirectShow の中心的なオブジェクト)
 IGraphBuilder *pGraph = NULL;
 CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraph);
        // http://marupeke296.com/DSW_No5_FullScreenProgram.html //② フィルタグラフマネージャの取得
        // フィルタグラフマネージャの取得
        //
        // DirectShowのプログラムは実質ここから始まります。まずはフィルタグラフを管理するフィルタグラフマネージャを作成します。取得するインターフェイスはIGraphBuilderです。このインターフェイスはWindows自体が提供するためCoCreateInstance関数というグローバルな関数を用いて生成します。
        //
        //IGraphBuilder *pGB = NULL;
        //HRESULT hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGB);
        // CoCreateInstance関数はCOMとして登録されているインターフェイスを生成して返してくれます。
        //第1引数にインターフェイスを含むクラスIDを指定します。IGraphBuilderを取得するときに指定するIDはCLSID_FilterGraphと決まっているので、無条件で上のように与えます。
        //第2引数はCOMオブジェクトの内包状態を指定するフラグなのですが、殆どのCOMはNULLでOK。
        //第3引数は生成するCOMの実装ソースがあるDLLの所在を表します。IGraphBuilderについてはDLLがローカルにありますのでインプロセスであるCLSCTX_INPROC_SERVERを指定します。
        //第4引数に欲しいインターフェイスの固有IDを指定します。IGraphBuilderインターフェイスを表すIDはIID_IGraphBuilderです。
        //関数が成功すると第5引数にIGraphBuilderインターフェイスへのポインタが返ります。
        //
        //この処理はもう決まりきっていますので、変更のしようがありません。「IGraphBuilderの取得の仕方はこうだ!」と決めてしまってよいでしょう。
        //
        // IGraphBuilderの役目はフィルタグラフにかかわるインターフェイスの提供、およびフィルタグラフの管理です。

 // --- 2.システムデバイス列挙子の作成(接続されているデバイスを調べるためのオブジェクト) // ⑥ キャプチャデバイス(USBカメラ等)を選択するため,システムデバイス列挙子を作成する.つまり,PCに接続されているデバイスを列挙するってこと.
 ICreateDevEnum *pDevEnum = NULL;
 CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void **)&pDevEnum);

 // 列挙子の取得 // ⑦ 列挙したキャプチャデバイスのカテゴリ列挙子を取得する.音声なのか,映像なのか,とかそういうこと.ビデオキャプチャの場合は,CLSID_VideoInputDeviceCategoryを指定すればよい.
 IEnumMoniker *pClassEnum = NULL;
 pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
 if(pClassEnum == NULL){
  printf("Device does not exist!\n");
  pDevEnum->Release();
  CoUninitialize();
  return 1;
 }
 pDevEnum->Release(); // 以後に不要なメモリーをリリース
 IBaseFilter *pbf = NULL;
 IMoniker *pMoniker = NULL;
 ULONG cFetched;    //ULONGはCOMの型……らしいのだが……???
 if(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK){    // IEnumMoniker.Next メソッド(http://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.comtypes.ienummoniker.next(v=vs.85).aspx)←ハイパーリンクがバグってる……。
                  // Next(1, &pMoniker, &cFetched)
                  //返されるモニカの数,列挙されたモニカへの参照を格納,列挙されたモニカの実数への参照を格納
  pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pbf); // 最初のモニカをフィルタオブジェクトにバインド
  pMoniker->Release();
 }
 pClassEnum->Release(); // 以後に不要なメモリーをリリース

 pGraph->AddFilter(pbf, L"USB Camera Capture"); // グラフにフィルタを追加
        // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑥ ソースフィルタの作成と登録
        // IGraphBuilder::AddSourceFilter関数を使用した場合は自動的にフィルタグラフへの登録が行われるので、改めてIGraphBuilder::AddFilter関数を呼ぶ必要はない。
        // ファイル名に関して重要な注意があります。第2引数の型は「LPCWSTR」になっています。これはワイド文字(例えばunicode)というやつです。私たちが以前まで普通に使っていたのはマルチバイト文字でして、これは「LPCSTR」です。マルチバイト文字とワイド文字は互換性がありません。ですから、ワイド文字がコンパイラオプションに設定されていない環境では、文字をプログラマが明示的にワイド文字に変換する必要があります。ただリテラル(ダブルクォーテーションで囲まれた文字)で定義する場合は「L接頭子」を付けると常にワイド文字として扱われます。

 //============================================================================================
 // http://www.geekpage.jp/programming/directshow/samplegrabber.php //BitMapキャプチャ用フィルタの用意
 IBaseFilter *pSampleGrabberFilter;
 // SampleGrabber(Filter)を生成
 CoCreateInstance(CLSID_SampleGrabber,
      NULL,
      CLSCTX_INPROC,
      IID_IBaseFilter,
      (LPVOID *)&pSampleGrabberFilter);
 ISampleGrabber *pSampleGrabber;
 // FilterからISampleGrabberインターフェースを取得します
 pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (LPVOID *)&pSampleGrabber);
 AM_MEDIA_TYPE am_media_type;
 // SampleGrabberを接続するフォーマットを指定。
 // ここがポイントです。
 // ここの指定の仕方によりSampleGrabberの挿入箇所を
 // 決定できます。このサンプルのような指定をすると
 // 画面出力の寸前でサンプルを取得できます。
 ZeroMemory(&am_media_type, sizeof(am_media_type));
 am_media_type.majortype = MEDIATYPE_Video;
 am_media_type.subtype = MEDIASUBTYPE_RGB24;
 am_media_type.formattype = FORMAT_VideoInfo;
 pSampleGrabber->SetMediaType(&am_media_type);
 // GraphにSampleGrabber Filterを追加
 pGraph->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
 //============================================================================================
 // --- 3.キャプチャグラフ作成
 ICaptureGraphBuilder2 *pCapture = NULL;
 CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **) &pCapture);
 pCapture->SetFiltergraph(pGraph); // フィルタグラフをキャプチャグラフに組み込む//ICaptureGraphBuilder2インターフェイスの初期化
             //
             //http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑦ ICaptureGraphBuilder2インターフェイスの取得
             // フィルタの接続は意外と面倒なものなのですが、これに関してヘルパーインターフェイスが用意されています。「ICaptureGraphBuilder2」というインターフェイスは、フィルタの接続をある程度自動化してくれまして、使い勝手がとても良いものです。ここではこれを使うことにしましょう。このインターフェイスの取得にもCoCreateInstance関数を用います。
             // 取得したICaptureGraphBuilder2インターフェイスは初期化が必要なのですが、これはとても簡単でして先に取得したIGraphBuilderインターフェイスをICaptureGraphBuilder2::SetFiltergraph関数に渡すだけです。
// pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pbf, NULL, NULL); // キャプチャグラフの設定、グラバをレンダリング出力に設定//キャプチャ画像を表示するためのビデオプレビューグラフを作成する
 pCapture->RenderStream(NULL, NULL, pbf, pSampleGrabberFilter, NULL);//上記に変わってこのように第三引数以外全てNULLに指定してもなんとかなったりする。(キャプチャするには第4引数が必要)
             //
 //注意:ここの第4引数に「pSampleGrabberFilter」 // http://www.crystal-creation.com/software/technical-information/library/directshownet/filter-graph/capture-graph-builder2.htm#contents-3
 //     を指定して、経由するフィルタを明示する。 // int RenderStream(
 //     これをしないとキャプチャを取れない。  //    DsGuid PinCategory,       // ピン カテゴリ      //接続元と先が明らかである場合はNULL指定によりIntelligent Connectに接続を任せることも出来ます。
             //    DsGuid MediaType,         // 出力ピンのメディア タイプ   //NULL指定をすると、すべてIntelligent Connectに任せます。
             //    object pSource,           // 接続先のフィルタ      //ここは必ず指定する必要があります。
             //    IBaseFilter pfCompressor, // 経由するフィルタ (圧縮フィルタなど) //NULLが指定できます。
             //    IBaseFilter pfRenderer    // シンク フィルタ (レンダラなど)  //NULLを指定した場合、関数はデフォルトの入力フィルタへ接続を試みる。
             //   );
             //
             // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑧ フィルタの接続 を参照

 //============================================================================================
 // http://www.geekpage.jp/programming/directshow/samplegrabber.php //画像の縦横のpixelとデータサイズの取得
 // 接続情報取得。
 // この処理はRenderFileによりGraphが構成された後に
 // 行う必要があります。
 pSampleGrabber->GetConnectedMediaType(&am_media_type);
 pVideoInfoHeader = (VIDEOINFOHEADER *)am_media_type.pbFormat;
 // ビデオ ヘッダーには、ビットマップ情報が含まれる。
 // ビットマップ情報を BITMAPINFO 構造体にコピーする。
 CopyMemory(&bmpInfo.bmiHeader, &(pVideoInfoHeader->bmiHeader), sizeof(BITMAPINFOHEADER));// http://www.greenwood.co.jp/~k-aki/article/directshow_movie/directshow_movie.html //のdirectshow2.cpp// http://www.greenwood.co.jp/~k-aki/article/directshow_movie/directshow2/directshow2.cpp //
 //// 画像(映像)の幅と高さを表示
 //// サンプルをわかりやすくするために表示しているだけなので、
 //// 必ず必要というわけではありません。
 //printf("size = %dx%d\n",
 // pVideoInfoHeader->bmiHeader.biWidth,
 // pVideoInfoHeader->bmiHeader.biHeight);
 //// データサイズを表示
 //// これも説明のために表示しています。
 //printf("sample size = %d\n",
 // am_media_type.lSampleSize);
 // Grabを行う事を設定            //キャプチャをするかしないか選択
 // SetBufferSamplesを行わないとバッファから
 // データを取得できません。
 // 不必要に負荷をかけたくない場合にはFALSEにしておいて、
 // データを取得したくなったら、TRUEに変える
 // という方法もできます。
 hr = pSampleGrabber->SetBufferSamples(FALSE);//データを取得するか?「TRUE」or「FALSE」//コールバック関数を用いて連続キャプチャするので、FALSEで問題ない。//http://miraiware.net/memo/dshow-capture.html
 pSampleGrabber->SetOneShot(FALSE);
 // プレビュー開始時
 cameraCB *cb = new cameraCB();
 cb->hWnd = hWnd;//これが無いとウィンドウを識別できない……。
 pSampleGrabber->SetCallback(cb,1);
 //pSampleGrabber->SetCallback(this,1);

 // プレビュー停止時
 //pSampleGrabber->SetCallback(NULL, 1);
 //============================================================================================

 // http://ugd555.fc2web.com/frame.html //⑤
 // IVideoWindowインタフェース(ビデオウィンドウの設定)
 IVideoWindow *pVWindow=NULL;
 hr = pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVWindow);
 // http://ugd555.fc2web.com/frame.html // ⑫
 // プレビューウィンドウの設定
 hr = pVWindow->put_Owner((OAHWND)hWnd);
 //hr = pVWindow->put_Owner(NULL);とすると子ウィンドウを切り離す事ができる。
 hr = pVWindow->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);//WS_CHILD:子ウィンドウを作成します。|WS_CLIPCHILDREN:親ウィンドウの内部で描画するときに、子ウィンドウが占める領域を除外します。親ウィンドウを作成するときに使います。//WS_CLIPCHILDREN不要かと思ったけど、無いとウィンドウを消したとき?(うる覚え……)にバグる。
// RECT rc;
// GetClientRect(hWnd, &rc);//親ウィンドウのサイズを取得
// pVWindow->SetWindowPosition(0, 0, rc.right, rc.bottom);
 pVWindow->SetWindowPosition(0, 0, pVideoInfoHeader->bmiHeader.biWidth, pVideoInfoHeader->bmiHeader.biHeight);
 hr = pVWindow->put_Visible(OATRUE);//ウィンドウを表示する。//無くても動くけど、たぶんあった方が良いって感じのものかな?//ShowWindow()関数に相当する物だと言う事にしておく。

 //画像処理用の動的メモリを確保
 rgbValues = (unsigned char *)malloc(640*480*3 * sizeof(unsigned char));//unsignedにしないとバグる。具体的には「水色っぽい部分を見つけたら」「if ( rgbValues[p] > rgbValues[p + 1] && rgbValues[p] > rgbValues[p + 2]*2 )」などと判定する時にバグる。

 // --- 5.キャプチャ開始
 IMediaControl *pMC = NULL;
 pGraph->QueryInterface(IID_IMediaControl, (LPVOID *)&pMC);  // MediaControlインターフェース取得(グラフの開始・停止を制御するインターフェイス)
 pMC->Run();
      // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑩ フィルタグラフの実行
      // フィルタグラフの接続が全て終了し、表示サイズを正しく指定すれば、いつでも動画を再生することができます。再生を担当するインターフェイスは、IGraphBuilderから生成できるIMediaControlインターフェイスです。

 // --- 6.終了待ち
 MessageBox(NULL, TEXT("Click to stop."), TEXT("Message"), MB_OK);
 // getch()で待つのはだめ、レンダリング表示のためにシステムに実行を返す必要がある
 // --- 終了処理
 pMC->Stop();
 hr = pVWindow->put_Visible(OAFALSE);
 pMC->Release();    // http://marupeke296.com/DSW_No5_FullScreenProgram.html //⑪ 終了処理
 pVWindow->Release();  // 解放順番はきっと厳密ではないのだろうと思うのですが、一応作成順と逆に解放しておきます。
 // プレビュー停止時
 pSampleGrabber->SetCallback(NULL, 1);//これをしないと正しく終了できない。「ハンドルされない例外が 0x56F067C7 (qedit.dll) で発生しました(Image_Processing_Ver0.7.exe 内): 0xC0000005: 場所 0xFEEEFEF6 の読み取り中にアクセス違反が発生しました。」と言ってエラーを吐く。
 delete cb;
 pCapture->Release();
 pSampleGrabber->Release();
 pSampleGrabberFilter->Release();
 pbf->Release();
 pGraph->Release();
 CoUninitialize(); // COMを終了
 return 0;
}

  Top  > Direct Show 関連 > Direct Show によるUSBカメラのリアルタイム画像処理

Win32APIによるシリアル通信・その②

  Top  > Win32APIによるシリアル通信 > Win32APIによるシリアル通信。続き。

前回(http://the-united-front.blogspot.jp/2013/09/windows-api-rs232c-usb23-win7-x64.html)
より、シリアル通信はできるようになったが、このままではデータの受信が今一だった。そのため、今回はデータの受信処理を書いてみた。


前回より、
>下記のようなデータを送信した場合、
「"□□□□□\r\n△△△△△\r\n\0\0\0……"」
受信したデータが、
「"□□□□□\r\n△△\0\0\0……"」「"△△△\r\n\0\0\0……"」のようにデータが二つの
バッファに分かれてしまう場合があるので、きちんと処理しないとバグ
引き起こします。
と言うのが、ほかのアプリケーションのCPU使用率などが引き金となって結構頻繁に発生します。
要は、受信したデータの最後に終了コードがきちんと挿入されている事を確認してから、
次の処理へデータを引き渡たさないとバグを引き起こすので、
確認しましょう、と言う話です。


終了コードを「\r\n」としているが、一つ注意してほしいのは、もしかしたら「\n\r」の間違いだったかもしれない。(一まず私の環境ではこれで動くので良しとしています。(SH7144Fとの通信ですが……))


見難いのでファイルとして置いておく。
http://ux.getuploader.com/ADMIS/download/9/Serial_Communication_Interface_Ver1.1%E3%82%88%E3%82%8A%E6%8A%9C%E7%B2%8B.cpp


主な変更点





全体


  Top  > Win32APIによるシリアル通信 > Win32APIによるシリアル通信。続き。