OpenCV图像窗口滚动条实现

如果要在OpenCV窗口是显示大图,OpenCV的namedWindow是不提供滚动条的,当然可以用CV_WINDOW_AUTOSIZE参数把图像缩小到指定窗口中,但这种处理使用了resize函数,窗口大小设置不合适就会产生失真(当然还可以加上参数CV_WINDOW_KEEPRATIO来固定长宽比),有时候希望在窗口中显示原图,这就需要自己画出滚动条,然后用onMouse事件来控制图像显示。

源码如下,主体是网上流行的,但里面的一些函数是低版本的OpenCV的(如CvRect、CvRectangleR、cvSetImageROI等等),我把它们改成了高版本的函数(如Rect, rectangle,删除cvSetImageROI),这样做是为了使用Mat结构体能自己释放内存的能力。

(以下代码在VS2015+OpenCV3.3上实现)


// Image_ScrollBar.cpp : Defines the entry point for the console application.  
//  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc_c.h>  

#include <iostream>  
#include <vector>  

using namespace std;  
using namespace cv;

double mx = 0, my = 0;  
int dx = 0, dy = 0, horizBar_x = 0, vertiBar_y = 0;  
bool clickVertiBar = false, clickHorizBar = false, needScroll = false;  
Rect 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 [<img_filename default im.jpg> <window_width default 1400> <window_height default 700>]/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, Mat src_img)  
{  
    Mat dst_img, dstROI;
	Rect  rect_dst,   // 窗口中有效的图像显示区域  
		rect_src;   // 窗口图像对应于源图像中的区域  
	int imgWidth = img.cols,
		imgHeight = img.rows,
		barWidth = 20;  // 滚动条的宽度(像素)  


	CRect rect;
	int winWidth =  1400;
	int winHeight =  900;
	double  scale_w = (double)imgWidth / (double)winWidth,    // 源图像与窗口的宽度比值  用以判断是否超出显示范围
		scale_h = (double)imgHeight / (double)winHeight;            // 源图像与窗口的高度比值  用以判断是否超出显示范围


	if (scale_w<1)                                                                     //如果小于1 说明原图比窗口小,窗口的宽度将重新赋值
		winWidth = imgWidth + barWidth;
	if (scale_h<1)                                                                     //如果小于1 说明原图比窗口小,窗口的高度将重新赋值
		winHeight = imgHeight + barWidth;


	int showWidth = winWidth, showHeight = winHeight; // 窗口中有效的图像显示区域的宽和高  
	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 = Mat(Size(winWidth, winHeight), img.type());


		// 源图像宽度大于窗口宽度,则显示水平滚动条  
		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 = Rect(
				horizBar_x,
				showHeight + 1,
				horizBar_width,
				horizBar_height);
			// 显示水平滚动条  
			rectangle(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 = Rect(
				showWidth + 1,
				vertiBar_y,
				vertiBar_width,
				vertiBar_height);   //确定垂直滚动条的白色部分的大小
									// 显示垂直滚动条  
			rectangle(dst_img, rect_bar_verti, cvScalarAll(255), -1);
		}


		showWidth = min(showWidth, imgWidth);
		showHeight = min(showHeight, imgHeight);


		// 设置窗口显示区的 ROI  
		rect_dst = Rect(0, 0, showWidth, showHeight);
		dstROI = 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 = Rect(src_x, src_y, showWidth, showHeight);
		// 将源图像内容复制到窗口显示区  


		img(rect_src).convertTo(dstROI, dstROI.type());


		// 显示图像和滚动条  
		imshow(winName, dst_img);
	}
	// 源图像小于设定窗口,则直接显示图像,无滚动条  
	else
	{
		imshow(winName, img);
	}
}  

int main(int argc, char** argv)  
{  
    help();  
    const char* filename = argc > 1 ? argv[1] : "1.bmp";  
    int width = 1200, height = 800;  
    if (4==argc)  
    {  
        sscanf( argv[2], "%u", &width );  
        sscanf( argv[3], "%u", &height );  
    }  

    namedWindow("Image Scroll Bar", 1);  
    setMouseCallback("Image Scroll Bar", mouse_callback);  

    Mat image = imread( filename);  
    if( !image )  
    {  
        fprintf( stderr, "Can not load %s and/or %s/n"  
            "Usage: Image_ScrollBar [<img_filename default im.jpg>]/n",  
            filename );  
        exit(-1);  
    }  

    while(1)  
    {  
        myShowImageScroll("Image Scroll Bar", image);  

        int KEY = cvWaitKey(10);  
        if( (char) KEY == 27 )  
            break;  

    }

    return 0;  
}  


其中最关键的是myShowImageScroll函数中源图像ROI区域的拷贝。

另外,这个方法只适用于用OpenCV自己的窗口显示图像,因为滚动条是画出来的,所以要在while中不停的调用,如果要在MFC框架上显示,最好还是在View是想办法,因为CScrollView直接提供滚动条。

MFC框架下OpenCV显示大图(CScrollView滚动条)已经实现,等有时间了把方法总结一下。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值