很久没发博客了,趁着近期项目需要,再和大家分享一个小程序,即在OpenCV窗口中创建水平、竖直滚动条来浏览大型图像。如果我们所要显示的图像像素较高,例如1440*900的,而显示器只有 1280*800的分辨率,那么通过cvNamedWindow创建的最大窗口也只能看到部分图像,其余的因为超出窗口大小看不到了,也没有滚动条用来滚动观察其余图像,这样,就不方便我们通过窗口对图像进行交互操作。通常滚动条是在MFC界面上建立的,这方面的例程很多,例如 http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=8349 ;也有使用OpenCV的Trackbar的例子,但Trackbar只有水平滚动、没有垂直方式,而且是按1为步长,使用时不方便、也占用了不少窗口空间。下面这段例程展示了怎样通过 cvSetMouseCallback, cvRect, cvRectangleR, cvResize 等一系列OpenCV函数来创建方便、可随意定制的滚动条。
// Image_ScrollBar.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #include using namespace std; double mx = 0, my = 0; int dx = 0, dy = 0, horizBar_x = 0, vertiBar_y = 0; bool clickVertiBar = false, clickHorizBar = false, needScroll = false; CvRect rect_bar_horiz, rect_bar_verti; void help() { printf( "/n" "This program demonstrated the use of the cvSetMouseCallback /n" "for viewing large image with scroll bar in a small window/n" "created by OpenCV highgui model. (chenyusiyuan, 2011-06-24)/n" "Call:/n" "./Image_ScrollBar [ ]/n/n" ); } void mouse_callback( int event, int x, int y, int flags, void* param ) { if (needScroll) { switch( event ) { case CV_EVENT_LBUTTONDOWN: mx = x, my = y; dx = 0, dy = 0; // 按下左键时光标定位在水平滚动条区域内 if (x >= rect_bar_horiz.x && x <= rect_bar_horiz.x+rect_bar_horiz.width && y >= rect_bar_horiz.y && y<= rect_bar_horiz.y+rect_bar_horiz.height) { clickHorizBar = true; } // 按下左键时光标定位在垂直滚动条区域内 if (x >= rect_bar_verti.x && x <= rect_bar_verti.x+rect_bar_verti.width && y >= rect_bar_verti.y && y<= rect_bar_verti.y+rect_bar_verti.height) { clickVertiBar = true; } break; case CV_EVENT_MOUSEMOVE: if (clickHorizBar) { dx = fabs(x-mx) > 1 ? (int)(x-mx) : 0; dy = 0; } if (clickVertiBar) { dx = 0; dy = fabs(y-my) > 1 ? (int)(y-my) : 0; } mx = x, my = y; break; case CV_EVENT_LBUTTONUP: mx = x, my = y; dx = 0, dy = 0; clickHorizBar = false; clickVertiBar = false; break; default: dx = 0, dy = 0; break; } } } void myShowImageScroll(char* title, IplImage* src_img, int winWidth = 1400, int winHeight = 700) // 显示窗口大小默认为 1400×700 { IplImage* dst_img; CvRect rect_dst, // 窗口中有效的图像显示区域 rect_src; // 窗口图像对应于源图像中的区域 int imgWidth = src_img->width, imgHeight = src_img->height, barWidth = 25; // 滚动条的宽度(像素) double scale_w = (double)imgWidth/(double)winWidth, // 源图像与窗口的宽度比值 scale_h = (double)imgHeight/(double)winHeight; // 源图像与窗口的高度比值 if(scale_w<1) winWidth = imgWidth+barWidth; if(scale_h<1) winHeight = imgHeight+barWidth; int showWidth = winWidth, showHeight = winHeight; // rect_dst 的宽和高 int src_x = 0, src_y = 0; // 源图像中 rect_src 的左上角位置 int horizBar_width = 0, horizBar_height = 0, vertiBar_width = 0, vertiBar_height = 0; needScroll = scale_w>1.0 || scale_h>1.0 ? TRUE : FALSE; // 若图像大于设定的窗口大小,则显示滚动条 if(needScroll) { dst_img = cvCreateImage(cvSize(winWidth, winHeight),src_img->depth, src_img->nChannels); cvZero(dst_img); // 源图像宽度大于窗口宽度,则显示水平滚动条 if(scale_w > 1.0) { showHeight = winHeight - barWidth; horizBar_width = (int)((double)winWidth/scale_w); horizBar_height = winHeight-showHeight; horizBar_x = min( max(0,horizBar_x+dx), winWidth-horizBar_width); rect_bar_horiz = cvRect( horizBar_x, showHeight+1, horizBar_width, horizBar_height); // 显示水平滚动条 cvRectangleR(dst_img, rect_bar_horiz, cvScalarAll(255), -1); } // 源图像高度大于窗口高度,则显示垂直滚动条 if(scale_h > 1.0) { showWidth = winWidth - barWidth; vertiBar_width = winWidth-showWidth; vertiBar_height = (int)((double)winHeight/scale_h); vertiBar_y = min( max(0,vertiBar_y+dy), winHeight-vertiBar_height); rect_bar_verti = cvRect( showWidth+1, vertiBar_y, vertiBar_width, vertiBar_height); // 显示垂直滚动条 cvRectangleR(dst_img, rect_bar_verti, cvScalarAll(255), -1); } showWidth = min(showWidth,imgWidth); showHeight = min(showHeight,imgHeight); // 设置窗口显示区的 ROI rect_dst = cvRect(0, 0, showWidth, showHeight); cvSetImageROI(dst_img, rect_dst); // 设置源图像的 ROI src_x = (int)((double)horizBar_x*scale_w); src_y = (int)((double)vertiBar_y*scale_h); src_x = min(src_x, imgWidth-showWidth); src_y = min(src_y, imgHeight-showHeight); rect_src = cvRect(src_x, src_y, showWidth, showHeight); cvSetImageROI(src_img, rect_src); // 将源图像内容复制到窗口显示区 cvCopy(src_img, dst_img); cvResetImageROI(dst_img); cvResetImageROI(src_img); // 显示图像和滚动条 cvShowImage(title,dst_img); cvReleaseImage(&dst_img); } // 源图像小于设定窗口,则直接显示图像,无滚动条 else { cvShowImage(title, src_img); } } int main(int argc, char** argv) { help(); const char* filename = argc > 1 ? argv[1] : "im.jpg"; int width = 1400, height = 700; if (4==argc) { sscanf( argv[2], "%u", &width ); sscanf( argv[3], "%u", &height ); } cvNamedWindow("Image Scroll Bar", 1); cvSetMouseCallback("Image Scroll Bar", mouse_callback); IplImage* image = cvLoadImage( filename, CV_LOAD_IMAGE_COLOR ); if( !image ) { fprintf( stderr, "Can not load %s and/or %s/n" "Usage: Image_ScrollBar []/n", filename ); exit(-1); } while(1) { myShowImageScroll("Image Scroll Bar", image, width, height); int KEY = cvWaitKey(10); if( (char) KEY == 27 ) break; } cvDestroyWindow("Image Scroll Bar"); return 0; }
程序还只是实现了比较初步的功能,只能通过鼠标拖曳滚动条来浏览图像,不能通过鼠标中键滚轮实现上下滚动,这个主要是 cvSetMouseCallback 里的 Event 不包括滚轮滑动事件,有空再探索下解决办法。欢迎大家一起交流讨论。