Di bagian pertama, kita sudah mencoba menampilkan gambar pada Picture Box MFC dengan menggunakan OpenCV library. Kali ini kami akan membahas bagaimana menampilkan video (baik dari file atau kamera) untuk aplikasi MFC. Pada dasarnya, menampilkan video sama saja dengan menampilkan gambar secara berurutan. Ada proses “looping” yang terjadi di dalamnya, dimulai dari mengambil satu frame, pemrosesan, dan penampilan frame secara berulang-ulang.
Pada pemrograman console, dengan mudah kita dapat menampilkan video dengan program berikut:
// ...
// Initialization
cv::namedWindow("Display",1);
cv::VideoCapture cap(video_path);
if(!cap.isOpened())
return -1;
cv::Mat img;
// ...
// Looping, frame by frame
for(;;){
cap >> img;
if(img.empty())
break;
cv::imshow("Display",img);
int k = cv::waitKey(1);
if(k == 'q')
break;
}
// ...
Bagaimana halnya dengan MFC?
Bayangkan kita membuat sebuah program dengan GUI, ketika kita menekan tombol “Play” maka video akan berjalan, dan ketika tombol “Stop” ditekan maka video akan berhenti. Adalah sebuah hal yang “cukup konyol” ketika kita menempatkan “looping” pada program console di atas ke dalam tombol “Play”, yang artinya program tidak bisa melakukan hal lain selain menunggu video selesai dijalankan.
Ada banyak cara untuk membuat proses looping frame-by-frame secara otomatis. Di sini kami mencoba menawarkan satu strategi, yaitu dengan menggunakan thread. Pada dasarnya kita akan membuat sebuah non-blocking thread yang di dalamnya berisi program untuk membaca tiap frame.
hThread = (HANDLE)_beginthreadex( NULL, 0, processImages,
reinterpret_cast<LPVOID>(this), 0, &threadID );
Program pembacaan frame video yang ter”automasi” ini kita letakkan pada fungsi processImages.
unsigned __stdcall processImages( void *g) {
ImageProcessor *proc= reinterpret_cast<ImageProcessor *>(g);
while (!proc->isStopped()) {
if(proc->capture.isOpened()) {
proc->capture >> proc->img;
// ...
// TODO : processing image here
// ...
// displays image
if(proc->isDisplayed()) {
cv::imshow("Display",proc->img);
}
} else {
proc->stopIt();
}
}
_endthreadex( 0 );
return 0;
}
Untuk memastikan agar thread yang telah kita buat terproteksi dari multiple access, kita dapat menggunakan mutex. “Putting it together”, kita dapat membuat sebuah class untuk meng-handle pekerjaan di atas sebagai berikut
class ImageProcessor {
friend unsigned __stdcall processImages( void *);
public:
// Object attributes
DWORD delay;
cv::Mat img;
cv::VideoCapture capture;
bool display;
bool stop;
HANDLE hThread;
HANDLE mut;
unsigned threadID;
public:
// Constructor
ImageProcessor() : display(true), delay(0), stop(false), hThread(0)
{
mut= CreateMutex(NULL, FALSE, NULL);
}
// Destructor
~ImageProcessor()
{
stopIt();
if (hThread != 0)
CloseHandle( hThread );
CloseHandle(mut);
}
// stop the capture
void stopIt()
{
WaitForSingleObject(mut, INFINITE);
stop= true;
ReleaseMutex(mut);
}
bool isStopped()
{
bool c;
WaitForSingleObject(mut, INFINITE);
c= stop;
ReleaseMutex(mut);
return c;
}
bool isDisplayed()
{
bool d;
WaitForSingleObject(mut, INFINITE);
d= display;
ReleaseMutex(mut);
return d;
}
// grab and process the frame sequence
bool run()
{
// destroy any previoulsy created thread
if (hThread != 0)
CloseHandle( hThread );
stop= false;
// start the thread
hThread = (HANDLE)_beginthreadex( NULL, 0,processImages,reinterpret_cast<LPVOID>(this), 0, &threadID );
return true;
}
};
Selanjutnya, kita tinggal menginisialisasi class di atas pada MFC Dialog
// ...
cv::namedWindow("Display",CV_WINDOW_AUTOSIZE);
HWND hWnd = (HWND) cvGetWindowHandle("Display");
HWND hParent = ::GetParent(hWnd);
::SetParent(hWnd, GetDlgItem(IDC_STATIC)->m_hWnd); // IDC_STATIC is the handle of Picture Box
::ShowWindow(hParent, SW_HIDE);
// ...
improc = new ImageProcessor();
improc->capture = cv::VideoCapture(video_path);
if(!improc->capture.isOpened()){
return FALSE;
}
// ...
Terakhir, fungsi untuk menjalankan video dapat dipanggil ketika tombol “Play” ditekan
// ...
void CopencvMFCDlg::OnBnClickedPlay()
{
improc->run();
}
// ...
Cukup mudah bukan?
