星期二, 5月 19, 2015

cornerSubPix

Camera calibration with OpenCV 有強調, 以 camera 即時取圖時,仍要有間隔,以取得不同的 image。因為相似的 image 會導致 similar equations,而 similar equations 在校正階段,會導致 ill-posed problem。文中建議, findChessboardCorerns 後,再用 cornerSubPix 再找 image points。

Similar position 導致的 similar equation 的破壞力究竟多大?我還不確定。本來以為只是會 over-fitting 某些視角。但目前只要相似圖,undistortion 的結果就很慘。而相近的那幾張圖也沒有特別好。

文中建議的 cornerSubPix 指出,我們所面對的 image points,不該用有限解析度的整數點位置,而是有小數點的真實位置。學習重點:
1) cornerSubPix 的使用
2) 逼近 "真實位置" 的原理
3) findChessboard 如果不用 fast mode,內部是否已使用 cornerSubPix ?


1) cornerSubPix 的使用
C++: void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteriacriteria)
Python: cv2.cornerSubPix(image, corners, winSize, zeroZone, criteria) → None

image: input image . opencv 2.4.9 仍是用 gray image

corners : 重要的兼 input 和 output。本例中將 findChessboard 得到的 image points 傳入,結束後得到修正結果。opencv 2.4.9 仍是用 vector. Point2d 馬上 throw error。

size: winSize。實際對每角點的搜尋區大小是 (winSize*2+1, winSize*2+1)。

zeroZone: 搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域

criteria: 停止條件。cornerSubPix 是遞迭式,可設最多幾回 criteia.maxCount 或角點的回合位移小於 criteria.epsilon,若回合內移動太小,沒必要再算就停止。

2) 逼近 "真實位置" 的原理
先說 gradient 與 edge 的關係。在理想的 edge 上,p 點的梯度,與自身的 edge 方向垂直。對在同 edge 上的 q,可寫成  G(p)^t  (q - p) = 0。其中 G(p) 是 p 之梯度。
回到本題,我們想找角點 q 落在 p 附近。對 p 附近的 pi 點,已知對應梯度 D Ipi。 (這裡困惑了,貌似 pi 都該在 edge 上,但圖中 p0 顯然不在 edge 上?),角點 q 也落在這些 edge 上,所以滿足  G(pi)^t  (q - pi) = 0。

命題為找角點 q 盡可能讓 pi 梯度能垂直所有的 (pi - q)

對 searching window,加總求 q 能使 epsilon i sum 最小 

find q min. sum DIpi^t  (q - pi)

 這邊應可直接用 least square 解 q。


不過文件說用 iterative 解,可能是我們大約知道 q 的位置。文件說法,讓下式為 0




3) findChessboard 如果不用 fast mode,內部是否已使用 cornerSubPix ?
是。trace cvFindChessboardCorners(),至少在 2.4.6 版,非 fast mode,就會進入 cvFindCornerSubPix()。



星期六, 5月 09, 2015

opecv:error C2668 "cv::write" ambiguous call to overloaded function

error C2668: "cv::write" : ambiguous call to overloaded function
VC2008 之 error C2668: "cv::write" : ambiguous call to overloaded function

改 size_t 後,opencv 常送出 ambiguous call 的 error message。
乍見這類錯誤很惶恐,發生在 OpenCV 提供的 template function,
不是自己能控制的,要如何訂正呢?還好從 output 可以看出端倪。

編譯過程中的 Output window, 找出 error C2668 的上下文
..operations.hpp(2911): error C2668: 'cv::write' : ambiguous call to overloaded function
..operations.hpp(2709): could be 'void cv::write(cv::FileStorage &,const std::string &,double)'
..operations.hpp(2708): or       'void cv::write(cv::FileStorage &,const std::string &,float)'
..operations.hpp(2707): or       'void cv::write(cv::FileStorage &,const std::string &,int)'
while trying to match the argument list '(cv::FileStorage, std::string, const size_t)'
.\ex_tracer.cpp(377) : see reference to function template instantiation 'cv::FileStorage &cv::operator <<(cv::FileStorage &,const _Tp &)' being compiled
with
[
    _Tp=size_t
]

