Skip to main content

關於『數位影像分析之智慧型監視系統』的Q&A - Part 4

作者:Mr. Lai Tai-Yu, Averill, 赖岱佑, 賴岱佑
標題:關於『數位影像分析之智慧型監視系統』的Q&A - Part 4

由於有網友對於數位影像演算法及智慧型監視系統有疑問,因此整理如下:
P.S.整理得很粗糙,不好意思,凡是來信有帳號名稱、人名、稱謂、單位名稱或地名,都不包含在內。小弟儘量仔細檢查,將可以放上來的問題公佈出來,以供其他讀者參考。但若您認為不方便將問題公開,煩請來信告知,有冒犯之處,還請多多見諒。我會儘速處理,謝謝各位,靜祝各位萬事如意、鴻圖大展、健康快樂、心想事成。感謝。
P.S.小弟的回答,僅供參考,還需要各位先進指導,謝謝。


Q1:目前在想~在時間背景完成前~到底該用什麼來當作背景?當時間背景完成後~有物件在上面不更新背景這樣就可以了~可是在時間背景完成前~若是一開始就有車停在上面~那該怎麼辦?一開始就有車在上面~時間背景就會錯誤~快速背景也沒辦法取得動量標記物件~也沒辦法儲存一個固定背景~請問還有其他計算背景的方法嗎??
A1:先使用快速分割法取出動量,然後有動量的部分不要更新為背景,如此就是漸進式背景法。
一開始若有車輛停在螢幕內,那變成背景是無法避免的,重點是當車輛移走時,要抓住車輛並且將原來車輛的位置更新為背景。
先想想把時間背景法、快速分割法、漸進式背景法結合在一起,這樣就可以大致解決您的問題。

Q2:那8位元灰階是要用甚麼方式寫
A2:以下這段程式碼是處理8位元深度的BMP檔寫入方式,供您參考。
BITMAPFILEHEADER*bmpFH=newBITMAPFILEHEADER;
BITMAPINFOHEADER*bmpIH=newBITMAPINFOHEADER;
RGBQUAD*rgbQuad=newRGBQUAD[256];
bmpFH->bfType=0x4d42;
bmpFH->bfReserved1=0;
bmpFH->bfReserved2=0;
bmpFH->bfSize=(DWORD)sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBQUAD)*256)+
(ulSRangeWidth*ulSRangeHeight);
bmpFH->bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBQUAD)*256);
bmpIH->biSize=sizeof(BITMAPINFOHEADER);
bmpIH->biWidth=(LONG)(ulSRangeWidth);
bmpIH->biHeight=(LONG)(ulSRangeHeight);
bmpIH->biPlanes=1;
bmpIH->biBitCount=8;
bmpIH->biCompression=BI_RGB;
bmpIH->biSizeImage=(DWORD)(ulSRangeWidth*ulSRangeHeight);
bmpIH->biXPelsPerMeter=0;
bmpIH->biYPelsPerMeter=0;
bmpIH->biClrUsed=0;
bmpIH->biClrImportant=0;
for(intiI=0;iI<256;iI++)
{
rgbQuad[iI].rgbBlue=iI;
rgbQuad[iI].rgbGreen=iI;
rgbQuad[iI].rgbRed=iI;
rgbQuad[iI].rgbReserved=0;
}
charpcFileName[100];
sprintf_s(pcFileName,"C:\\[目錄]\\[檔名].bmp");
ulCountLPRIndex++;
FILE*pBMPFile=fopen(pcFileName,"wb");
if(pBMPFile)
{
fwrite(bmpFH,sizeof(BITMAPFILEHEADER),1,pBMPFile);
fwrite(bmpIH,sizeof(BITMAPINFOHEADER),1,pBMPFile);
fwrite(rgbQuad,sizeof(RGBQUAD)*256,1,pBMPFile);
fwrite(pbyBitmap_G8,ulSRangeWidth*ulSRangeHeight,1,pBMPFile);
fclose(pBMPFile);
}
SAFE_DELETE(bmpFH);
SAFE_DELETE(bmpIH);
SAFE_DELETE_ARRAY(rgbQuad);
SAFE_DELETE_ARRAY(pbyBitmap_G8);


