網頁

2017年5月16日 星期二

[OpenCV] 3.2.0 實裝心得


OpenCV 3.2.0 已經在 2016/12/23 公開發行. 除了一些問題的修正外, 這一個版本的 Windows 版本是在 VC14, 也就是 Visual Studio 2015. 剛好接下來的工作也議定在 Visual Studio 2015 Community 上開發, 於是就選定用這個版本. 這個版本的使用上, 遇到的一些問題, 做個紀錄.

下載 :

直接在 OpenCV 的下載網頁下載 OpenCV download, 如果是在 Windows, 建議直接使用 Win pack 這個選項, 除了已經 build 好的 library, source code 也已經包含在裡面. 下載的檔名會是像 opencv-3.2.0-vc14.exe 這個樣子.

安裝 (解壓縮) :

下載下來的的 exe 是壓縮檔, 隨便解到那個目錄都可以. 這樣應該不算是安裝吧.

內容 :

3.2.0 的 Win pack 有 java 和 python 以及 VC 的版本, 除了 VC 以外, 都有 X86/X64 的版本, VC 則只有 X64 的版本. 如果要 X86 的版本, 就需要自己 build. 我需要的不只是 X86, 還要 static library 的版本, 所以需要自己 build.

2017年4月10日 星期一

[VideoInput] Download


Download VideoInput Libray from GitHub

https://github.com/ofTheo/videoInput to D://VideoInput
Extract
image

Synchronize project setting

image
image

Modification for Visual Studio 2015

VideoInput.cpp Line 821 as
        char * name =  (char*) videoInput::getDeviceName(i);

2017年4月9日 星期日

[OpenCV] Using OpenCV static library in MFC application


Create MFC Application

image

Add Picture Control

image

Add Initial codes in OnInitDialog

// TODO:  Add extra initialization here
CRect rect;
CWnd *pWnd = GetDlgItem(IDC_LIVE);
pWnd->GetWindowRect(&rect);
ScreenToClient(&rect); //optional step - see below
cv::namedWindow("IDC_STATIC_OUTPUT", 0);
cvResizeWindow("IDC_STATIC_OUTPUT", rect.right - rect.left, rect.bottom - rect.top);
HWND hWnd = (HWND)cvGetWindowHandle("IDC_STATIC_OUTPUT");
HWND hParent = ::GetParent(hWnd);
::SetParent(hWnd, GetDlgItem(IDC_LIVE)->m_hWnd);
::ShowWindow(hParent, SW_HIDE);
if (m_video.open(0))
{
    SetTimer(0, 33, NULL);
}


Add OnTimer Handler to drawing

image
void COpenCV_Static_MFCDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    cv::Mat frame;
    m_video >> frame;
    cv::imshow("IDC_STATIC_OUTPUT", frame);
    CDialogEx::OnTimer(nIDEvent);
}
Update project setting
/Configuration Prperties/General
Use of MFC –> Use MFC in a Static Librar
Character Set –> Use Multi-Byte Character Set
/Configuration Properties/C/C++/Code Generation
Runtime Library –> Multi-thread (/MT)
Security Check –> Disable Security Check (/GS-)
/Configuration Properties/VC++ Directories
Include Directories: D:\OpenCV-3.2.0\opencv\build\include
Library Directires
image
/Configuration Properties/Linker/Input
Additional Dependencies
opencv_highgui320.lib
opencv_core320.lib
ippicvmt.lib
zlib.lib
opencv_imgproc320.lib
opencv_imgcodecs320.lib
IlmImf.lib
libjpeg.lib
libjasper.lib
libpng.lib
libtiff.lib
libwebp.lib
opencv_video320.lib
opencv_videoio320.lib
vfw32.lib

[OpenCV] Using static library in Visual Studio 2015 Console mode


Create Visual Studio 2015 console program

image

Add VideoCapture calls

#include "stdafx.h"
#include <opencv2/opencv.hpp>
int main()
{
    cv::Mat frame;
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) {
        return -1;
    }
    while (true) {
        if (!cap.read(frame))
            break;
        cv::Mat src = cv::Mat(frame);
        cv::imshow("window", src);
        cv::waitKey(30);
    }
}