引發 error 之 source code 是下段
FileStorage fs(fname, FileStorage::WRITE);
for (size_t i = 0; i < V.size(); ++i) {
  ..
  fs << "vid" << i; // => 出現 ERROR c2668
}
.............................................................................
原來是 for_loop 中使用 size_t i, 使得 fs 困惑了. 
這類  write::(fs, name, value),
fs 只吃 double, float, int, 不接受 size_t.

size_t 轉型為 int 即可.
fs << "vid" << (int) i;  // 正確,不再出現 ERROR c2668

MFC:在 Dialog 中使用 CToolBar 的 tooltip (vc++)

許久之前遇上一個問題 - MFC 的 Dialog 中, 為何 CToolBar 的 tooltip 無效? 

在網路上轉了半天, 盯著 MSDN 的 CBRS_TOOLTIPS 發呆, 
想說這個不就是會讓 tooltip 閃閃發亮的參數嗎? 
實際上, CBRS_TOOLTIPS 對 docuement/view 的 SDI/MDI 都有效, 
但 CDialog 沒有處理對應 message 的 message handler, 

還好網上早有高人解答, 只要增加 TTN_NEEDTEXTW, TTN_NEEDTEXTA 的 message handler.
為了紀念轉了四五個小時的搜尋, 不只引用, 也鄭重地貼上原文:
http://www.wangchao.net.cn/bbsdetail_748638.html

改編
CToolBar 一般用在 SDI 或是 MDI, 
如果我們在一個Dialog里新建了一個CToolBar,它的提示信息可能就沒有辦法出來了。
主要的原因是由於我們沒有為這個CToolBar寫提示信息的消息映射函數。 如果要增加的話,

0) 設定 tooltips 給 toolbar, 設定 toolbar button 之 prompt
1) 建立 message mapping
2) 建立 message handler
3) 加上 header