Q3.目前正處於學習影像處理,
A3.很高興您對於數位影像處理有興趣。

Q4.我想嘗試寫即時視訊瞳孔追蹤的程式,我想了幾個步驟流程,先搜尋臉->再找到眼部->最後找到瞳孔位置,目前正試寫搜尋臉,我是以膚色偵測的方式來偵測臉部區塊,但有誤判雜點的情形,這部分應如何處理? 另外如何從膚色像素進一步來找出臉部區塊?
A4.在我的拙作『數位影像分析之智慧型監控系統』一書就提到膚色偵測法目前在學術界上仍是個爭議的問題,大家在探討何謂膚色,如何定義膚色,各問論文所提出的膚色都是經過樣本統計下的結果,而非一個完美的數學解來定義膚色。所以使用膚色本來就會有很多問題。
In Addition, 一般做瞳孔偵測,都是只針對瞳孔影像作分析,而且瞳孔位於臉部屬於較小範圍,沒有百萬像素以上等級,很容易將眉毛與眼睛部分誤判。這點可以參考華碩ASUS的LifeFrame產品,就有這樣的誤偵測。我的建議是要做瞳孔就針對瞳孔影像作分析,而不要從人臉開始。應該要反推回來,就是先把瞳孔分析做好,再退一步做到人臉定位瞳孔,這樣會比先偵測臉部再找瞳孔來的有效率。

Q5.在偵測人臉的研究中,有Haar like方式偵測人臉,但對這個方法不了解,比較膚色偵測的方法與Haar like,似乎是Haar like比較符合即時的效率來達到偵測人臉,想請教什麼是Haar like?
A5.Haar Like牽涉到影像積分,以及特徵建立,Haar Like的方法的確優於膚色偵測法,除了傳統的Haar Like慢速的積分及辨識,現在還有快速Haar Like積分及辨識法。最最簡單的概念就是計算灰度的特徵,進而找到符合人臉灰度特徵。但實際上還要更複雜一些,並非三言兩語就可以解釋清楚,因為Haar Like不斷被應用及進步,他還搭配其他分類器,使得辨識率及效能更高更快,例如:請搜尋關於adaboost haar like的關鍵字,您會有很多收穫。

Q6.還是老師有更好的方法及流程,懇請老師指導。
A6.我並沒有跟您詳談過,我也不知道您教授的研究方向,也不知道您的整個細節,因此無法有更多的建議,怕會誤導您的方向,導致您無法畢業。
我把我寫的人臉偵測程式寄給您一份做為參考,如附件:FaceDetectionRelease.rar,解壓縮後點選執行檔,就可以由Web Camera獲得影像,並將人臉框出。請參考。

Q7.我想對04-06_長條圖強化 書中範例改寫成開擋讀圖方式,我用MFC 精靈產程式碼 跟 書程式碼是不一樣的,HistogramEqualization.cpp ,我看長條圖強化程式碼主要是寫在 ,HistogramEqualizationDlg.cpp 是OK 那按鈕?
void CHistogramEqualizationDlg::OnBnClickedOk() // 點 OK 按鈕就 call 他嗎?
1. 想請問一下OK 按鈕// 請問你這檔是怎麼產生出來的?用資源建出來的嗎?
2. 要改成由開檔方式BMP 要怎麼要寫長條圖強化範例?
3. 在MFC 裡面有哪個物件可以開很多種圖檔格式? 例如 jpg ,gif , bmp都可以開?
A7.
>>我用MFC 精靈產程式碼 跟 書程式碼是不一樣的
那是因為你勾選的步驟與我不同。
>>我看長條圖強化程式碼主要是寫在 HistogramEqualizationDlg.cpp 是OK 那按鈕?
是的。
>> void CHistogramEqualizationDlg::OnBnClickedOk() // 點 OK 按鈕就 call 他嗎?
點了OK鈕就執行這個函式。
>>1. 想請問一下OK 按鈕// 請問你這檔是怎麼產生出來的?用資源建出來的嗎?
點選ViewResource View找到Dialog就可透過拖拉的方式將按鈕放上去。
>>2. 要改成由開檔方式BMP 要怎麼要寫長條圖強化範例?
使用CFileDialog就可以有你所謂開檔的功能。以下的Code給你參考。
CFileDialog fd( TRUE, NULL, NULL, OFN_EXPLORER, _T("Bitmap (*.bmp)|*.bmp\0"), NULL, 0 );
if( fd.DoModal() == IDOK )
{
CString cstrPathName = fd.GetPathName();
int intLengthPathName = cstrPathName.GetLength();
CHAR strPathName [ 1024 ];
memset ( &strPathName, 0x00, 1024 * sizeof(CHAR) );
for (int i = 0; i < intLengthPathName; ++i)
{
strPathName[i] = (CHAR)cstrPathName[i];
}
memcpy ( &Image1InPath, &strPathName, intLengthPathName );
GetDlgItem(IDC_editImageIn1)->SetWindowText(strPathName);
}
else
{
memset ( &Image1InPath, 0x00, MAX_PATH * sizeof(char) );
}
>>3. 在MFC 裡面有哪個物件可以開很多種圖檔格式? 例如 jpg ,gif , bmp
使用GDI+就可以開啟很多種格式。