Add OpenCV include path

D:\OpenCV-3.2.0\opencv\build\include

Add OpenCV library path

D:\OpenCV-3.2.0\opencv\build\x86-static\lib\Release
D:\OpenCV-3.2.0\opencv\build\x86-static\3rdparty\ippicv\ippicv_win\lib\ia32
D:\OpenCV-3.2.0\opencv\build\x86-static\3rdparty\lib\Release

Add OpenCV library

kernel32.lib
user32.lib
gdi32.lib
winspool.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
comdlg32.lib
advapi32.lib
opencv_highgui320.lib
opencv_core320.lib
ippicvmt.lib
zlib.lib
opencv_imgproc320.lib
opencv_imgcodecs320.lib
IlmImf.lib
libjpeg.lib
libjasper.lib
libpng.lib
libtiff.lib
libwebp.lib
opencv_video320.lib
opencv_videoio320.lib
vfw32.lib

Modify project configuration

image
image
image

Compile and Run

[OpenCV] 自行編譯 library for Visual Studio 2015

 

OpenCV 3.2.0 的安裝版本只有 X64 的 DLL 版本. 如果需要的不是這個版本, 就要自行編議. 譬如說, 主程式用到的是 X86 的編譯設定, 就需要 X86 的 DLL 版本. 如果不想要 release 的時候, 還要附帶一堆 DLL, 或是程式裡面已經用到了 /MT 的編譯選項, 那麼就要自行編譯 static library 的版本.

OpenCV 3.2.0 的 source code, 需要用 CMake 來做 configuration, 然後才能在 Visual Studio 2015 中編譯.

Download OpenCV 3.2.0 win pack

1. 下載 win pack (opencv-3.2.0-vc14.exe) from opencv.org

2. 解壓縮到硬碟中的任意目錄 : 這裡解壓縮到 D:\OpenCV-3.2.0image

Download CMake

1. 在 CMake 的網站下載安裝檔. Download Page, 他有 ZIP 版及 MSI 不同的安裝程式, 以及 32/64 的版本. 除了不要在 32 位元的系統上安裝 64 位元的版本外, 其他應該都沒有問題.

2. 安裝完之後, 會在程式列多一個 CMake 的目錄, 其中的 CMake( cmake-gui ) 就是稍後要來做 configuration 用的.

Configure build with CMake

1. 執行 cMake-gui
2. 設定 OpenCV 原始碼及目的碼的位置. source code 在解壓縮出來的目錄中, 目的碼最好另外指定. 以下指定為 build/x86-static, 名稱就是他的意思.
image
3. 按下 “configure” 開始設定, generator 選擇 VS 2015
image
4. 等待 configuration 結束
5. 清除選項中的 BUILD_SHARED_LIBS, 以編譯靜態連結版本
image

6. 按下 “generate” 開始產生 Visual Studio 2015 的專案檔案
7. 等待 …
image

用 Visual Studio 2015 編譯 OpenCV Library

1. 執行 VS2015, 並開啟  “OpenCV” solution file
image
2. Build Solution

這個 solution 中, 包含了 66 個專案, 所以要 build 一段時間, build 完會產生如下的檔案

image

2017年2月16日 星期四

OpenCV Mask (Filter) performance comparison


