遥感图像处理时少不了ENVI,用过ENVI的人都知道,打开图像时或图像处理完后,在缩略图上移动鼠标时,鼠标周围的图像信息会在大的视图中实时的显示,即大图会跟着小图中的鼠标移动,这即是图像的连动效果。如下图是ENVI的示意图:
在软件设计中,要实现类似ENVI中连动的效果,确实要费一番脑筋。
要求:有两幅图,一个是缩略图,一个是原图或处理后的图,使用两个对话框,鼠标在小的缩略图中移动或点击时,大图中的图像实时显示出鼠标所在位置的图像。
结果展示:首先给出结果展示,这样读者也好明白实现这样的功能入手点和重点在那些地方。
从上图中可以看出:1.大图在移动时其实只需要控制滑动杆的位置即可,控制滑动杆的位置即设置水平和垂直两个滑动轴的值大小;
2.需要检测出鼠标在缩略图中的滑动和点击事件
再联系到使用QT实现时,可以使用信号与槽将这两者联系起来,两幅图对应两个对话框,使用两个类分别实现对话框的功能。鼠标在缩略图中点击或移动时,实时记录鼠标所在位置,并触发信号,大图的槽函数响应缩略图对话框发过来的信号,设置滑动轴的值。
坐标值与滑动轴值之间的联系:需要确定鼠标在缩略图中的位置,x长度占缩略图宽度的百分比,y长度占缩略图长度的百分比,然后这两个值传递给大图对话框的类对象,根据这两个百分比和大图图像的宽和高设置滑动杆的值。
缩略图对话框类为SmallImage,大图对话框类为ImgShow
首先是在SmallImage类中重写鼠标事件,缩略图图像显示区大小固定为300*300
1 //smallimage.h 2 #ifndef SMALLIMAGE_H 3 #define SMALLIMAGE_H 4 5 #include <QWidget> 6 #include <QMouseEvent> 7 #include "ui_smallimage.h" 8 9 class SmallImage : public QWidget 10 { 11 Q_OBJECT 12 13 public: 14 SmallImage(QWidget *parent = 0); 15 ~SmallImage(); 16 17 void show_small_img(float *data,int width,int height);//显示缩略图图像 18 19 float x_value;//鼠标点所在位置占x轴的百分比 20 float y_value;//鼠标点所在位置占y轴的百分比 21 22 signals: 23 void press(float x,float y); 24 25 private: 26 Ui::SmallImage ui; 27 28 void mousePressEvent(QMouseEvent *ev);//重写鼠标按下事件 29 void mouseMoveEvent(QMouseEvent *ev);//重写鼠标移动事件 30 31 int img_width_;//缩略图宽 32 int img_height_;//缩略图高 33 34 }; 35 36 #endif // SMALLIMAGE_H 37 38 39 //smallimage.cpp 40 //重写鼠标按下事件 41 void SmallImage::mousePressEvent(QMouseEvent *ev) 42 { 43 int x1=ui.label->x();//x坐标 44 int y1=ui.label->y();//y坐标 45 int x_left=x1+(300-img_width_)/2;//缩略图左起x坐标 46 int x_right=x1+img_width_+(300-img_width_)/2;//缩略图右边的x坐标 47 int y_top=y1+(300-img_height_)/2;//缩略图上起y坐标 48 int y_bottom=y1+img_height_+(300-img_height_)/2;//缩略图底部y坐标 49 int x0=ev->x(); 50 int y0=ev->y(); 51 if ( (x0>=x_left) && (x0<=x_right) && (y0>=y_top) && (y0<=y_bottom)) 52 { 53 x_value=(float)(x0-x_left)/(float)img_width_; 54 y_value=(float)(y0-y_top)/(float)img_height_; 55 //触发鼠标按下信号 56 emit press(x_value,y_value); 57 } 58 } 59 60 //鼠标移动操作 61 void SmallImage::mouseMoveEvent(QMouseEvent *ev) 62 { 63 int x1=ui.label->x();//x坐标 64 int y1=ui.label->y();//y坐标 65 int x_left=x1+(300-img_width_)/2;//缩略图左起x坐标 66 int x_right=x1+img_width_+(300-img_width_)/2;//缩略图右边的x坐标 67 int y_top=y1+(300-img_height_)/2;//缩略图上起y坐标 68 int y_bottom=y1+img_height_+(300-img_height_)/2;//缩略图底部y坐标 69 int x0=ev->x(); 70 int y0=ev->y(); 71 if ( (x0>=x_left) && (x0<=x_right) && (y0>=y_top) && (y0<=y_bottom)) 72 { 73 x_value=(float)(x0-x_left)/(float)img_width_; 74 y_value=(float)(y0-y_top)/(float)img_height_; 75 emit press(x_value,y_value); 76 } 77 }
然后是在ImgShow类中定义槽函数
1 //imgshow.h 2 public slots: 3 void set_scroll_value(float x0,float y0);//设置滑动杆的值 4 5 //imgshow.cpp 6 //设置滑动杆的值 7 void ImgShow::set_scroll_value(float x0, float y0) 8 { 9 ui.scrollArea->horizontalScrollBar()->setValue(x0*img_width_- 10 11 (ui.scrollArea->width())/2); 12 ui.scrollArea->verticalScrollBar()->setValue(y0*img_height_- 13 14 (ui.scrollArea->height())/2); 15 }
最后是当着两个对话框均显示出来之后将他们的信号与槽连接起来。
connect(small_img,SIGNAL(press(float,float)),img_show,SLOT(set_scroll_value(float,float)))
还有相当重要的一点就是,两个对话框必须都是非模态,关于非模态的内容请见:http://blog.csdn.net/luo_klt/article/details/8826975
本文中忽略了图像读取及显示等操作,在本人博客中的其他文章中有详细叙述,详见:
http://www.cnblogs.com/Romi/archive/2012/03/14/2396627.html
http://www.cnblogs.com/Romi/archive/2012/03/14/2396533.html
http://www.cnblogs.com/Romi/archive/2012/03/29/2424073.html
最后说一下,此文论述的方法是通过移动滑动杆(即改变滑动杆的值)来实现图像连动的,该文中的大图是显示的全部图像而非区域图像。
当只需要对区域进行处理而非全部图像时,这时上述方法便不起作用了,这时有一个简单的方法是固定选取鼠标在缩略图中周围的一小块数据,将它全部处理然后显示在大图上,每当鼠标移动或点击时都进行选取区域处理然后显示的执行过程。因为固定选取的是一小块区域因此处理起来速度也快。
倘若不是上面说的两种情况而是选取一块感兴趣区域时,这时就更加麻烦了,但回头一想,选择感兴趣区域区域大还好区域小的话,连动还有什么意义呢,况且选择的都不会是大的区域。有一个办法是先把选择的区域通过第三方软件截下来,然后使用文中的方法对截下来保存的图像数据进行处理并显示。