最近老师,让我做一下关于视频处理方面的一个项目,在实时处理这里实在是卡住了太长时间,因为不知道如何使用多线程来进行实时检测,终于有点眉目,来写个笔记记录一下。
首先需要介绍一下关于项目的背景,做一个人脸检测系统,通过摄像头实时的进行检测。
刚开始,使用settimer实现,但效果视频流畅度不好。
最后使用多线程来解决这个问题。多线程的作用和定义的就不再赘述了。
介绍两个线程函数
在MFC中实现多线程有两个函数,一个是AfxBeginThread 一个是 CreateThread
HANDLE WINAPI CreateThread(
_in LPSECURITY_ATTRIBUTES lpThreadAttributes,
_in SIZE_T dwStackSize,
_in LPTHREAD_START_ROUTINE lpStartAddress,
_in LPVOID lpParameter,
_in DWORD dwCreationFlags,
_out LPDWORD lpThreadId
);
参数:
lpThreadAttributes: 指向一个LPSECURITY_ATTRIBUTES结构的指针决定返回的句柄能否被继承,如果lpThreadAttributes为空,这个句柄不能被继承。
sdStackSize:初始化的堆栈大小,以字节为单位。如果为0,使用默认的大小,即使用和当先线程一样大的堆栈大小。
lpStartAddress:函数的入口地址,一般为线程函数名。
lpParameter:一个参数指针,被传递到线程函数里。
dwCreationFlags:线程创建的标志。如果为CREATE_SUSPENDED这个标志,那么需要使用ResumeThread函数来激活线程函数,如果为0,线程函数立刻执行。
IpThreadId:一个指向线程id的指针,如果为空,线程id不被返回。
如果函数成功执行,返回值将是这个新线程的句柄。如果失败,返回值是NULL。
如果线程函数return,返回值会隐式条用ExitThread函数,可以使用GetExitCodeThread函数获得该线程函数的返回值。
系统中的线程对象一直存活到线程结束,并且所有指向它的句柄都需要通过调用CloseHandle关闭。
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = ,
DWORD dwCreateFlags = ,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = ,
DWORD dwCreateFlags = ,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
参数:
pfnThreadProc:线程函数的入口地址。
函数原型:UINT __cdecl MyControllingFunction( LPVOID pParam );
pThreadClass:继承CWinThread类的RUNTIME_CLASS对象。
pParam: 传递给线程函数的参数,可以为0。
nPriority:线程优先级。
nStackSize:指明线程堆栈的大小,以字节为单位,可以为0。
dwCreateFlags:线程创建标志。
lpSecurityAttrs:线程安全属性。
这两种之间的区别
CreateThread是Windows的API函数,提供操作系统级别的创建线程的操作,这个函数即是操作系统提供的接口。
CreateThread的线程函数形式为 DWORD WINAPI ThreadFun(LPVOID pParam)
AfxBeginThread则是编译器对它的封装
CreateThread的线程函数形式为 UINT ThreadFun(LPVOID pParam)
要在线程中对图片控件picturecontrol进行操作需要如下所示
CWinThread* mythread = AfxBeginThread((AFX_THREADPROC)ThreadFun,this,THREAD_PRIORITY_NORMAL,0,0,NULL);
将this作为参数传到线程函数中使用,这样即可通过下面两行代码得到一个dlg的指针,通过它来进行操作一些类的成员函数
HWND hwnd=(HWND)pParam;
CcaptureDlg *pMainDlg = (CcaptureDlg*)(hwnd);
CDC* pDC=(CDC*)pMainDlg->GetDlgItem(nID)->GetDC();
HDC hDC=pDC->GetSafeHdc ();
CRect rect;
pMainDlg->GetDlgItem(nID)->GetClientRect (&rect);
CvvImage images;
images.CopyOf(iplimg);
images.DrawToHDC (hDC,rect);
images.Destroy();
pMainDlg->ReleaseDC(pDC);
对于终止进程,通过查阅资料,了解到对于进程,不能直接暴力地终止它,这里我才用的是发送一个消息,来使进程结束运行
这里我使用的函数为:PostThreadMessage()
BOOL WINAPI PostThreadMessage(
_In_ DWORD idThread,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
idThread 为线程id
Msg 指定发送的消息,windows常用消息含义
wParam 消息的附加信息
lParam 消息的附加信息
if(::PostThreadMessage(mythread->m_nThreadID,STOP_THREAD,0,0))
{
WaitForSingleObject(mythread, INFINITE );//等待进程状态发生变化
}//结束进程
注意:调用GetLastError可以查看出错信息,而且 这里的STOP_THREAD不能是0—VM_USER-1范围内的数字,设置为这些数时,会与系统预定义发生冲突,可以设为VM_USER+1
接收消息函数使用PeekMessage()函数,
BOOL WINAPI PeekMessage(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax,
_In_ UINT wRemoveMsg
);
lpMsg 为指向MSG结构的指针,用于存放消息
hWnd 指的是需要获取消息的窗口的句柄,该窗口必须属于当前线程。当其值是 NULL 时,将获取所有的当前线程的窗口消息和线程消息,当其值是 -1 时,只获取当前线程消息
wMsgFilterMin 指定被可以被获取的消息值的最小整数(消息其实就是一个被定义的整数)
wMsgFilterMax 指定被可以被获取的消息值的最小整数
wRemoveMsg
一是指定消息如何被处理(该值可以是下边一个或多个组合):
值 含义
PM_NOREMOVE 消息被获取后不从消息队列中删除
PM_REMOVE 消息被获取后并从消息队列中删除
PM_NOYIELD 1. 防止系统释放任何正在等待被调用的线程 2. 跟 PM_NOREMOVE 或 PM_REMOVE 相结合使用
二是默认设置下处理所有类型的消息,若要求只处理某些消息,则指定下列一个或多个组合:
值 含义
PM_QS_INPUT 处理鼠标和键盘消息
PM_QS_PAINT 处理绘图消息
PM_QS_POSTMESSAGE 处理所有 posted 的消息,包括计时器和快捷键消息
PM_QS_SENDMESSAGE 处理所有 send 的消息
如果PeekMessage获取到信息则返回值非零,获取不到则为零
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message==STOP_THREAD)
{
return 0;
}
else{
DispatchMessage(&msg);//分发消息
}
}//不断地检测消息队列是否有消息
通过这个发消息,收消息的过程即可结束进程