OpenCV 的內建 filter2D 函式, 實作了影像處理上常用的 Mask, 或是稱為 Filter, Convolution, 的操作. 通常這也是影像相關的演算法的骨幹, 也是最耗費時間的環節.
Mask 的運算時間, 和 Mask 的組成有絕對的關係, 以下套用不同的 Mask, 來觀察他運算時間上的差異.
比較 filter2D()以及自行撰寫的 C 函式
Index Implement ms
C Function *output++ = saturate_cast<uchar>(5*current[i]
              -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
64.909
filter2D Mat kernel = (Mat_<char>(3,3) <<  0, -1,  0, -1,  5, -1,   0, -1,  0);
filter2D( src, dst1, src.depth(), kernel );
3.030
以 C 的實作來說, 差不多就是以上所列出來的程度. OpenCV 可以做到將近 20 倍的速度, 應該是用了一些硬體加速的方法.
不同 Mask 的時間比較
Index Implement ms
1x1 Mat kernel_1x1 = (Mat_<char>(1,1) <<  1); 1.134
3x3 Mat kernel_3x3 = (Mat_<char>(3,3) <<  0, -1, 0, -1, 5, -1, 0, -1, 0); 2.718
5x5 Mat kernel_5x5 = (Mat_<char>(5,5) <<  0,0,-1,0,0,0,0,-1,0,0,-1,-1,9,-1,-1,0,0,-1,0,0,0,0,-1,0,0); 4.313
7x7-1 Mat kernel_7x7 = (Mat_<char>(7,7) <<
        0,0,0,-1,0,0,0,
        0,0,0,-1,0,0,0,
        0,0,0,-1,0,0,0,
        -1,-1,-1,13,-1,-1,-1,
        0,0,0,-1,0,0,0,
        0,0,0,-1,0,0,0,
        0,0,0,-1,0,0,0);
6.170
7x7-2 Mat kernel_7x7_2 = (Mat_<char>(7,7) <<
        -4,-3,-2,-1,-2,-3,-4,
        -3,-3,-2,-1,-2,-3,-3,
        -2,-2,-2,-1,-2,-2,-2,
        -1,-1,-1,107,-1,-1,-1,
        -2,-2,-2,-1,-2,-2,-2,
        -3,-3,-2,-1,-2,-3,-3,
        -4,-3,-2,-1,-2,-3,-4);
22.44
隨著 Mask 的 size 變大, 所耗費時間也跟著增加, 有趣的是 Mask 的內容也有很大的影響, 可見 OpenCV 會對於 零項作刪去的最佳化. 

2017年2月15日 星期三

[OpenCV] LUT access time comparison


比較 OpenCV 做 Look Up Table 的效率.
OpenCV 提供了幾種作 Look Up Table 的動作的方法. 以下對這幾種方法做效率上的比較, 同時加上以 C 的 Pointer 的存取方式.
Index Reference Code ms
C Pointer for( int i = 0; i < scan_size; i ++ )
{
    * pDest = pTable[ * pSrc ];
    pDest ++;
    pSrc ++;
}
4.98
C [] for( i = 0; i < nRows; ++i)
{
    p = I.ptr<uchar>(i);
    for ( j = 0; j < nCols; ++j)
    {
        p[j] = table[p[j]];
    }
}
5.12
iterator MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
    (*it)[0] = table[(*it)[0]];
    (*it)[1] = table[(*it)[1]];
    (*it)[2] = table[(*it)[2]];
}
170.45
Random Access Mat_<Vec3b> _I = I;
for( int i = 0; i < I.rows; ++i)
   for( int j = 0; j < I.cols; ++j )
      {
          _I(i,j)[0] = table[_I(i,j)[0]];
          _I(i,j)[1] = table[_I(i,j)[1]];
          _I(i,j)[2] = table[_I(i,j)[2]];
   }
I = _I;
291.56
LUT
    LUT(I, lookUpTable, J);
5.72
Note :
  1. C Pointer 的作法只是做為一個參考, 以 C 程式指令來說, 應該已經是最快的, 這裡主要是用來作為對比.
  2. C [] 所耗費的時間略多, 主要應該是用到索引定址的關係. 因為它要先做計數器累加.
  3. OpenCV 的 LUT() 函式耗費時間較多. 通用型的函式, 一般來說, 都會比特用型的函式, 來得慢一些, 這樣的時間, 是滿不錯的了.
  4. iterator 的存取方式的耗時, 非常的不好, 是 30~40 倍的慢. 顯然這種存取方式, 並不適合用來做 LookUpTable 這種對大量記憶體做簡單運算的動作. 它在存取每一個點的 overhead 太大, 需要做 iterator 前進到下一點的動作, 對每一點的存取也需要做多次的計數器及取址.
  5. Random Access, 用 Random Access 的方法, 來做 Sequential Access, 就好像拿拖把掃地, 這邊也是作為參考.

2017年2月12日 星期日

1.5 ms 解出數獨的簡易程式解法

 

朋友傳了一個數獨的題目給我, 這應該算是一個相當難的題目. 原題如下 :