A8. 請問用 windows Form 跟用 MFC 寫優缺點在哪?
Q8. 這是一個很多解釋的問題,從不同面向回答的答案都不同。
我猜測您關心的想法,嘗試做回答。請當參考。
您使用Windows Form就是在Net. Framework下寫程式。
NET. Framework是 Base Class Library,最主要的特色,就是架構在 CLR (Common Language Runtime )之上。
MFC 則是在 Windows早期為方便 C++ 程式設計師撰寫 Windows 應用程式,設計的C++ Class Library。
就效能來說,.NET Managed 應用程式和MFC效能的差別是在第一次執行時,Managed程式必須被CLR JIT Compiler編譯,所以比較慢,基本上小弟實驗的結果,編譯完成後就和MFC應用程式效能差不多了。

A9.我這兩天拜讀您寫的書。看到High pass或Low pass等Filter範例,範圍都是從1開始到width-1。然後去和3*3矩陣(Filter)做處理。最後將新值存在新的image上,然後輸出。但是0和width等,邊緣線似乎沒被處理到。是否有其他目的或疏漏之處,還望指教一二。
A9. 關於您的問題,這是因為在邊緣處沒有辦法取得3x3的資料,因此通常在處理時,有兩種方法:
第一種方法就是忽略最邊緣處,直接填上黑色(0),就如同本書的作法。
第二種作法就是處理到邊緣時,沒有資料的部分,直接以(0)代替。
您對於數位影像有興趣,因此我另外附上一些資料給您參考。這些資料與小弟拙作有關『數位影像處理技術手冊』與『數位影像分析之智慧型監視系統』。

Q10.由於專題想要帶與ip cam有關的應用(遠端機器人控制),在網站爬了許多文,但沒找到完整資訊,正巧看到您的大作,馬上就到博客來訂購您的書,由於本身興趣,工作相關及希望能深入了解及實作,可否冒昧的向您請求相關的程式碼及論文,謝謝!
A10,由於我不了解您的需求,是有關於數位影像處理或是視訊影像處理,還是機器人控制,所以我盡我所能把相關的程式原始碼寄給您,但由於我不了解機器人控制,在這方面我還得多多學習。若您的問題是關於數位影像或視訊處理,我相信我是可以幫得上忙,但我需要您再仔細描述需求,否則這方面的資料很多,會不知道您所需要的方向。
附件有五支程式原始碼,對於即時處理視訊影像有所幫助,但卻不知道是否是您所需要的資料。請先參考。

