前面講到的都是離線的圖像擷取方法,實際中我們做機器視覺都是線上采集圖像和處理,處理結果決定了計算機要給出的控制信号如電機運動等,這樣就實作了實時視覺回報運動。MIL中的采集需要Matrox采集闆卡的支援,本文中以實驗室的Matrox Helios闆卡為例講解MIL的采集。
1.采集系統構成
談到采集,首先必須了解一套完整的采集系統從硬體到軟體的構成,下面采集系統示意圖采用Matrox闆卡、MIL軟體,圖中各種CPU、MCU、GPU互動通信的詳細過程并沒有表示出來,隻是為了說明大概流程,實際過程中完整采集系統差别不大。(以後有時間我會考慮單獨出一個機器視覺硬體系列博文,後話啦)
對照上圖,簡要說明一下采圖過程:光源照射下,物體反射光經過相機鏡頭在相機CCD(或CMOS)晶片上,這個過程成稱為Capture,相機的時序控制器控制間隔一定的時間将CCD中的資料傳輸到相機的緩存Buffer中,這個過程稱為Acquisition,注意如果這個Buffer的資料不及時取出來的話下次acquisition會覆寫以前的資料,相機連接配接到插入PCI-E接口上的Matrox闆卡上,在闆卡上的時序控制單元(Time control unit)控制從相機中Buffer中拿資料,這一過程稱為Grab,從相機buffer中拿的還是模拟信号,在闆卡中會通過A/D單元做一個A/D轉換,将拿到的資料轉成相應量化的數值存到相應的MIL buffer中,這一過程稱為Digtize。在這裡Capture和Acquisition在相機(Camera)中完成輸出的是模拟信号,這個相機是模拟相機,Grab和Digtize在相機采集闆卡(Frame Grabber)中完成,一般這樣的相機和闆卡之間用的是Camera Link接口,也有用1394接口的,适用于高速采集的情況;也有相機将Capture、Acquisition、Grab、Digtize做在一起的,實際上這也是大多數普通工業相機(13fps-30fps)的做法,他們輸出的是數字信号,稱為數字相機,一般采用GigE 、1394或USB接口。注意這裡我用紅字辨別的四個英文單詞Capture、Acquisition、Grab、Digtize,他們都可以翻譯為采集,英文有些單詞意義近似但是有微妙的不同,用中文是沒有辦法明确的區分它們的意思,事實上,我們通常所說的采集是站在PC擷取物體圖像的角度來說的,是這四個過程的總稱。
當我們在上位機(PC)中操作整個采集過程,MIL提供給我們用于采集的是配置設定的Digtizer對象,對應Mdig開頭函數。在配置設定Digitizer對象時要同時指明一個DCF(Device Configure File)檔案,這個檔案定義了Grab時的頻率和分辨率等等,是非常重要的,簡單來說就是相機時序控制器往相機Buffer存入資料的頻率和闆卡時序控制單元從相機Buffer中擷取資料的頻率必須有一個比對關系。預設MIL安裝時會讓使用者設定一個預設的DCF檔案,配置設定Digtizer時預設使用這個檔案,MIL提供了一系列對應相機的DCF檔案,如果沒有還可以在MIL Intellicam中自定義DCF檔案,同一個相機,可以在定義Digtizer時候采用不同的DCF檔案改變采集的頻率和采集的圖像大小,如圖中的Digtizer1和Digtizer2,如果想在定義了Digtizer以後實時調整DCF中對應的采集參數可以用MdigControl開頭函數。
下面對照上圖說明幾個概念,對大家看手冊有幫助:
Acquisition path:從Capture曆經Acquisition、Grab到Digtize一條完整的過程,如圖對于彩色相機有6條Acquisition path(RGB每一個算一個通道,即你可以把彩色相機當做單色相機來使),對于單色相機分别各有2條Acquisition path,每一條Acquisition path都必須包含一個Time control unit。每條Acquisition path上可連接配接若幹Digtizer,如圖中Digtizer1和Digitizer2,但是同一Acquisition path上一次隻能有一個Digtizer工作,你可以預配置設定多個Digtizer,在需要的時候做切換工作即可。
Independent acquisition path:一個Time control unit一次隻能控制一條Acquisition path采集,如果想實作兩個相機同時采集就必須用兩個Time control unit,兩條擁有不同的Time control unit的Acquisition path稱為Independent acquisition path。如圖彩色相機和單色相機的Acquisition path之間就是Independent acquisition path,可以同時采集。連接配接到兩個Independent acquisition path上的Digtizer可以同時工作采圖。
Data input channel (channel):經常我們會聽到雙通道采集,就是同一個Time control unit的Capture源分為多個,每一個稱為一個通道,在采集的時候可以切換采集,但是不能同時采集。
Device Number:MIL中配置設定Digtizer時要求指明每個Channel的第一條Acquisition path的device number,MIL會根據相應的DCF檔案自動計算總的Acquisition path數目,如指明channel 0的彩色相機的R Acquisition path為M_DEV0,那麼G Acquisition path和B Acquisition path相應就為M_DEV1和M_DEV2。這時候Channel 0的單色相機就隻能配置設定從M_DEV3開始的Device Number了。如果闆卡隻有一個Time control unit,那麼必須指明為M_DEV0或M_DEFAULT。
2.MIL采集和實時顯示
MIL中采集有關函數都是以Mdig開頭,開啟采集操作的函數為MdigGrab,MdigGrabContinuous,MdigProcess,這三個函數的功能從字面上就很容易了解,分别對應單幀采集,連續采集和采集的同時處理。 在具體将這三個函數前,還要說明一點的就是要清楚 三個Buffer之間的轉移和 大小及類型對應關系。這三個Buffer按資料傳遞順序為相機Buffer、PC的Buffer(記憶體)、顯存Buffer,示意圖如下:
當我們采集的時候使用Mdig函數将資料從相機buffer傳到PC的記憶體buffer(這裡采用MIL定義的Buffer)中,當我們顯示的時候使用Mdisp函數将資料從記憶體buffer從傳遞到顯存Buffer(這裡指代顯示的視窗)中。 這裡注意示意圖上的每一個小方格代表一個像素點資料,可以看到不是每個Buffer一開始就是大小不一定是一樣的:如果相機Buffer大于PC Buffer采集的時候會 自動将多的像素點截掉;如果相機Buffer小于PC Buffer采集的時候會 自動從Buffer的初始點(預設為左上角)開始填充,PC Buffer中剩下未填充的會保持初始化狀态(是以一般配置設定完Buffer後立即初始化);PC Buffer和顯存Buffer的轉移關系類似。如果相鄰Buffer之間大小不一樣,我們可以使用MdigControl控制采集時的比例(例如采集比例設為1/2,那麼采集時從相機Buffer間隔取值,相當于相機Buffer為原來的1/2),使用MdispZoom控制顯示的比例(例如顯示比例設為2,那麼顯示時從PC Buffer線性插補為原來2倍)。另外,如果想控制預設對齊的初始點,可采用MdigControl和MdispPan調整期對應參數,一般不改變,具體參看MIL手冊。為了獲得采集的最佳效果,我們 不應當對相機Buffer做任何假設,應該采用MdigInquire函數來查詢相機Buffer的大小和類型(例如8+M_UNSIGNED),配置設定對應的PC Buffer,同樣 不應當對顯存Buffer做任何假設,應當查詢顯示Buffer大小,顯示時對PC Buffer做相應放大縮小操作。
MdigGrab
示例代碼如下
//配置設定預設的應用、系統
MappAllocDefault(M_SETUP, &MilApplication, &MilSystem, M_NULL, M_NULL, M_NULL);
//配置設定采集器
MdigAlloc(MilSystem, M_DEFAULT, "M_DEFAULT", M_DEFAULT, &MilDigitizer);
int nGrabScaleSet = 4;//設定的采集比例
//配置設定buffer
if (MsysInquire(MilSystem, M_SYSTEM_TYPE, M_NULL) == M_SYSTEM_HELIOS_TYPE)
{
BufferLocation = M_ON_BOARD;
}
MbufAlloc2d(MilSystem,
long(MdigInquire(MilDigitizer, M_SIZE_X, M_NULL) / nGrabScaleSet),
long(MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL) / nGrabScaleSet),
MdigInquire(MilDigitizer, M_TYPE, M_NULL),
M_DISP + M_IMAGE + M_GRAB + BufferLocation,
&MilBufferImage);
MbufClear(MilBufferImage, 0xFF);
//配置設定顯示
MdispAlloc(MilSystem, M_DEFAULT, "M_DEFAULT", M_WINDOWED, &MilDisplay);
//Buffer和Display綁定
MdispSelectWindow(MilDisplay, MilBufferImage, GetDlgItem(IDS_DISPLAY)->GetSafeHwnd());
//單幀采集兩幀
MdigControl(MilDigitizer, M_GRAB_SCALE_X, 1.0/nGrabScaleSet);
MdigControl(MilDigitizer, M_GRAB_SCALE_Y, 1.0/nGrabScaleSet);
MdigControl(MilDigitizer, M_GRAB_MODE, M_ASYNCHRONOUS );
MdigGrab(MilDigitizer, MilBufferImage);
Sleep(1000);//停頓一秒
MdispZoom(MilDisplay, 2, 2);
MdigGrab(MilDigitizer, MilBufferImage);
Sleep(1000);//停頓一秒
//釋放資源
if (M_NULL != MilBufferImage)
{
MbufFree(MilBufferImage);
}
if (M_NULL != MilDisplay)
{
MdispFree(MilDisplay);
}
if (M_NULL != MilDigitizer)
{
MdigFree(MilDigitizer);
}
if (M_NULL != MilApplication)
{
MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
}
MdigGrab很簡單,就是每次從相機Buffer中抓取一幀到PC記憶體Buffer中,注意我們這裡采用MdigInquire函數查詢相機Buffer大小和配置設定,MdigControl控制采集比例,這裡配置設定的PC Buffer和相機Buffer大小和類型是比對的,但是這裡的PC Buffer和顯存Buffer(視窗大小)是不一樣的,讀者自己處理吧(GetWindowRect和MdispZoom)。
還需要注意的是采集的時候有兩種基本模式,異步和同步,一般采集異步(M_ASYNCHRONOUS),具體含義請檢視MIL手冊。
MdigGrabContinuous
開始連續采集
//配置設定預設的應用、系統
MappAllocDefault(M_SETUP, &MilApplication, &MilSystem, M_NULL, M_NULL, M_NULL);
//配置設定采集器
MdigAlloc(MilSystem, M_DEFAULT, "M_DEFAULT", M_DEFAULT, &MilDigitizer);
int nGrabScaleSet = 4;//設定的采集比例
//配置設定buffer
if (MsysInquire(MilSystem, M_SYSTEM_TYPE, M_NULL) == M_SYSTEM_HELIOS_TYPE)
{
BufferLocation = M_ON_BOARD;
}
MbufAlloc2d(MilSystem,
long(MdigInquire(MilDigitizer, M_SIZE_X, M_NULL) / nGrabScaleSet),
long(MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL) / nGrabScaleSet),
MdigInquire(MilDigitizer, M_TYPE, M_NULL),
M_DISP + M_IMAGE + M_GRAB + BufferLocation,
&MilBufferImage);
MbufClear(MilBufferImage, 0xFF);
//配置設定顯示
MdispAlloc(MilSystem, M_DEFAULT, "M_DEFAULT", M_WINDOWED, &MilDisplay);
//Buffer和Display綁定
MdispSelectWindow(MilDisplay, MilBufferImage, GetDlgItem(IDS_DISPLAY)->GetSafeHwnd());
//開始連續采集
MdigGrabContinuous(MilDigitizer, MilBufferImage);
關閉連續采集
MdigHalt(MilDigitizer);
//釋放資源
if (M_NULL != MilBufferImage)
{
MbufFree(MilBufferImage);
}
if (M_NULL != MilDisplay)
{
MdispFree(MilDisplay);
}
if (M_NULL != MilDigitizer)
{
MdigFree(MilDigitizer);
}
if (M_NULL != MilApplication)
{
MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
}
注意MdigGrabContinuous為異步采集模式,在采集過程中是直接送到顯存中的,不儲存在MilBufferImage中,隻有停止采集後的最後一幀儲存在MilBufferImage中,一般用于實作線上觀測功能,要想實作實時抓取圖像和處理隻能采用MdigGrab和MdigProcess函數。
MdigProcess
開始采集
// TODO: Add your control notification handler code here
//配置設定預設的應用、系統
MappAllocDefault(M_SETUP, &MilApplication, &MilSystem, M_NULL, M_NULL, M_NULL);
//配置設定采集器
MdigAlloc(MilSystem, M_DEFAULT, "M_DEFAULT", M_DEFAULT, &MilDigitizer);
//配置設定顯示buffer
MbufAlloc2d(MilSystem,
long(MdigInquire(MilDigitizer, M_SIZE_X, M_NULL)),
long(MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL)),
MdigInquire(MilDigitizer, M_TYPE, M_NULL),
M_DISP + M_IMAGE,
&MilBufferImage);
MbufClear(MilBufferImage, 0xFF);
//配置設定顯示
MdispAlloc(MilSystem, M_DEFAULT, "M_DEFAULT", M_WINDOWED, &MilDisplay);
//Buffer和Display綁定
MdispSelectWindow(MilDisplay, MilBufferImage, GetDlgItem(IDS_DISPLAY)->GetSafeHwnd());
/************************************************************************/
/* 配置設定Buffer List */
/************************************************************************/
MappControl(M_ERROR, M_PRINT_DISABLE);
//初始化buffer list
for(m = 0; m < BUFFERING_SIZE_MAX; m++)
{
MilGrabBufferList[m] = M_NULL;
}
//配置設定盡可能多的buffer list
if (MsysInquire(MilSystem, M_SYSTEM_TYPE, M_NULL) == M_SYSTEM_HELIOS_TYPE)
{
BufferLocation = M_ON_BOARD;
}
MilGrabBufferListSize=0;
for(m = 0; m < BUFFERING_SIZE_MAX; m++)
{
//配置設定一個Buffer
MbufAlloc2d(MilSystem,
MdigInquire(MilDigitizer, M_SIZE_X, M_NULL),
MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL),
MdigInquire(MilDigitizer, M_TYPE, M_NULL),
M_IMAGE+M_GRAB+M_PROC+BufferLocation,
&MilGrabBufferList[m]);
if (MilGrabBufferList[m])//配置設定成功則初始化
{
MbufClear(MilGrabBufferList[m], 0xFF);
LastAllocatedM = m;
MilGrabBufferListSize++;
}
else//配置設定失敗則停止配置設定
{
break;
}
}
MappControl(M_ERROR, M_PRINT_ENABLE);
//防止占完記憶體空間,釋放最後一個buffer
MbufFree(MilGrabBufferList[LastAllocatedM]);
MilGrabBufferList[LastAllocatedM] = M_NULL;
MilGrabBufferListSize--;//注意這裡釋放後一定要将相應的size-1,否則調用MdigProcess檢測
//到實際可用buffer size和傳入的size參數不符,會報錯
/************************************************************************/
/*采集和處理*/
/************************************************************************/
//設定待傳遞的資料
UserHookData.MilImageDisp = MilBufferImage;
UserHookData.ProcessedImageCount = 0;
MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
M_START, M_DEFAULT, ProcessingFunction, &UserHookData);
停止采集
/************************************************************************/
/*停止采集和處理*/
/************************************************************************/
MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
M_STOP, M_DEFAULT, ProcessingFunction, &UserHookData);
//釋放資源
MappControl(M_ERROR, M_PRINT_DISABLE);
for (m = 0; m < BUFFERING_SIZE_MAX; m++)
{
if(M_NULL != MilGrabBufferList[m])
{
MbufFree(MilGrabBufferList[m]);
MilGrabBufferList[m] = M_NULL;
}
}
if (M_NULL != MilBufferImage)
{
MbufFree(MilBufferImage);
}
if (M_NULL != MilDisplay)
{
MdispFree(MilDisplay);
}
if (M_NULL != MilDigitizer)
{
MdigFree(MilDigitizer);
}
MappControl(M_ERROR, M_PRINT_ENABLE);
if (M_NULL != MilApplication)
{
MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
}
采集回調函數
long MFTYPE ProcessingFunction(long HookType, MIL_ID HookId, void MPTYPE *HookDataPtr)
{
HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
MIL_ID ModifiedBufferId;
char Text[10]= {'\0'};
//得到buffer list獲得采集資料的buffer号
MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);
UserHookDataPtr->ProcessedImageCount++;
//目前圖檔上寫入采集編号
MOs_ltoa(UserHookDataPtr->ProcessedImageCount, Text, 10);
MgraText(M_DEFAULT, ModifiedBufferId, 10, 10, Text);
//處理完的Buffer資料複制到顯示buffer
MbufCopy(ModifiedBufferId, UserHookDataPtr->MilImageDisp);
return 0;
}
原則上我們推薦使用MdigProcess函數,首先可以使用多Buffer優化程式性能,其次給了我們對采集過程的強大控制能力,我們可以在采集回調函數中實時處理采集到的圖像或儲存采集的每一幀(這也是MIL錄像功能實作的原理)。在示例中,我示範的是使用回調函數對采集到的每一幀添加一個序号。
程式注釋我已經寫的很清楚了,結合MIL手冊相信寫出完整的采集程式不是什麼大問題了。
部落格中完整代碼檔案 下載下傳連結
原創,轉載請注明來自 http://blog.csdn.net/wenzhou1219