6           4   5
    8 2          
                 
          6   1  
4   7            
      1       2  
    6       7 8  
        5 4      
      9          
 

很明顯的,超多的空格, 意味著會有一些需要猜測. 所以這一題會很難. 我不想花時間去手動解, 又很好奇, 空格這麼多, 是不是會有多數解 ? 所以, 我們會需要一個程式, 可以跑出所有的解法.

數獨的解法, 曾經是一個流行的題目. 主要的關鍵字, EXACT COVER Problem, DLX, Dancing Link, Algorithm X, 都可以找到許多有用的資料. 和這些解法來比, 這邊提出來的解法, 只能說是簡單,直覺,一個普通人的解法, 有興趣的可以參考.

一個直覺的解法

數獨的解決過程, 可以分成推論和猜測以及判斷三個部分. 以下簡單的說明:

推論:

根據數獨的規則, 我們要對每一格, 所對應到的列, 行, 區塊, 計算它的可以填入數字的集合. 而這個格子的可填入數字, 就是行,列,區塊, 的交集. 譬如說, 第 7 列, 第 4 行, 這一格, 所對應到的 第 7 列, 第 4 行, 第 8 個區塊, 他們的可填入數字, 分別是 第 7 列 { 1, 2, 3, 4, 5, 9}, 第 4 行 { 3, 4, 5, 6, 7, 8 }, 第 8 區塊 {  2, 3, 6, 7, 8 }, 這幾個集合的交集是 { 3 } , 所以 S[7][4] 這一格的答案是 3.

猜測:
6           4   5
    8 2          
                 
          6   1  
4   7            
      1       2  
    6 3     7 8  
        5 4      
      9          

 

這一個階段, 已經沒有前述可以推論得知的答案, 考量 S[1][4], 第 1 , 第 1 列的可行解是 { 1, 2, 3, 7, 8, 9 }, 第 4 行的可行解是 { 4, 5, 6, 7, 8 }, 第 2 區塊的可行解是 { 1, 3, 4, 5, 6, 7, 8, 9}, 所以 S[1][4] 這一格的可行解是 { 7, 8 }, 我們暫時沒有線索可以判斷應該是 7 還是 8. 所以這時就需要去猜測. 猜測的方式是把題目分成兩個子題. 既然是猜測, 就有可能是錯誤, 所以會需要判斷.

判斷:
6     7     4 3 5
    8 2       7  
      4          
      8   6   1  
4   7 5       6  
      1       2  
    6 3     7 8  
      6 5 4   9  
      9          

 

考量 S[3][8] 這一格, 第 3 列的可行解是 { 1, 2, 3, 5, 6, 7, 8, 9}, 第 8 行的 可行解是 { 4, 5}, 第 3 區塊的可行解是 { 1, 2, 6, 8, 9}, S[3][8] 這一格的可行解是沒有. 原因是我們在前面的猜測錯誤, 這一個子題不存在解決方案.

一般的數獨的解決過程, 就是重複推論, 猜測, 判斷的過程. 也是最直覺的解法.

資料表示 :

從以上的這三個部份, 我們可以發現, 解題的過程, 就是在列, 行, 區塊的可行解的操作運算, 為了避免重複無意義的計算, 我們要把這些資訊記錄下來. Row/Column/Block 分別用來表示列, 行, 區塊的可行解. 由於可行解是 1-9 的數字集合, 並且只有存在及不存在兩種狀況, 因此用一個 unsigned short.  另外每一格的解決進度, 也會記錄下來.  接下來說明如何用這個資料結構來進行操作.

typedef struct tagSUDOKU_BOARD
{
    char Pattern[CELL_COUNT + 1];                       // For displaying
    unsigned short CellSet[CELL_COUNT];              // For Cell status tracking

    unsigned short RowSet[DIGIT_COUNT];             // Row possible set
    unsigned short ColumnSet[DIGIT_COUNT];        // Column possible set
    unsigned short BlockSet[DIGIT_COUNT];           // Block possible set
}    SUDOKU_BOARD, *LPSUDOKU_BOARD;