Q11. 關於不同位元深度的BMP檔讀寫
A11. 請參考這段Code。這段Code附有調色盤的部分,依照不同位元深度,自行修改,就可以讀任何位元深度的BMP圖檔了。
int iSaveRangeSX = 0;
int iSaveRangeEX = 0;
int iSaveRangeSY = 0;
int iSaveRangeEY = 0;
iSaveRangeSX = iRangeSX;
iSaveRangeEX = iRangeEX;
iSaveRangeSY = iRangeSY;
iSaveRangeEY = iRangeEY;
if ( ( iSaveRangeEX - iSaveRangeSX + 1 ) % 4 != 0 )
{
int iTempA = 0;
int iTempB = 0;
iTempA = ( ( iSaveRangeEX - iSaveRangeSX + 1 ) / 4 ) * 4;
iTempB = ( iSaveRangeEX - iSaveRangeSX + 1 ) - iTempA;
iSaveRangeEX -= iTempB;
iSaveRangeEX += 4;
}
int iSaveRangeWidth = 0;
int iSaveRangeHeight = 0;
iSaveRangeWidth = ( iSaveRangeEX - iSaveRangeSX + 1 );
iSaveRangeHeight = ( iSaveRangeEY - iSaveRangeSY + 1 );
BYTE* pbySaveTemp_G8 = new BYTE [ iSaveRangeWidth * iSaveRangeHeight * sizeof(BYTE) ];
memset ( pbySaveTemp_G8, 0x00, iSaveRangeWidth * iSaveRangeHeight * sizeof(BYTE) );
int iYS = 0;
int iXS = 0;
for ( int iYY = iSaveRangeSY; iYY <= iSaveRangeEY; iYY++ )
{
for ( int iXX = iSaveRangeSX; iXX <= iSaveRangeEX; iXX++ )
{
int iIndex = ( iYY * iMotionROI_Width ) + ( iXX );
int iIndexS = ( iYS * iSaveRangeWidth ) + iXS;
*( pbySaveTemp_G8 + iIndexS ) = *( pbyMarkObject_G8 + iIndex );
iXS++;
}
iXS = 0;
iYS++;
}
unsigned long ulSRangeHeight = ( iSaveRangeEY - iSaveRangeSY + 1 );
unsigned long ulSRangeWidth = ( iSaveRangeEX - iSaveRangeSX + 1 );
BYTE* pbyBitmap_G8 = new BYTE[ ulSRangeWidth * ulSRangeHeight * sizeof(BYTE) ];
memset ( pbyBitmap_G8, 0, ulSRangeWidth * ulSRangeHeight * sizeof(BYTE) );
unsigned long ulTempY = 0;
unsigned long ulTempX = 0;
for ( int iY = iSaveRangeHeight - 1; iY >= 0; iY-- )
{
for ( unsigned long ulX = 0; ulX < (unsigned long)iSaveRangeWidth; ulX++ )
{
unsigned long ulSourceIndex = ( ulX ) + ( (unsigned long)iY * iSaveRangeWidth );
unsigned long ulDestinationIndex = ( ulTempX ) + ( ulTempY * ulSRangeWidth );
*( pbyBitmap_G8 + ulDestinationIndex ) = *( pbySaveTemp_G8 + ulSourceIndex );
ulTempX++;
}
ulTempX = 0;
ulTempY++;
}
BITMAPFILEHEADER *bmpFH = new BITMAPFILEHEADER;
BITMAPINFOHEADER *bmpIH = new BITMAPINFOHEADER;
RGBQUAD *rgbQuad = new RGBQUAD[256];
bmpFH->bfType = 0x4d42;
bmpFH->bfReserved1 = 0;
bmpFH->bfReserved2 = 0;
bmpFH->bfSize = (DWORD) sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + ( sizeof(RGBQUAD) * 256 ) +
( ulSRangeWidth * ulSRangeHeight );
bmpFH->bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + ( sizeof(RGBQUAD) * 256 );
bmpIH->biSize = sizeof(BITMAPINFOHEADER);
bmpIH->biWidth = (LONG) ( ulSRangeWidth );
bmpIH->biHeight = (LONG) ( ulSRangeHeight );
bmpIH->biPlanes = 1;
bmpIH->biBitCount = 8;
bmpIH->biCompression = BI_RGB;
bmpIH->biSizeImage = (DWORD) ( ulSRangeWidth * ulSRangeHeight );
bmpIH->biXPelsPerMeter = 0;
bmpIH->biYPelsPerMeter = 0;
bmpIH->biClrUsed = 0;
bmpIH->biClrImportant = 0;
for ( int iI = 0; iI < 256; iI++ )
{
rgbQuad[iI].rgbBlue = iI;
rgbQuad[iI].rgbGreen = iI;
rgbQuad[iI].rgbRed = iI;
rgbQuad[iI].rgbReserved = 0;
}
char pcFileName[100] ;
sprintf_s( pcFileName, "C:\\LP\\LPR__%d__%d.bmp", lPixelChangeDistance, ulCountLPRIndex );
ulCountLPRIndex++;
FILE *pBMPFile = fopen( pcFileName, "wb" );
if( pBMPFile )
{
fwrite( bmpFH, sizeof(BITMAPFILEHEADER), 1, pBMPFile );
fwrite( bmpIH, sizeof(BITMAPINFOHEADER), 1, pBMPFile );
fwrite( rgbQuad, sizeof(RGBQUAD) * 256, 1, pBMPFile );
fwrite( pbyBitmap_G8, ulSRangeWidth * ulSRangeHeight, 1, pBMPFile );
fclose( pBMPFile );
}
SAFE_DELETE (bmpFH);
SAFE_DELETE (bmpIH);
SAFE_DELETE_ARRAY ( rgbQuad );
SAFE_DELETE_ARRAY ( pbyBitmap_G8 );
SAFE_DELETE_ARRAY ( pbySaveTemp_G8 );

