OpenCV 3.2.0 已經在 2016/12/23 公開發行. 除了一些問題的修正外, 這一個版本的 Windows 版本是在 VC14, 也就是 Visual Studio 2015. 剛好接下來的工作也議定在 Visual Studio 2015 Community 上開發, 於是就選定用這個版本. 這個版本的使用上, 遇到的一些問題, 做個紀錄.
2017年5月16日 星期二
[OpenCV] 3.2.0 實裝心得
OpenCV 3.2.0 已經在 2016/12/23 公開發行. 除了一些問題的修正外, 這一個版本的 Windows 版本是在 VC14, 也就是 Visual Studio 2015. 剛好接下來的工作也議定在 Visual Studio 2015 Community 上開發, 於是就選定用這個版本. 這個版本的使用上, 遇到的一些問題, 做個紀錄.
2017年4月10日 星期一
[VideoInput] Download
Download VideoInput Libray from GitHub
https://github.com/ofTheo/videoInput to D://VideoInputExtract
Synchronize project setting
Modification for Visual Studio 2015
VideoInput.cpp Line 821 aschar * name = (char*) videoInput::getDeviceName(i);
2017年4月9日 星期日
[OpenCV] Using OpenCV static library in MFC application
Create MFC Application
Add Picture Control
Add Initial codes in OnInitDialog
// TODO: Add extra initialization hereCRect 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
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/Configuration Properties/C/C++/Code Generation
Character Set –> Use Multi-Byte Character Set
Runtime Library –> Multi-thread (/MT)/Configuration Properties/VC++ Directories
Security Check –> Disable Security Check (/GS-)
Include Directories: D:\OpenCV-3.2.0\opencv\build\include/Configuration Properties/Linker/Input
Library Directires
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
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\includeAdd OpenCV library path
D:\OpenCV-3.2.0\opencv\build\x86-static\lib\ReleaseD:\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.libuser32.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
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.0![]()
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, 名稱就是他的意思.
3. 按下 “configure” 開始設定, generator 選擇 VS 2015![]()
4. 等待 configuration 結束
5. 清除選項中的 BUILD_SHARED_LIBS, 以編譯靜態連結版本
6. 按下 “generate” 開始產生 Visual Studio 2015 的專案檔案
7. 等待 …
用 Visual Studio 2015 編譯 OpenCV Library
1. 執行 VS2015, 並開啟 “OpenCV” solution file
2. Build Solution
這個 solution 中, 包含了 66 個專案, 所以要 build 一段時間, build 完會產生如下的檔案
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 |
不同 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 |
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 :
- C Pointer 的作法只是做為一個參考, 以 C 程式指令來說, 應該已經是最快的, 這裡主要是用來作為對比.
- C [] 所耗費的時間略多, 主要應該是用到索引定址的關係. 因為它要先做計數器累加.
- OpenCV 的 LUT() 函式耗費時間較多. 通用型的函式, 一般來說, 都會比特用型的函式, 來得慢一些, 這樣的時間, 是滿不錯的了.
- iterator 的存取方式的耗時, 非常的不好, 是 30~40 倍的慢. 顯然這種存取方式, 並不適合用來做 LookUpTable 這種對大量記憶體做簡單運算的動作. 它在存取每一個點的 overhead 太大, 需要做 iterator 前進到下一點的動作, 對每一點的存取也需要做多次的計數器及取址.
- 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;
啟始動作 :
- 預期的輸入, 是一個 81 個字元的字串, 所以要先把字串轉換成前述的資料表示. 檢查相對應字串的位置, 1~9 的數字代表這個位置已指定, 否則即未指定, 是要解決的部分.
- 對每一列, 每一行, 每一區塊, 計算它的可行解.
推論動作 :
- 對每一個格子, 計算它的可行解. 計算方式是對前述列, 行, 區塊, 作位元 AND 運算.
- 運算結果計算它的位元為 1 的數量. 程式中以查表得出. 數量為 1 , 即為得出的答案.
- 更新格子的字元表示, 可行解設定為 0. 代表已解.
- 更新此格子所對應到的列, 行, 區塊可行解.
猜測動作 :
- 對無法找出推論的子題, 進行猜測.
- 找出具有最小可行解的格子.
- 分別指定此格為可行解的其中之一
- 更新此格子所對應到的列, 行, 區塊可行解.
- 將此區塊加入待解問題列.
判斷動作 :
- 對每一個格子, 計算它的可行解. 計算方式是對前述列, 行, 區塊, 作位元 AND 運算.
- 如果任一未指定格子, 它的可行解為空, 那麼這一個子題就不存在解法.
效率 :
- 數獨的解出速度, 會大幅度的和題目相關, 這一個題目頗為複雜, 解出的速度應該很有代表性. 一般的題目所需消耗時間不會高於這個時間.
- 在 NoteBook 上實測, 可以達到 1.54 ms 解完的速度.
下載程式:
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]
2016年10月8日 星期六
[期貨] 台指期貨數據計算
以下整理台指的保證金及期交稅, 手續費
1. 保證金:
保證金分原始保證金及維持保證金.
原始保證金 83,000
維持保證金 64,000
2. 期交稅:
期交稅 37, 但是買賣都會收
3. 手續費:
手續費 100, 也是買賣都會收
4. 漲跌幅
指數 1 點的差額算 200 元.
5. 實例
10/7 日, 台指 12 月期. 開盤 9194, 最高 9202, 最低 9178, 收盤 9187.
以操作一口計 需要 83, 000 + 64, 000 = 147,000
如果在開盤買進, 然後在最高點平倉
獲利 (9202 – 9194 ) * 200 – 37*2 – 100 * 2 = 1,326
+ 0.9%
如果在開盤賣掉, 然後在最低點平倉
獲利 (9194 - 9178 ) * 200 – 37*2 – 100 * 2 = 2,953
+ 2.0 %
很有趣的結果. 參考參考.
[亂亂講] 四不總統 VS 六不總督
http://www.nownews.com/n/2016/10/07/2263684
總統蔡英文6日接受日本讀賣新聞專訪,這是她3天內第2度接受外媒專訪,主軸仍是兩岸關係。蔡英文繼續強調「4不」:承諾不變、善意不變、不屈服壓力,不走對抗老路.
這很有趣. 不由得想起英法戰爭時的兩廣總督葉名琛,面對英法聯軍兵臨城下, “不戰, 不和, 不守, 不死, 不降, 不走”. 最終被俘, 死在印度.
奈何.
2016年10月5日 星期三
[Util] 使用 Visual Studio 2015 下載 GitHub 的版本
1. 下載 repo
從 Visual Studio 的 File 選單, 選擇 File/New/Repository.
在 Visual Studio 的 Team Explorer 中, Local Repository 分頁, 選擇 Clone
填入 Git 的 URL, 例如 https://github.com/lxnick/Util.git, 此時 Local Repository 的路徑也已經做了相對的變化.
然後按下 Clone. 檔案就複製到 local.
2. 在 Visual Studio 中, 打開下載的專案.
先打開 Visual Studio 的 Team 分頁
選擇要打開的專案. 例如 Util. 在專案名稱上面按右鍵, 然後選擇 Open.
在 Solution 的選單下方, 選擇 Open. 在彈出的視窗中, 選擇 Util.sln. 回到 Solution Explorer 畫面, 專案已經打開.
[Util] 使用 Visual Studio 2015 的 git extension 連接 GitHub
介紹如何在 Visual Studio Community 2015 的 IDE 中, 連接上 GitHub. 現在 Visual Studio Community 2015 是免費的.
1. 首先在 GitHub 上建立一個 repository(Demo)
HTTPS 後面的, 就是可以存取到這個 repository 的網路位置.
2. 在本機端建立一個 github 的目錄.
3. 設定 Visual Studio 的 GitHub
在 Team Explorer 的分頁, 按下 Home 按紐, 以切換到 Home 分頁
選擇 Settings. 再選擇 Settings 下的 Global Settings. 填入 User name, email address, Default Repository Location 填入先前給定的路徑. 最後按下 Update 更新.
4. 在 Visual Studio 建立一個新的專案, Location 指向 先前給定的路徑.
完成後儲存專案.
專案檔案已生成在路徑中.
5. 將專案加入 Source Control
在 Solution Explorer 的分頁中. 在 Solution 名稱上按右鍵, 在彈出功能選單中, 選取 Add Solution to Source Control.
然後切換到 Team Explorer 畫面. 可以看到專案已被加入 local repository.
把 Enter a comment message 改掉 (譬如 Initial Version), Commit All 的顏色才會變成可以按下的狀態.
然後就 Commit All. 把變更加入 local repository.
6. 同步到 GitHub
在 Changes 分頁, 按下 Sync (藍色有底線的字 ). 切換到 Publish 頁面.
這個頁面有 3 個項目:
1. Publish to Visual Studio Team Services ( 這是 Microsoft 的 Team Server)
2. Publish to GitHub (這是我另外安裝的 Git extension for Visual Studio, 也可以用, 但不是這邊的重點)
3. Publish to Remote Repository (這是以下要說明的方法)
展開 Publish to Remote Repository, 在 “Enter the URL of an empty Git repo” , 代換成 GitHub 的 URL, 例如
“https://github.com/lxnick/Demo.git” 然後按下 Publish.
7. 完成. 回到 GitHub 的網頁. 檔案資料已經上傳.
2015年12月12日 星期六
滅頂還是滅自己
由於頂新在法院的一審獲判無罪, 激起許多抵制頂新的行動. 但是, 所謂的秒退這種方法, 我覺得是不可取的.
第一, 破壞人與人之間的基本誠信. 你本來就沒有要買, 假意購買之後, 再退貨, 不管合不合法, 本身就是一種虛偽的行為.
第二, 破壞零售商對消費者的誠信及服務. 零售商訂定無條件退款條款, 是保障消費者的權益. 利用這種條款去造成供應商的損失, 非常不道德.
第三, 要不要抵制頂新, 是每一個人自我的決定. 因為這種紊亂交易體系的行為, 影響到其他人的購買權益. 憑什你可以代替其他人做抵制的決定 ? 這樣的人, 有民主素養, 尊重他人嗎 ?
對不道德的企業, 用不道德的方法反制, 只是讓整個社會陷入不道德的惡性循環.
2015年12月6日 星期日
我看侯孝賢的聶隱娘
我很好奇侯導會怎麼處理聶隱娘這個故事. 這個故事, 很短, 可以說, 只是聶隱娘一生的節錄. 就故事來說, 只是有骨頭, 沒有血肉, 要怎麼去填充這一部份 ? 所以就去看了這部電影.
電影還滿好看的, 取景, 攝影, 服裝, 布景, 都很好看. 唯一的缺點就是沒有打出來在那裡取的景. 如果想要到當地旅遊, 會有點困擾.
劇情嗎 ? 沒有這個東西. 如果要看劇情, 只要看前面 1 分 30 秒, 師父要聶隱娘去刺殺聶隱娘的表哥, 也就是師父的外甥. 然後可以直接跳到後面 1 分 30 秒, 聶隱娘說下不了手, 兩個人小打一架, 師父打輸了. 中間的部份, 都跳過去, 也不影響結局, 所以省略之. 可惜的是, 連兩大高手打架, 也看不出來高手過招的感覺.
其實呢, 從前面 1 分 30 秒, 我就覺得很失望. 師父說, "某某人, 殺父弒兄, 罪大惡極. 為我刺之, 白晝之中, 取人首級, 如刺飛鳥般容易”(全文我沒有記很清楚). 這很怪. 明明是白天的現場, 為什麼要多說白晝之中, 現場又沒有瞎子. 刺殺就刺殺, 為什麼要多說如刺飛鳥般容易, 還沒出手, 哪知容不容易, 還要像刺飛鳥般容易. 如果我收到這種命令, 我也會覺得很茫然, 你到底要我幹嘛? 之所以會有這麼怪的對白, 就是把人稱搞錯了, 這些語句是第三人稱, 也就是看到的人, 記錄這件事情, 而不是出自師父口中的擊殺命令. 連這個都錯了, 你還能有什麼期待呢 ?
另外, 近距離作戰, 請不要用那麼長的箭. 那麼長的箭, 你要多大的弓, 多少時間來拉弓 ?
2015年11月4日 星期三
[歌詞翻譯] Deep Purple, Soilder of fortune
除了歌曲本身以外, 這首歌對我來說, 有特別的意義.
YouTube 的連結, 歌曲也很好聽, 不過不要在夜深人靜的時候聽, 那種悲傷的氛圍, 很凍.
歌詞原文
I have often told you stories about the way
I lived the life of a drifter waiting for the day
When I'd take your hand and sing you songs
Then maybe you would say
Come lay with me and love me
And I would surely stay
But I feel I'm growing older
And the songs that I have sung echo in the distance
Like the sound of a windmill going round
Guess I'll always be a soldier of fortune.
Many times I've been a traveler I looked for something new
In days of old when nights were cold I wandered without you
But those days I thought my eyes had seen you standing near
Though blindness is confusing it shows that you're not here.
Now I feel I'm going older
And the songs that I have sung echo in the distance
Like the sound of a windmill going round
Guess I'll always be a soldier of fortune
Yes, I can hear the sound of a windmill going round
I guess I'll always be a soldier of fortune.
I guess I'll always be a soldier of fortune.
歌詞翻譯
我常常跟你提起那一段我流浪著的故事, 直到那一天,我牽著你的手,為你唱歌. 也許你會說,留下來,愛我 。然後,我就會真的留下來。 但是,我感覺我正在老去, 而我唱過的歌,也只剩下遙遠的回音, 就像是迴盪在磨坊的低語, 我只能永遠當個傭兵戰士。
好幾次,我到處旅行,尋找新奇的事物。在古老的年代,冰冷的夜裡,我獨自漫遊。但是在那些日子裡,你的身影卻一直在我眼中,我盲目而且困惑,顯然你並不在這裡。現在,我覺得我正在老去,而我唱過的歌,也只剩下遙遠的回音, 就像是迴盪在磨坊的低語。我想,我會永遠是個傭兵戰士。
是的, 我仍然可以聽到磨坊的低語. 我想, 我只能永遠當個傭兵戰士。我想,我會永遠是個傭兵戰士。
註:
因為你沒有說 “留下來,愛我", 所以我沒有留下來, 我只能繼續為了別人的目標戰鬥的傭兵人生.
2015年9月23日 星期三
[i.MX6] Memos
About i.MX6
最近拿到一塊板子 Boundary Device 出的 Nitrogen6X, 雖然也買了一塊 mirco sd card, 很容易的就開機起來, 但是如果要自己做一些客製化的動作, 就不是那麼簡單. 怎麼做也許不是最困難的事, 要做什麼可能才是最難的. 怎麼說呢 ? 首先, 是要做一個 Ubuntu 的 embedded linux, 或是 Android 的 platform ? 因應需求的不同, 就會用到不同的 tool chain, build system, …. 然後就是一大堆的資訊, 最後就是被淹沒在這些東西當中.
載浮載沉幾次之後, 終於搞懂了一些東西. 也許了解得沒有那麼透徹, 如果有謬誤或更正請告訴我, 免得把錯誤的資訊傳染其他人, 那就不好了, 有建議也可以跟我說喔 : )
i.MX6 Family
iMX6 是 Freescale 出的一個處理器的系列. 我用到的是 i.MX6Q, 是四核心的版本, 有關 iMX6 的家族的資料, 可以參考以下連結 i.MX6 Family.
i.MX6 built images
一塊 SBC ( single board computer), 如果不能安裝作業系統, 那真的也是沒什麼用. 以下是已經 built 好的作業系統映像檔 (意思就是說, 這個板子可以跑這樣的作業系統). 我這邊的資料是來自 Boundary Devices, 因為板子是向他們買的 : ) 原始資料來源, 可以參考 Boundary Devices built images.
Yocto Linux ( Yocto 是 Linux 基金會下, 提供嵌入式軟體開發的工具的專案)
Debian/Ubuntu
Buildroot ( Buildroot 是一套自動建立完整而且可開機的嵌入式 linux 的工具)
- Buildroot image with GStreamer-imx
- Buildroot image for SoloX MCU development
Android
Others
- Qt for Device Creation 5.4 by The Qt Company:
- QNX Momentics for Nitrogen6x, and BD-SL-i.MX6 (SABRE Lite)
- GuruCE Windows Embedded Compact 7 (WEC7) and WEC2013
- Microsoft Windows Embedded Compact 2013, (ONLY for BD-SL-i.MX6)
- Timesys demo
- Timesys images for Nitrogen6_Max
i.MX6 Toolchain
[i.MX6] Booting with image Ubuntu Trusty
這裡記錄 i.MX6 用預先 build 好的 image (Ubuntu Trusty for i.MX6 boards, June 2015 (kernel 3.10.53).
先在 http://boundarydevices.com/imx6-builds/ 這個網址, 找到 Debian/Ubuntu 的這一個類別, 再點選 Ubuntu Trusty for i.MX6 boards, June 2015 (kernel 3.10.53) 這個連結, 到 Ubuntu Trusty 的下載頁. 這邊有兩個版本, lxde ( Lightweight X11 Desktop Environment), 搭配 GUI 的版本. 另一個是 console (想當然耳, 就是只有 console 介面的版本). 選擇要下載的檔案後, 會先跳轉到軟體授權頁, 翻到最下面, 按 “I Agree”, 然後就會開始下載.
下載之後, 用以下命令, 寫入 micro sd card. 會要一些時間, 要耐心等候.
$ sudo umount /dev/sdb*
$ zcat 20150622-nitrogen-3.10.53_1.1.1_ga-trusty-en_US-lxde_armhf.img.gz | sudo dd of=/dev/sdb bs = 1M
$ sync
做完之後, 把 micro sd card 放進 card slot 之後開機. 就可以進入到登入畫面. 如果有掛螢幕, 也可以看到 nitrogen 的 ubuntu 登入畫面. 如果忘記插卡, 就會一直停留在 U-Boot 的畫面.
2015年9月22日 星期二
[Ubuntu] 操作小筆記
這是一些非常簡單的操作, 不過, 最近腦容量有點不夠, 還是把它記下來, 備忘.
遠端登入, FTP 連線
$ sudo apt-get install remote-login-service
$ sudo apt-get install openssh-server
拍攝螢幕快照
$ sudo apt-get install scrot
找到 SD 卡
在插入 SD 卡前, 下以下指令
$ sudo lshw
$ ls –al /dev/sd*
在插入 SD 卡後, 下以下指令
$ sudo lshw
$ ls –al /dev/sd*
比較差異即得
2015年9月9日 星期三
賭徒
朋友們都知道我不喜歡賭博. 不管是玩撲克牌或是打麻將, 甚至也不買樂透. 但是, 我確確實實是一個賭徒.
我舉一個例子, 我唸書的時候, 有一段時間常去電動玩具店, 玩賭博式的電玩, 那時還沒抓那麼兇. 有一段時間, 我站在小瑪莉機臺(就是那種有櫻桃, 柳丁, 西瓜, 的圖片, 然後一個紅點在那裡跑來跑去, 看停到那裡, 那個就是賠率)的前面觀察中獎的模式. 就是出大賠率之前, 它的落點和順序. 大約兩三個星期吧, 我觀察到一個特定的順序, 於是我開始等待, 等了幾天之後, 出現了那個順序, 剛好在玩的人也離開. 於是我連坐都沒有坐下來, 直接投錢, 押滿, 然後就錢一直滾下來. 那個機臺的最大賭注是 9, 最大賠率是 50, 就是大 BAR啦. 所以呢, 實得 9*50 – 9, 之後不只是那家電動玩具店, 甚至賭博性電玩我都沒再玩過.
我喜歡大賭注, 高賠率, 而且可以參與其中的 game.
最近遇到了一個這樣的 game. 也不知道算是主動, 或是被動的加入這個 game, 既然進來了, 而且, 這也觸動了我賭徒的靈魂, 我準備加入. 為了這個花了很多精神, 朝思暮想, 弄到有點憂鬱了.
第一個要考量的就是賭注. 你手上有多少籌碼? 如果還有時間, 你能或是你要再加多少籌碼? 你可以承受多少? 輸掉的有時候不是只有賭注, 籌碼. 當你在牌桌上看牌, 小孩在旁邊吵著要回家, 你已經輸掉了親情. 當你把精神都投入在這上面, 你已經輸掉了自己. 也許, 賭注根本就不只是金錢, 也許是 3 年的自由, 10 年的努力, 甚至是活下去的勇氣.
其次, 是賠率. 其實呢, 賠率是沒什麼意義的. 獎品和賭注可能根本不是同一類的東西, 那有賠率可言? 應該說, 贏得的獎品, 夠不夠吸引力 ? 這個吸引力, 能夠吸引你帶來多少籌碼. 也許是更大量的金錢, 也許是你朝思暮想的東西, 可是, 更多時候, 贏得的, 只是選擇未來的一把鑰匙.
I am in.