啟始動作 :
  1. 預期的輸入, 是一個 81 個字元的字串, 所以要先把字串轉換成前述的資料表示. 檢查相對應字串的位置, 1~9 的數字代表這個位置已指定, 否則即未指定, 是要解決的部分.
  2. 對每一列, 每一行, 每一區塊, 計算它的可行解.
推論動作 :
  1. 對每一個格子, 計算它的可行解. 計算方式是對前述列, 行, 區塊, 作位元 AND 運算.
  2. 運算結果計算它的位元為 1 的數量. 程式中以查表得出. 數量為 1 , 即為得出的答案.
  3. 更新格子的字元表示, 可行解設定為 0. 代表已解.
  4. 更新此格子所對應到的列, 行, 區塊可行解.
猜測動作 :
  1. 對無法找出推論的子題, 進行猜測.
  2. 找出具有最小可行解的格子.
  3. 分別指定此格為可行解的其中之一
  4. 更新此格子所對應到的列, 行, 區塊可行解.
  5. 將此區塊加入待解問題列.
判斷動作 :
  1. 對每一個格子, 計算它的可行解. 計算方式是對前述列, 行, 區塊, 作位元 AND 運算.
  2. 如果任一未指定格子, 它的可行解為空, 那麼這一個子題就不存在解法.

效率 :

  1. 數獨的解出速度, 會大幅度的和題目相關, 這一個題目頗為複雜, 解出的速度應該很有代表性. 一般的題目所需消耗時間不會高於這個時間.
  2. 在 NoteBook 上實測, 可以達到 1.54 ms 解完的速度.

下載程式:

    https://github.com/lxnick/SudokuSolver

2017年1月23日 星期一

[VC] Multi-Threading data transfer 框架

 

Multi-Threading 現在已經是一種普遍應用的技術, 但是對初次接觸的人來說, 並不是那麼容易上手.手上有一個測試程式, 用這個例子做個說明, 並且提供一個完整框架.

這個例子是透過 USB, 取得一個 Frame (畫面), 做一些計算處理, 同時顯示到螢幕上. 所謂的同時, 是說計算處理並不和顯示有直接相關. 計算主要是計算它的解像力, Lens Shading, 這些計算比較耗時, 結果並不需要更新到畫面. 顯示的目的只是提供操作人員可以看到畫面.

由這樣的應用目的, 可以導出如下的需求:

  • 盡力收取影像, 提高計算部分的 frame rate –> 效率考量.
  • 計算或顯示如果運算來不及, 可以放棄目前收進來的  frame.
  • frame 必須完整. 計算完成前, 影像資料不能被更動.

為了因應這三個不同的工作內容( 取得影像, 計算, 顯示), 程式部分區分成 3 個 thread.

  • Master Thread, 身兼管理其他 thread 的任務. 同步機制, 分配記憶體, 取得影像, 發送影像. 以及結束後收拾殘局.
  • Computing Thread. 默默地計算影像資料.
  • Display Thread. 更新影像到螢幕上.

Computing Thread  及 Display Thread 在這個例子中是一樣的. 程式的流程簡單說明如下

  • CMasterThread ( 封裝 MasterThread . 及相關變數)
  • CMasterThread::Create() 嘗試建立 CMasterThread 物件, 並啟動 CMasterThread 的 ThreadFunction
  • CMasterThread::ThreadFunction() 為 ComputingThread, DisplayThread 配置同步物件, CriticalSection, Event, Image Buffer.
  • CMasterThread::ThreadFunction() 建立 Shutdown 作為通知所有 Thread 系統即將關閉的同步機制.
  • CMasterThread::ThreadFunction() 啟動 Display Thread
  • CMasterThread::ThreadFunction() 啟動 Computing Thread
  • -- 此時 Display Thread, Computing Thread 雖已啟動, 但會等不到資料 ready 的 event,  而進入等待狀態
  • CMasterThread::ThreadFunction() 配置接收記憶體
  • -- 此時 CMasterThread::ThreadFunction() 會等待 bStart 的旗標. 此旗標會由外部透過 CMasterThread::Start() 設定. 這樣的做法是可以讓外部準確的控制起跑時間.
  • 起跑之後, CMasterThread::ThreadFunction() 進入迴圈
    • bStop 旗標, 此旗標由外部控制是否繼續執行. 若不繼續執行, 則進行停止的程序.
    • 首先, 先取得一個 Frame
    • 以 TryEnterCriticalSection 嘗試鎖定 Computing 的 Buffer, 如果可以鎖定, 表示 Computing Thread 沒有在使用中, 所以可以更新資料. 如果不能鎖定, 表示 Computing Thread 在使用中. 放棄這次的更新.
    • 設定 Computing Thread 的 data available event. Computing Thread 會因而喚起, 開始處理.
    • 同樣的嘗試更新 Display Thread
  • 開始關閉程序
  • 設定 Shutdown event. Computing Thread 及 Display Thread 收到後會結束執行.
  • 等到 Computing Thread 及 Display Thread 都結束.
  • 關閉同步物件, 移除配置的記憶體.
  • 由外部呼叫 Clean() 釋放 CMasterThread 物件