Q12. 前您提到~有物件在上面時候~就不要更新時間背景~那應該要用哪一個物件提取的結果來參考呢?一個是影像減去時間背景(可是當時間背景還沒學完時物件資訊並不正確)另一個是快速背景法(可是這物件資訊並沒有很正確)另一個是影像減去預先儲存背景(這最好可是我沒辦法預先取得一個OK的背景)還是有另外的方法比較OK呢??
A12. 呵呵~您真的很用心喔,這就是關鍵。用教科書的背景法絕對不實用ㄉ,有沒有想過把三個背景加再一起呢?互補各背景法的不足呢?這個方向才對喔~。目前沒還有任何一種背景法可以解決所有問題。您真的很用心在做,思考一下吧,這三個要怎麼結合。

Q13. 目前在想~在時間背景完成前~到底該用什麼來當作背景?當時間背景完成後~有物件在上面不更新背景這樣就可以了~可是在時間背景完成前~若是一開始就有車停在上面~那該怎麼辦?一開始就有車在上面~時間背景就會錯誤~快速背景也沒辦法取得動量標記物件~也沒辦法儲存一個固定背景~ 請問還有其他計算背景的方法嗎??
A13. 先使用快速分割法取出動量,然後有動量的部分不要更新為背景,如此就是漸進式背景法。一開始若有車輛停在螢幕內,那變成背景是無法避免的,重點是當車輛移走時,要抓住車輛並且將原來車輛的位置更新為背景。先想想把時間背景法、快速分割法、漸進式背景法結合在一起,這樣就可以大致解決您的問題。

Q14. 請教您一下~關於背景~我有想到個方法~先用人工方式取得一張完成的時間背景~將這張完成的時間背景暫時當作程式一開始的時間背景~之後有物件在上面~就不更新時間背景~這樣的方法可以解決~如果有車停在上面很久~會變成背景的問題~而且背景依舊可以一直更新~不知道這個方法正確嗎?有沒有更好的其他方法?(因為還是一定要去現場利用人工取的一張的背景)這個方法不知道還有哪些我還沒想到的問題?或是有更棒的取得背景方式?
A14. 先用人工取得一張背景的話,那就需要清場,學術研究是可行的,但實際應用是不可行的。現在的重點不是車子變成背景,而是車子由背景離開時,能夠抓到車子,並且將車子移開的位置更新為背景。所以您的問題要換個角度思考,才會有解。嘗試漸進式背景法+時間背景法+快速分割法,您會有收穫的。