0.1 ToolBar 設定為有 tooltips
CToolBar mToolBar;
mToolBar.Create(this, AFX_DEFAULT_TOOLBAR_STYLE| CBRS_TOOLTIPS);
0.2 在 ToolBar button 的屬性 prompt, 加上對應之 tooltip
1. 建立消息映射
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText) 
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText) 
2. 增加 message handler OnToolTipText()
afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ); 
BOOL MyDlg::OnToolTipText(UINT nFlags, NMHDR* pNMHDR, LRESULT* pResult)
{
ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);
// if there is a top level routing frame then let it handle the message
if (GetRoutingFrame() != NULL) return FALSE;
// to be thorough we will need to handle UNICODE versions of the message also !!
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
TCHAR szFullText[512];
CString strTipText;
UINT nID = pNMHDR->idFrom;
if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
{
// idFrom is actually the HWND of the tool 
nID = ::GetDlgCtrlID((HWND)nID);
}
if (nID != 0) // will be zero on a separator
{
AfxLoadString(nID, szFullText);
strTipText=szFullText;
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
{
lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
}
else
{
_mbstowcsz(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
}
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
{
_wcstombsz(pTTTA->szText, strTipText,sizeof(pTTTA->szText));
}
else
{
lstrcpyn(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
}
#endif
*pResult = 0;
// bring the tooltip window above other popup windows
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
return TRUE;
}
return FALSE;
}
3.增加一個頭文件 // 不確定是否必要
  #include  

opencv:How to access a Mat with multiple channels ?

Mat.at(row, col) , 雖然每次都得從頭算出位址, 是效率不高的存取方式,
但頗好用. 只是, 非單一 channel 的 matrix 要如何存取呢?

下文 testM 是有三個 channel 的 8-bits unsign char matrix.
可依舊以 (row, col) 存取, 但最小單位是 Vec3b


        Mat testM(3,4,CV_8UC3);
for (int i = 0; i < testM.rows; i++)
{
for (int j = 0; j < testM.cols; j++)
{
Vec3b v = testM.at(i,j);
v[0] = i;
v[1] = i+10;
v[2] = i+20;
testM.at(i,j) = v;
}
}

當然也可直接存取 data, serial data 如下

row0= {ch0 ch1 ch2}, {ch0 ch1 ch2}, {ch0 ch1 c2}, {ch0 ch1 ch2}
row1= {ch0 ch1 ch2}, {ch0 ch1 ch2}, {ch0 ch1 c2}, {ch0 ch1 ch2}  
row2= {ch0 ch1 ch2}, {ch0 ch1 ch2}, {ch0 ch1 c2}, {ch0 ch1 ch2}      
for (int i = 0; i < testM.rows; i++)
{
uchar* pdata = testM.data + i*testM.step;
int k = 0;
for (int j = 0; j < testM.cols; j++, k+=3)
{
printf("r=%d,c=%d: %d,%d,%d\n", i, j, pdata[k],pdata[k+1],pdata[k+2]);
}
}

輸出:
r=0,c=0: 0,10,20
r=0,c=1: 0,10,20
r=0,c=2: 0,10,20
r=0,c=3: 0,10,20
r=1,c=0: 1,11,21
r=1,c=1: 1,11,21
r=1,c=2: 1,11,21
r=1,c=3: 1,11,21
r=2,c=0: 2,12,22
r=2,c=1: 2,12,22
r=2,c=2: 2,12,22
r=2,c=3: 2,12,22

CUDA:Can not find cutil32D.lib ...

Problem :  SDK (e.g. OpenCV, OptiX) 和本機 CUDA  32 bits or 64 bits 間選用了不同平台.

Solution 

step 1 :重新編譯適當平台的 cutil library.  位在 
C:\ProgramData\NVIDIA Corporation\NVIDIA GPU Computing SDK 4.0\CUDALibraries\common\ 

step 2 : 將不同平台產生的 cutil32D.lib/cutil64D.lib 加入 linker   
Linker -> General -> Additional Library Directories 

由於 step 1 不同平台的 library 會置於不同 path, 將有平台變數的 path

  
 $(NVSDKCOMPUTE_ROOT)\C\common\lib\$(PlatformName)
   $(NVSDKCOMPUTE_ROOT)\shared\lib\$(PlatformName)
加入 Linker -> General -> Additional Library Dire

p.s. 
 win7 找環境變數 …
電腦 -> 變更設定 -> 系統內容 -> 環境變數
NVSDKCOMPUTE_ROOT=C:\ProgramData\NVIDIA Corporation\NVIDIA GPU Computing SDK 4.1

Step 3 : 如果不幸地出現因 cutil 引起的 error lnk2005 … already define in LIBCMTD.lib
        [Debug/Win32]
        1.  C++ 改用 /MTd 編譯
                C/C++ -> Code Generation change the Runtime Library to/MTd        2.  忽略 LIBCMT.lib
                Linker->Input->Ignore Specific Library to  libcmt.lib
        3.   CUDA 的 Host code (CPU code) 也以  /MTd 編譯                        CUDA Runtime API -> Host -> Runtime Library => /MTd

                [Release/Win32]
                1.  C++ 改用 /MT 編譯
                        C/C++ -> Code Generation change the Runtime Library to /MT
                2.  忽略 LIBCMTD.lib
                        Linker->Input->Ignore Specific Library to  libcmtd.lib 
                3.   CUDA 的 Host code (CPU code) 也以  /MT 編譯                       CUDA Runtime API -> Host -> Runtime Library => /MTd 
             

        p.s
. 曾經同時忽略 libcmt.lib 和 libcmtd.lib, 反而出現更多 error lnk2005 ! 

Reference link

CUDA: Rule File in VC2008

執行 CUDA file (.cu) 時, 如果發生某某預期的 header file 不能讀取, 很可能是 CUDA file 沒有使用正確的 CUDA rules, 或是該 rule file 的 include path 不正確.

以下是 VS2008/VC++ 下設定 cuda rules 的方法


1. 一般來說, cuda rule 會置於VCProjectDefaults 下, 如下

C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\VCProjectDefaults
  1. NvCudaDriverApi.rules
  2. NvCudaDriverApi.v4.0.rules
  3. NvCudaRuntimeApi.rules
  4.  
  1.  
  1. NvCudaRuntimeApi.v4.0.rules

2. 如果是自訂的 project, 得先確認 project 有 CUDA Build rules.  可右擊 project, 點選"Custom Build Rules ..."; 對話盒出現 Build Rule Files 後, 點選適當的 rule path
                
        

     

3. 再指定 .cu file 使用 cuda rules..  可對該 cuda file 右擊, 點選 Properties



4. 出現該 cuda file 的 Property Pages 後指定 Tool. 本例中使用 CUDA Runtime API (會因為之前選擇的 CUDA rule file 不同而改變)

Configuration Properties > General > Tool  = "CUDA Runtime API"

5. 設定 Additional Include Directories
Configuration Properties > CUDA Runtime API > General > Additional Include Directories


Reference links
http://forums.nvidia.com/index.php?showtopic=30273http://forums.nvidia.com/index.php?showtopic=167114

最大似然估計 Maximum likelihood - I

學習筆記:ML,max likelihood 最大似然律

看到幾乎要爛的 XD

============================================
大致上來說, 會先用離散的機率說明, 從樣本猜猜主體是誰
如果有黑白棋各兩桶, A桶白子機率 1/5, B桶白子機率 4/5, 
對某桶拿了九子, W W B W W B B W W, 猜最可能是哪一桶? 

第一次遇上這類比喻, 只想把抽樣結果, 統計一下. 但 L(x) 不這樣玩,


L(x=A) = 可能是 A 的機率 = 1/5 * 1/5 * .... = 4^3 / 5^9
L(x=B) = 可能是 B 的機率 = 1/4 * 4/5 * .... = 4^6 / 5^9

可能是 A 的機率比較大, 所以是 A. 

老實說第一次在 wiki 上看到這個範例時,
很疑惑為何不能統計一下樣本呢? 比如白子出現機率是 6/9, 比較接近 4/5 之類的?

原因是,我們沒辦法說樣本的結果就是母體結果,只能從一次次的樣本累積,
判定是 A 還是 B 的可能性較大。

============================================

再繼續進入連續的機率函數 
如果知道身高是高斯分布, 但不知道 mean 和 sigma 
只有樣本, 要怎麼找出 mean 和 sigma 呢?

theta = { mean, sigma)
theta = max L(h; theta)
L(h; theta) = p(h1|theta) p(h2|theta) p(hk|theta)

未知數是 theta 
要找 theta, 能使 Likelihood 最大
而 L = 在 theta 下的連乘

通常轉個方法, 用 log 變成連加, 因為 p < 1, 就算連乘最大也只是小於 1, log P < 0
負的最大, 不如 -logP 最小. 

l = log L

而極值問題, 就可以用微分解決

∂l/∂ mean = 0 => ... 
∂l/∂ sigma = 0 => ...

python:將 list 內部分群

前面如何精簡地找出 list 中特定字的 index.
現實遇上的問題是 :  "一串 list, 自動分群, 同內容為一群"

前述討論串提供了好方法, 將 list 改成 dictionary
http://stackoverflow.com/questions/176918/finding-the-index-of-an-item-given-a-list-containing-it-in-python

a = ['foo','bar','baz','bar','any', 'foo', 'much']
l = dict(zip(set(a), map(lambda y: [i for i,z in enumerate(a) if z is y ], set(a))))
l['foo']
#[0, 5]
l ['much']
#[6]
l
{'baz': [2], 'foo': [0, 5], 'bar': [1, 3], 'any': [4], 'much': [6]}


上述等義先由 set 建立內容單一的集合, 再分群.
(老實說用 pyton, perl 會用太多內建物而失去效率. 建立 set 的過程顯然就分群了)

sub = set(a)
#set(['baz', 'foo', 'bar', 'any', 'much'])
def myset(val, a):
  return [i for i,v in enumerate(a) if v == val]
clusters = [myset(s, a) for s in sub]
# [[2], [0, 5], [1, 3], [4], [6]]

也可以再簡化為
b = [ [i for i,v in enumerate(a) if v is s] for s in sub ]
# [[2], [0, 5], [1, 3], [4], [6]]
跟 map 的結果是一樣的
map(lambda s: [i for i,v in enumerate(a) if v == s], set(a))
# [[2], [0, 5], [1, 3], [4], [6]]

希望有個 dictionary d,
d['baz'] = [2]
d['foo'] = [0,5]
d['bar'] = [1,3]
..

Dictionary 的生成方式, 要不是一個個  key:val, 要不用 zip (keys, val_list)
dict( zip(set(a), clusters) )

dict( zip(set(a), [[i for i,v in enumerate(a) if v == s] for s in set(a)] ) )
之後取用很方便
d['bar']
#[1, 3]