** 注意, 程式中沒有資料處理, 而是以隨機的 Sleep() 來產生不同的時間差, 以測試程式的穩定性.

** Source code 的 plug-in 好像壞掉了. 可以由此取得完整專案 source code https://github.com/lxnick/ThreadTest

 

程式列表 :

MasterThread.h

[sourcecode language='cpp' ]
#include <process.h>
#include <windows.h>  
#include <string>

#define BUFFER_SIZE				(16 * 1024 * 1024 )

typedef struct tagTHREAD_INFO
{
	std::string szName;
	HANDLE		hHandle;
	DWORD		dwID;
	CRITICAL_SECTION	CriticalSection;
	HANDLE				hDataAvailable;

	unsigned long	nFrameIndex;
	unsigned char * pBuffer;
}	THREAD_INFO, *LPTHREAD_INFO;

class CMasterThread
{
private:
	CMasterThread();
	~CMasterThread();

	HANDLE hThread;	
	unsigned threadID; 

public:
	static CMasterThread* Create();
	static void Clean();

	static unsigned __stdcall ThreadFunction(void* pArguments);

	void Start();
	void Stop();

	BOOL bStart;
	BOOL bStop;

	HANDLE	hShutdown;

	THREAD_INFO	DisplayInfo;
	THREAD_INFO	ComputingInfo;
};
[/sourcecode]

MasterThread.cpp

[sourcecode language='cpp'  gutter='false']

#include "stdafx.h"
#include "MasterThread.h"

#define	WAIT_FRAME_TIME ( 100 )
#define	COMPUTING_TIME ( 50 )
#define	DISPLAY_TIME ( 50 )

const static char EVENT_COMPUTING[] = "ComputingDataAvailable";
const static char EVENT_DISPLAY[] = "DisplayDataAvailable";

static CMasterThread* pMasterThread = NULL;

unsigned __stdcall DisplayThreadFunction( void* pArguments )  
{
	LPTHREAD_INFO pInfo = (LPTHREAD_INFO) pArguments;

    printf( "\tDisplay Thread Launch ...\n" );  
	HANDLE hDataEvent = OpenEvent(EVENT_ALL_ACCESS, false, EVENT_DISPLAY);

	while( TRUE )
	{
		HANDLE Wait[] = { pMasterThread->hShutdown, pInfo->hDataAvailable };
		DWORD dwWait = WaitForMultipleObjects(sizeof(Wait)/sizeof(HANDLE), Wait, FALSE, INFINITE);

		if (dwWait != (WAIT_OBJECT_0 + 1))
		{
			printf("\tDisplay Thread Exit ...\n");
			return 0;
		}

		if ( TryEnterCriticalSection( & pInfo->CriticalSection ) != 0)
		{
			Sleep( DISPLAY_TIME );
			printf( "\tDisplay Thread Frame %d\n", pInfo->nFrameIndex);

			if (pInfo->pBuffer[0] != (pInfo->nFrameIndex & 0xFF))
				printf("\tDisplay Data dismatch\n");

			int random_number = rand() % DISPLAY_TIME;
			printf("\tDisplay Time = %d\n", random_number);
			Sleep(random_number);

			if (pInfo->pBuffer[0] != (pInfo->nFrameIndex & 0xFF))
				printf("\tDisplay Data overwritten\n");

			LeaveCriticalSection ( &pInfo->CriticalSection );
			ResetEvent(hDataEvent);
		}
	}

   printf( "\tDisplay Thread Shutdown ...\n" );  
}

