1、修改窗口背景色或者光标形状
在OpenCV1.0版本利用函数int cvNamedWindow( const char* name, int flags )初始化创建一个窗口后,窗口的背景色是灰色,光标形状是十字线,通过如下方法改变这些窗口属性:
第一种方法是从源头直接修改。(1) 打开OpenCV安装目录下的_make文件夹,使用VC6.0打开opencv.dsw工程,打开文件”…\\highgui\\window_win32.cpp” 找到函数CV_IMPL int cvInitSystem( int, char** ),该函数就是被cvNamedWindow函数调用用来创建窗口的,在该函数中可以看到注册窗口的一些属性(包括窗口背景色是灰色,光标形状是十字线等),只要修改相应属性值即可改变窗口的属性(比如将窗口背景色的属性值改为WHITE_BRUSH就使得窗口背景色为白色,将光标形状属性ID改为IDC_ARROW就使得窗口的光标变成箭头形状)。
(2) 修改了相应属性值后,还需要在VC6.0中选择build | Batch build菜单项,在弹出的对话框中,选择需要重新编译的库,点击Rebuild。这样就生成了修改后的库文件和动态链接库文件。这种方法直接通过修改WNDCLASS参数来达到目的。
第二种方法就是在当前工程中通过调用全局函数来修改。比如要修改窗口背景色可以采用以下示例代码:
cvNamedWindow("Image",1);
HDC dc;
HBRUSH brush;
HGDIOBJ oldgdi;
RECT rc;
dc = ::GetDC((HWND)cvGetWindowHandle("Image"));
brush = ::CreateSolidBrush(RGB(0,0,0));
oldgdi = ::SelectObject(dc,brush);
::GetClientRect((HWND)cvGetWindowHandle("Image"),&rc);
::FillRect(dc,&rc,brush);
::SelectObject(dc,oldgdi);
::DeleteObject(brush);
::ReleaseDC((HWND)cvGetWindowHandle("Image"),dc);
修改窗口光标可以利用以下一对函数:
mycursor = ::LoadCursor(NULL,IDC_ARROW); ::SetCursor(mycursor);
2、窗口消息处理
OpenCV1.0提供了鼠标、键盘、拖动条等常用的事件消息处理机制,对于鼠标和拖动条消息,用户可以自己编写回调函数来实现需要的功能。例如:对于鼠标的消息处理,用户在完成回调函数编写后只需调用OpenCV中的cvSetMouseCallback函数将处理鼠标消息的回调函数地址传给窗口过程函数即可。具体消息处理流程见如下示意图:
仔细观察window_w32.cpp文件中的窗口回调函数WindowProc的定义会发现(如图4所示),该函数在调用HighGUIProc之前和之后还做了2个工作,那就是访问了由函数指针hg_on_preprocess和hg_on_postprocess指向的函数地址空间。如果将用户自定义的回调函数地址赋值给这两个指针同样可以完成窗口消息的处理,正好OpenCV1.0有两个函数可以达到如此目的,这两个函数分别如图2所示在头文件highgui.h中已经作了申明:
这两个函数的实现实例如图3所示:
例如用户想要创建右键式弹出菜单,那么对菜单的响应消息WM_COMMAND在OpenCV封装的HighGUIProc函数中并未处理,此时用户可以自己定义回调函数,然后通过函数cvSetPreprocessFuncWin32或者cvSetPostprocessFuncWin32将用户定义的函数地址赋值给函数指针hg_on_preprocess或者hg_on_postprocess。
最后基于VC6.0创建一个Win32 Console Application程序,实现如下功能:
(1) 创建一个窗口,修改初始化的光标形状并加载一幅图像;
(2) 在图像上通过鼠标绘制绿色矩形框,在鼠标左键按下并移动鼠标时光标形状改变;
(3) 鼠标右键按下创建一个弹出式菜单,并响应菜单条目消息。
实现示例代码如下:
#include <stdio.h> #include "cv.h" #include "highgui.h" #include "opencv_test.h" #define IDM_OTSU 0x0010 #define IDM_JOSE 0x0020 int wndcallback( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, int* was_processed); IplImage* img = 0; IplImage* img1 = 0; CvPoint pt1; CvPoint pt2; POINT pt3; HMENU mymenu; HCURSOR mycursor; HCURSOR precursor; bool isChangCursor = false; int main() { img = cvLoadImage("lena.jpeg"); if (!img) { printf("can not load the image!"); exit(0); } mycursor = ::LoadCursor(NULL,IDC_ARROW); precursor= ::LoadCursor(NULL,IDC_CROSS); cvNamedWindow("Image",1); cvShowImage("Image",img); img1 = cvCloneImage(img); // cvSetPreprocessFuncWin32(wndcallback);//用这个函数时要确保用户定义的回调函数wndcallback的最后一个//参数*was_processed=0,否则窗口过程函数HighGUIProc就不能被调用,此时窗口重绘消息WM_PAINT如果用户没//有处理就不能被处理。 cvSetPostprocessFuncWin32(wndcallback); while(1) { int key = cvWaitKey(5000); if (key == ‘ ‘) break; } printf("menu is destroy : %d \n",::DestroyMenu(mymenu)); printf("cursor is destroy : %d \n",::DestroyCursor(mycursor)); printf("precursor is destroy : %d \n",::DestroyCursor(precursor)); cvReleaseImage(&img); cvReleaseImage(&img1); cvDestroyWindow("Image"); return 0; } int wndcallback( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ,int* was_processed) { if (hwnd == (HWND)cvGetWindowHandle("Image")) { switch (uMsg) { case WM_COMMAND: if (LOWORD(wParam) == IDM_OTSU) printf("otsu !\n"); else if (LOWORD(wParam) == IDM_JOSE) printf("jose !\n"); break; case WM_RBUTTONUP: pt3.x = LOWORD(lParam); pt3.y = HIWORD(lParam); ::ClientToScreen(hwnd,&pt3); mymenu = ::CreatePopupMenu(); ::AppendMenu(mymenu,MF_STRING,IDM_OTSU,"Otsu(&O)"); ::AppendMenu(mymenu,MF_STRING,IDM_JOSE,"Jose(&J)"); ::TrackPopupMenu(mymenu,TPM_LEFTALIGN | TPM_LEFTBUTTON, pt3.x, pt3.y, 0, hwnd, NULL); break; case WM_LBUTTONDOWN: pt1.x = LOWORD(lParam); pt1.y = HIWORD(lParam); break; case WM_LBUTTONUP: isChangCursor = false; break; case WM_MOUSEMOVE: if (MK_LBUTTON == wParam) { isChangCursor = true; ::SetCursor(precursor); pt2.x = LOWORD(lParam); pt2.y = HIWORD(lParam); img = cvCloneImage(img1); cvRectangle(img, pt1, pt2, cvScalar(0,255,0), 3); cvShowImage("Image", img); } break; default: break; } if (!isChangCursor) ::SetCursor(mycursor); } return *was_processed; }