Q15. 這邊是依照您給的 VC 相似度 code 改的 Pxy 會大於1 這正確嗎 ??
Public Function HistogramSimilarity( _
ByRef Histogram1() As Integer, _
ByRef Histogram2() As Integer) As Double
Dim dbA As Double = 0, dbB As Double = 0
Dim dbC As Double = 0, dbP As Double = 0
For IntI As Integer ......
A15.
要做比較的話,第一筆資料要少於或小於第二筆資料,或者兩筆資料是相等長度。這樣才能正確比較。因此您可能第一筆資料大於或多於第二筆資料,所以才會產生大於1的情形,也就是超過100%。
不過由於您是使用長條圖,照理來說資料量是相當的,您所謂的大於1 ,有點奇怪。
試著將For IntI As Integer ......= 0 To 256 * 3
改成For IntI As Integer ......= 0 To 255 * 3
好嗎?

Q16. 請教您一下~假設相似度函式正確~利用相似度去與上一張Frame中的所有物件去比對~最像的就是上一個物件~但是最像不代表一定是耶~假設目前的 Frame 中有一個新的物體進入或是離開~那該怎麼辦呢?還有當物體離開或進入畫面一半~那這樣也沒辦法計算正確丫??將目前 Frame 中所有的物件去跟上一張所有 Frame中比對~假設從第一個物件開始比對~比對到與上一張第1個物件最像~第二個物件開始比對也發現與第1個物件最像~那怎麼辦呢?>"< 怎麼覺得越後面的問題越困難~都完全想不到一點頭緒~前面的問題還有辦法去找資源來學來處理~後面的都沒地方能學到如何解決!
A16. 先用相似度來做比較,這是比較簡單的做法。
當有新的物體進入,相似度就會很差,因此應該建立新物件資訊。
若有兩個物件相似度都很高,當然取最高相似度為主。
除了相似度之外,還有距離量測也是很簡單的做法。
當物體移動位置很接近時,也可以直接給予。......

Q17. 您好~又要請教您一下問題了~我目前遇到的問題是想要利用兩支以上同型號的webcam 同時拍攝BMP影像存入電腦的不同資料夾內~解析度也需要1600*1200由於我並沒有C語言的基礎所以這兩三個禮拜都在鬼打牆= =||~希望賴老師能給些建議與指導~謝謝
A17. 我了解您沒有C語言基礎要做影像處理的難處,因此我提供以下的程式碼,藉由OpenCV的幫助,您只需要寫以下不到20行的程式,就可以做到雙Camera的影像擷取了。嘗試看看吧。
#include <cv.h>
#include <highgui.h>
int main()
{
CvCapture *capture1,*capture2;
IplImage *frame1,*frame2;
capture1 =cvCaptureFromCAM(0);
capture2 =cvCaptureFromCAM(1);
cvNamedWindow("C1",1);
cvNamedWindow("C2",1);
while(true)
{
frame1 = cvQueryFrame(capture1);
frame2 = cvQueryFrame(capture2);
cvShowImage("C1",frame1);
cvShowImage("C2",frame2);
if(cvWaitKey(10)>=0)
break;
}
}

Popular posts from this blog

Python 日期與時間的處理

Visual Basic 6.0 (VB6) 程式語言案例學習 (10. 條碼列印程式)

寫作:波蘭文學習之旅:1-1. 波蘭文字母與發音(注音版)

Python 日期與時間的處理

Image

Visual Basic 6.0 (VB6) 程式語言案例學習 (10. 條碼列印程式)

Image

寫作:波蘭文學習之旅:1-1. 波蘭文字母與發音(注音版)

Image

數位影像處理:最佳化處理策略之快速消除扭曲演算法

Image

Visual Basic 6.0 (VB6) 程式語言案例學習 (04. 人事考勤管理系統)

Image

用10種程式語言做影像二值化(Image binarization)

Visual Basic 6.0 (VB6) 程式語言案例學習 (07. 收據列印程式)

Image

Visual Basic .Net (VB.Net) 程式語言案例學習 (03. 場地預約系統)

Image

Visual Basic 6.0 (VB6) 程式語言案例學習 (11. 生產線拍照程式)

Image

Visual Basic .Net (VB.Net) 程式語言案例學習 (06. 題庫測驗系統)

Image