unsigned __stdcall ComputingThreadFunction( void* pArguments )  
{
	LPTHREAD_INFO pInfo = (LPTHREAD_INFO) pArguments;

    printf( "\tComputing Thread Launch ...\n" );  
	HANDLE hDataEvent = OpenEvent(EVENT_ALL_ACCESS, false, EVENT_COMPUTING);

	while( TRUE )
	{
		HANDLE Wait[] = { pMasterThread->hShutdown, hDataEvent };
		DWORD dwWait = WaitForMultipleObjects(sizeof(Wait)/sizeof(HANDLE), Wait, FALSE, INFINITE);

		if ( dwWait != ( WAIT_OBJECT_0 +1))
		{
			printf("\tComputing Thread Exit ...\n");
			return 0;
		}

		if ( TryEnterCriticalSection( & pInfo->CriticalSection ) != 0)
		{
			printf("\tComputing Thread Frame %d\n", pInfo->nFrameIndex);

			if (pInfo->pBuffer[0] != (pInfo->nFrameIndex & 0xFF) )
				printf("\tComputing Data dismatch\n");

			int random_number = rand() % COMPUTING_TIME;
			printf("\tComputing Time = %d\n", random_number);
			Sleep(random_number);

			if (pInfo->pBuffer[0] != (pInfo->nFrameIndex & 0xFF))
				printf("\tComputing Data overwritten\n");

			LeaveCriticalSection(&pInfo->CriticalSection);
			ResetEvent(hDataEvent);
		}
	}
   printf( "\tComputing Thread Shutdown ...\n" ); 
}


