Mil을 이용한 프로그래밍을 위해서는 라이브러리에 대한 기본적인 구조를 알아야한다.
카메라로부터 영상을 획득해 디스플레이 하거나, 처리를 해주기 위해서는 다음과 같은 5가지 요소가 필요합니다.
먼저 이 5가지 요소에 대한 선언을 해주고 프로그램 초기화부분에 Allocate해줍니다. 마지막으로
프로그램이 종료할때 Free를 해주면 됩니다.
■ 선언
MIL_ID Application;
MIL_ID System;
MIL_ID Digitizer;
MIL_ID Display;
MIL_ID Buffer;
■ Allocate
MappAlloc() // Application 할당
MsysAlloc() // 프레임그래버 할당
MdigAlloc() // 카메라 할당.
MdispAlloc() // 디스플레이 할당
MbuffAlloc2d() // Buffer할당
■ Free
마지막으로 프로그램 종료되기 전 Free를 해주면 됩니다. 여기에서 Free는 할당의 역순으로 하면된다.
MbuffFree();
MdispFree();
MdigFree();
MsysFree();
MappFree();
이상이 가장 기본적인 구조입니다. 더 자세한 사항은 다음을 참고하기시길 바랍니다.
CD안에 들어있는 User guide Manual의 28페이지를 참고.
영상을 획득하기 위한 함수로는 MdigGrabContinuous()와 MdigGrab()이 쓰입니다.
■ MdigGrabContinuous( DigId, DestImageBufId );
MdigGrabContinuous() 함수는 하나의 버퍼공간에 계속 데이터를 덮어쓰면서 연속적인 Grab를 합니다.
영상처리를 위한 버퍼로는 거의 쓰이지 않습니다. 영상처리를 위해서라면 아래에 설명되는
MdigGrab() 함수를 쓰기 바랍니다.
MdigGrabContinuous() 함수를 종료하기 위해서는 MdigHalt() 명령어를 써서 종료하여야 한다.
■ MdigGrab( DigId, DestImageBufId )
MdigGrab() 함수는 하나의 Buffer또는 여러개의 Buffer에 영상데이터를 저장시킵니다.
거의 모든 Application에서는 MdigGrab()를 이용합니다.
MdigGrab()을 한번 수행하면 단지 한장의 영상데이터만을 가져오므로, 연속적인 저장을 위해서는
for문이나 while문 Do문 , HookFunction(); Thread를 이용해서 계속적인 호출을 발생시켜야 합니다.
여기서는 카메라로부터 받아들인 데이터를 화면상에 디스플레이 해주는 방법에 대해서 알아보겠습니다.
MdispAlloc(); // 을 이용해서 디스플레이를 할당시켜줍니다.
그리고 가장 중요한 것은 디스플레이 시켜줄곳에 대한 Select를 해주어야 합니다.
1. MdispSelect() // MIL이 제공하는 Tool에 디스플레이
2. MdispSelectWindow(); // user가 원하는 곳에 디스플레이
위의 두가지 중에 하나를 쓰면 되는데, 첫번째것은 Mil에서 제공되는 Display Box에 디스플레이를 해주며,
두번째 것은 User가 원하는 곳에 디스플레이를 해줍니다.
MdispSelectWindow( DisplayId, ImageBufId, ClientWindowHandle );
- 첫번째 인자는 디스플레이 ID, 두번째 인자는 Buffer ID, 세번째는 윈도우 핸들러로 m_hWnd를 쓰면
현재 ClientWindowHandle에 디스플레이 해주며, 유저가 원하는 디스플레이를 원하면 핸들러를 만들어서 그곳의 핸들러를
입력시켜주면 됩니다.
물론, ImageBufId값에 MdigGrab() or MdigGrabContinuous()를 이용하여 넣어주어야 디스플레이가 됩니다.
Mil에서 영상데이터에 접급하는 방법은 2가지 방법, 사용자가 택일해서 사용해야한다
■ MbufGet()
- MbufGet() 명령어는 버퍼를 유저가 지정한 Array에 입력시킬수 있습니다. 유저 Array로 가져온 데이터는 유저가
원하는대로 조작 ( 이진화, Edge추출등.. ) 할 수가 있습니다.
그럼 직접 영상데이터를 Access해 보겠습니다.
BYTE Buffer[480][640];
MdigGrab(DigitizerID, MilImage);
MbufGet2d( MilImage, 0, 0, 640, 480, &Buffer );
이렇게 해주면, MilImage의 버퍼를 Buffer라는 유저Array로 가져올수 있습니다.
그러면, Buffer를 조작할 수 있스빈다. 가령 이진화를 한다면....
for(int i=0; i<480; i++)
{
for(int j=0; j<640; j++)
{
if( Buffer[i][j] > 128 )
Buffer[i][j] = 255;
else
Buffer[i][j] = 0;
}
}
위와같이 해주면 되겠져.
그러나 이렇게만 해준다면 다 끝난게 아닙니다. 변경된 데이터값을 다시 원래 Buffer에 넣어주어야합니다.
그 명령어는 MbufPut() 이라는 명령어입니다. 다음과 같습니다.
MbufPut2d( MilImage, 0, 0, 640, 480, &Buffer );
이렇게만 해주면 완벽한 이진화를 할 수 있습니다. 하지만 단점은 매 영상을 MbufGet를 이용해 얻어와서
처리를 한 다음 다시 MbufPut()를 이용해서 넣어줘야 하므로, 프로세싱 타임이 길어지게된다.
하지만 가장 쉽게 접근할 수 있는 장점도 있다.
■ MbufInquire()
이 명령어는 위의 경우와는 다르게 한번만 적어놓으면, 연결시켜둔 버퍼의 내용이 변경되더라도 자동적으로
갱신되게 됩니다.( 왜냐하면 주소에 직접연결되기 때문 ), 그렇기 때문에 프로세싱 을 하는 시간은 훨씬 효율적.
쓰이는 방법은 다음과 같습니다. 역시 이진화를 예로 들겠습니다.
BYTE * BufferAddress;
MbufAlloc2d( ....., &MilImage); // 주소값을 받아온다.
MbufInquire(MilImage, M_HOST_ADDRESS, &BufferAddress);
이렇게 선언을 해 놓습니다. 그러면 MilImage의 내용이 변경되더라도 자동적으로
BufferAddress는 갱신되게 됩니다.
for(int i=0; i<480; i++)
{
for(int j=0; j<640; j++)
{
if(BufferAddress[ i * 640 + j ] > 128 )
BufferAddress[ i * 640 + j ] = 255;
else
BufferAddress[ i * 640 + j ] = 0;
}
}
이렇게 해주면 이진화는 완벽하게 구현.
그러면 자동적으로 MilImage의 버퍼도 이진화가 되어있을 것입니다. 하지만 그냥 보이는것은 이진화가 되지 않은
영상일 것입니다. 이 부분은 변경되어진 것을 갱신시켜주어야 하는데 그럴때 다음의 명령어를 추가
MbufControl( MilImage, M_MODIFIED, M_DEFAULT );
■ 오버레이 ( Overlay )
오버레이란 원영상에는 영향을 미치지 않고 ( non-destructively ) 윈도우 화면이나 외부화면에 디스플레이 해줄수 있는
일종의 버퍼를 말합니다. 간단하게 설명하면, 원래의 버퍼위에 투명버퍼를 씌운다고 생각하면 쉬울 것.
■ 프로그래밍
1. 먼저 오버레이를 쓸 수 있게끔 해줍니다.
MdispControl( DisplayID, M_WINDOW_OVR_WRITE, M_ENABLE );
2. 디스플레이 하기 위한 버퍼를 선택합니다.
MdispSelect( DisplayID, ImageBufID , MdispSelectWindow();
3. Display할 버퍼와 연결시킬 오버레이 버퍼 인자를 적습니다.
MdispInquire( DisplayID, M_WINDOW_OVR_BUF_ID, &OverlayBufferID );
이렇게 선언을 하고, OverlayBuferID값에 글씨나 그림을 넣어줄 수 있습니다. 그런데, 이 오버레이 버퍼를
clear하려고 하면 ????
검정색이나 흰색으로 clear를 한다면 우리가 보는 영상은 까맣거나, 하얗게 됩니다.
즉 원 영상과 같은 그림으로 clear를 해주어야 하는 것입니다.
이렇게 하기 위해서는 다음과 같이 key칼라를 얻어와서 그것을 이용해 clear를 해준다.
4. MdispInquire( DisplayID, M_KEY_COLOR, &TransparentColor );
5. MbufClear( OverlayBufferID, TransparentColor );
이상입니다.
■ RGB버퍼는 packed나 planer 중에 하나의 방식으로 저장되게 됩니다.
▶ packed : packed는 RGB버퍼의 성분이 다음과 같이 구성되어 있습니다.
즉 BGRBGRBGRBGRB... 이런식으로 같은 속성을 가진 버퍼를 아래와 같은 형식으로
R,G,B를 분리해 낼수 있습니다.
BYTE * GrabAddress;
MbufInquire ( BufferID, M_HOST_ADDRESS, &GrabAddress );
for(int i=0; )
{
for(int j=0; )
{
GrbAddress[ i * ( SizeX ) * 3 + k*3+0 ] // Blue
GrbAddress[ i * ( SizeX ) * 3 + k*3+1 ] // Green
GrbAddress[ i * ( SizeX ) * 3 + k*3+2 ] // Red
}
}
▶ Planner : Planner 는 RGB버퍼의 성분중에 모든 R성분들이 연속적으로 저장되어져 있고, 그 다음에 B성분들이
... 마지막으로 G성분들이 연속적으로 저장되어 있습니다. 아래와 같이 R, G, B를 분리 해 낼 수 있습니다.
즉 다음과 같습니다.
RRRRRRRRRRRRRRRR....BBBBBBBBBBBBBBBB....GGGGGGGGG....
BYTE * GrabAddress;
MbufInquire( BufferID, M_HOST_ADDRESS, &GrabAddress );
for(int i=0; i<480; i++)
{
for(int j=0; j<640; j++)
{
GrabAddress[ i*640 + j + (640* 480 * 0 ) ]; // Blue 성분
GrabAddress[ i*640 + j + (640* 480 * 1 ) ]; // green 성분
GrabAddress[ i*640 + j + (640* 480 * 2 ) ]; // Red 성분
}
}
실시간 영상처리를 위한 하나의 방법으로 Hook-Function을 쓰게 됩니다. 그럼 Hook-Function에 대해서 ...
■ Hook-Function이란?
Hook-Function이란 말 그대로 Digitizer( Frame Grabber ) 로 부터 발생된 Event를 가로채는 함수를 말합니다.
Event로 부터 가로챈 함수는 별개의 Thread로 실행합니다. 이것은 Hook Function이 비동기적으로 실행되게 합니다.
- Hook Function의 발생. Hook Function의 이벤트는 다음과 같은 여러 종류의 이벤트에 따라 적용시킬수 있습니다.
M_GRAB_START ( hook to the start of each grab )
M_GRAB_END( hook to the end of the each grab )
M_GRAB_FRAME_START( hook to the start of grabbed frames )
M_GRAB_FRAME_END( hook tothe end of grabbed frames )
M_GRAB_FIELD_END( hook to the end of grabbed fields)
M_GRAB_FIELD_END_ODD( hook to the end of grabbed odd fields )
M_GRAB_FIELD_END_EVEN ( hook to the end of grabbed even fields )
즉 위와 같은 상황에서 hook event를 얻어올 수 있는 것입니다.
예를들어 M_GRAB_START의 이벤트를 가져오려면 다음과 같이 사용하면 된다.
MdigHookFunction( DigID, M_GRAB_START, HookHandlerPtr, UserDataPtr );
여기서 HookHandlerPtr은 다음과 같은 형식으로 정의하시면 됩니다.
long MFTYPE HookHandler( HookType, EventId, UserDataPtr );
long HookType; // Type of event hooked
MIL_ID EventId; // Event Identifier ( Currently set to null )
void MPTYPE * UserDataPtr; // user Data Pointer
■ 연속적인 Hook Function의 실행.
실시간 처리를 위해서는 Grab를 지속적으로 해주어서.. 여기에서 나오는 Event를 가지고 프로세싱을 해주면 됩니다.
즉, 먼저 MdigGrab() 명령어를 실행시켜서 정의한 이벤트가 발생되면 Hook Function으로 들어가게 됩니다 그리고 Hook Function안에서
처리를 해주고 마지막에 MdigGrab 명령어를 다시 넣어주면 또 다시 MdigGrab의 이벤트를 받아서 처리를 해주게 됩니다.
이런식으로 계속적인 Grab과 프로세싱이 가능합니다.
■ Hook Fucntion의 종료
Hook Fucntion의 종료할때에는 위해서 얻어온 이벤트와 같은 Hook Event + M_UNHOOK 라고 쓰면 됩니다.
MdigHookFuction( DigId, M_GRAB_START+M_UNHOOK, HookHandlerPtr, UserDataPtr );