unsigned __stdcall CMasterThread::ThreadFunction( void* pArguments )
{  
    printf( "Master Thread Launch ...\n" );  

	memset( &pMasterThread->DisplayInfo, 0, sizeof ( pMasterThread->DisplayInfo));
	pMasterThread->DisplayInfo.szName = "Display";
	InitializeCriticalSection( & pMasterThread->DisplayInfo.CriticalSection );
	pMasterThread->DisplayInfo.hDataAvailable = CreateEvent( NULL, TRUE, FALSE, EVENT_DISPLAY);
	pMasterThread->DisplayInfo.pBuffer = new unsigned char [ BUFFER_SIZE ];

	memset( &pMasterThread->ComputingInfo, 0, sizeof ( pMasterThread->ComputingInfo));
	pMasterThread->ComputingInfo.szName = "Display";
	InitializeCriticalSection( & pMasterThread->ComputingInfo.CriticalSection );
	pMasterThread->ComputingInfo.hDataAvailable = CreateEvent( NULL, TRUE, FALSE, EVENT_COMPUTING);
	pMasterThread->ComputingInfo.pBuffer = new unsigned char [ BUFFER_SIZE ];

	pMasterThread->hShutdown = CreateEvent( NULL, TRUE, FALSE, "MasterShutdown" );

	pMasterThread->DisplayInfo.hHandle =  (HANDLE)_beginthreadex( 
		NULL, 
		0, 
		&DisplayThreadFunction, 
		&pMasterThread->DisplayInfo, 
		0, 
		NULL );  

 	pMasterThread->ComputingInfo.hHandle =  (HANDLE)_beginthreadex( 
		NULL, 
		0, 
		&ComputingThreadFunction, 
		&pMasterThread->ComputingInfo, 
		0, 
		NULL );   

	unsigned char * pFrameBuffer = new unsigned char [ BUFFER_SIZE ];
	int FrameIndex = 0;

	while( ! pMasterThread->bStart)
		Sleep( 100 );

	while( ! pMasterThread->bStop )
	{
		int random_number = rand() % WAIT_FRAME_TIME;
		printf("Master Wait Frame = %d\n", random_number);
		Sleep(random_number);

		FrameIndex ++;
		printf( "Master Frame Index = %d\n", FrameIndex );  

		int FramePattern = FrameIndex & 0xFF;
		pFrameBuffer[0] = (unsigned char) FramePattern;
//		memset(pFrameBuffer, FramePattern, sizeof(unsigned char) * BUFFER_SIZE);

		if ( TryEnterCriticalSection( & pMasterThread->ComputingInfo.CriticalSection ) != 0)
		{
//			printf("Master Send to Computing = %d\n", FrameIndex);
			memcpy( pMasterThread->ComputingInfo.pBuffer, pFrameBuffer, sizeof( unsigned char ) * BUFFER_SIZE );
			pMasterThread->ComputingInfo.nFrameIndex = FrameIndex;
			LeaveCriticalSection ( & pMasterThread->ComputingInfo.CriticalSection );
			SetEvent( pMasterThread->ComputingInfo.hDataAvailable );
		}

		if ( TryEnterCriticalSection( & pMasterThread->DisplayInfo.CriticalSection ) != 0)
		{
//			printf("Master Send to Display = %d\n", FrameIndex);
			memcpy( pMasterThread->DisplayInfo.pBuffer, pFrameBuffer, sizeof( unsigned char ) * BUFFER_SIZE );
			pMasterThread->DisplayInfo.nFrameIndex = FrameIndex;
			LeaveCriticalSection ( & pMasterThread->DisplayInfo.CriticalSection );
			SetEvent( pMasterThread->DisplayInfo.hDataAvailable );
		}
	}

	printf( "Master Thread End Run\n");  
	SetEvent( pMasterThread->hShutdown );
	HANDLE hWaitThread[] = { pMasterThread->DisplayInfo.hHandle, pMasterThread->ComputingInfo.hHandle };
	WaitForMultipleObjects( sizeof(hWaitThread)/sizeof(HANDLE), hWaitThread, TRUE, INFINITE );
	printf("Child Thread Exit\n");

	CloseHandle( pMasterThread->hShutdown );
	CloseHandle( pMasterThread->ComputingInfo.hDataAvailable  );
	DeleteCriticalSection( & pMasterThread->ComputingInfo.CriticalSection );
	delete [] pMasterThread->ComputingInfo.pBuffer;
		 

	CloseHandle( pMasterThread->DisplayInfo.hDataAvailable );
	DeleteCriticalSection( & pMasterThread->DisplayInfo.CriticalSection );
	delete [] pMasterThread->DisplayInfo.pBuffer;

	ResetEvent(pMasterThread->hShutdown);
	CloseHandle(  pMasterThread->hShutdown);
	delete [] pFrameBuffer;

    return 0;  
}  



CMasterThread::CMasterThread()
{

}

CMasterThread::~CMasterThread()
{

}

CMasterThread* CMasterThread::Create()
{
	if ( pMasterThread != NULL)
		return pMasterThread;

	pMasterThread = new CMasterThread;
	pMasterThread->bStart = FALSE;
	pMasterThread->bStop = FALSE;

	pMasterThread->hThread =  (HANDLE)_beginthreadex( NULL, 0, &ThreadFunction, NULL, 0, &pMasterThread->threadID );

	return pMasterThread;
}

void CMasterThread::Clean()
{
	if ( pMasterThread != NULL)
		delete pMasterThread;

	pMasterThread = NULL;
}

void CMasterThread::Start()
{
	pMasterThread->bStart = TRUE;
}

void CMasterThread::Stop()
{
	pMasterThread->bStop = TRUE;
	WaitForSingleObject( pMasterThread->hThread, INFINITE );

	CloseHandle(pMasterThread->hThread);
}
[/sourcecode]

ThreadTest.cpp

[sourcecode language='cpp' ]
#include "stdafx.h"
#include <stdlib.h>  
#include <stdio.h>  
#include <time.h>  

#include "MasterThread.h"

int _tmain(int argc, _TCHAR* argv[])
{
	for (int i = 0; i < 100; i++)
	{
		srand((unsigned)time(NULL));

		CMasterThread * pMasterThread = CMasterThread::Create();
		Sleep(100);

		pMasterThread->Start();
		Sleep(1000 * 5);
		pMasterThread->Stop();

		CMasterThread::Clean();
	}

	return 0;
}
[/sourcecode]