一个量测同名点的程序是图像几何定向的必要准备,为此我们开发了ImageMeasuringApparatus(图1),它是一款用来进行双像立体量测的程序,其功能有:
1、 影像对的显示和漫游;
2、 显示缩略图,进行手动粗定位;
3、 载入已量测点、手动亚像素级刺点。
该程序源代码(VC++)和详细的开发说明文档(.doc)已经添加在了本人的资源里,可以下载查阅。
图 1. ImageMeasuringApparatus运行界面
下面我们阐述改程序的开发方法,通过学习本方法或许有助于加强对于MFC多视图图形界面程序开发的掌握:
本程序是基于MFC的单文档应用程序,布局上采用分割窗口形式。其中涉及的主要的类有CMainFrame,CImageMeasuringApparatusDoc,CMeasuringView,CPhotoView以及CPhotoView2,另外还有一些对话框用来接收参数,原理简单此处不予介绍。
CMainFrame类主要实现了主框架窗口的分割,分割方式如图2所示。
CImageMeasuringApparatusDoc 类用来存储影像和量测点数据。
CMeasuringView类用以显示缩略图,微调视图,并响应用户微调操作。
CPhotoView和CPhotoView2类用以显示原始影像,相应用户漫游刺点等操作。
图 2 窗口分割示意图
下面通过操作实例来分析各类的关系:
1、 打开影像,显示,漫游及粗定位。
①点击菜单或工具栏打开按钮,CImageMeasuringApparatusDoc类响应该消息,打开文件对话框接收用户输入的文件路径,利用OpenCV读取左右影像数据保存于m_img_l和m_img_r,并分别转换备份一个三通道影像m_l和m_r,以便后续处理。刷新视图。
②在CPhotoView和CPhotoView2的Ondraw函数中显示CImageMeasuringApparatusDoc保存的影像,注意转换坐标将其显示到视图中心。
③点击,就转换CMainFrame中保存的布尔型变量flag_drag的值为true,在CPhotoView和CPhotoView2中添加鼠标按下,拖放和释放的消息响应函数,在CMainFrame的flag_drag为true的情况下处理用户的漫游影像操作。
④在CMeasuringView的缩略图上按下左键,要将原始影像粗定位到相应位置。为此,在其LButtonDown中将按下点位置坐标转到原始影像,设置CPhotoView或CPhotoView2的offset变量,刷新视图,实现定位。
2、 载入像点,显示像点。
①点击菜单或工具栏载入像点按钮,CImageMeasuringApparatusDoc类响应该消息,打开文件对话框接收用户输入的文件路径,读取左右影像像点数据保存在m_pt_l,m_pt_r,同时保存点数m_ptl_num和m_ptr_num。
②在CPhotoView ,CPhotoView2和CMeasuringView的m_overview_l和m_overview_r控件上实现显示像点的功能。首先像点坐标转换到这三个视图的坐标,然后在该坐标处画十字丝即可。
3、 手动刺点,微调,保存所刺像点。
①点击刺点按钮,就转换CMainFrame中保存的布尔型变量flag_cross的值为true,在CPhotoView和CPhotoView2中添加鼠标按下,拖放和释放的消息响应函数,在CMainFrame的flag_drag为true的情况下处理用户的刺点操作。
②一旦触发刺点操作,就从原始影像上汲取触发点处为中心31*31范围的像素值保存在CMeasuringView的m_ptl_lpBits和m_ptr_lpBits中。
③CMeasuringView将m_ptl_lpBits和m_ptr_lpBits像素块显示在微调视图上,当用户点击微调按钮时调整offset_l和offset_r值,重绘微调视图上的图像。
④量点完毕,按下空格键时弹出输入点号对话框,输入点号后保存该点对于CImageMeasuringApparatusDoc的m_pt_l和m_pt_r.
⑤点击保存按钮,将保存CImageMeasuringApparatusDoc的m_pt_l和m_pt_r与当前文件夹下。
图 3. 类视图
以下是上述几个类的详细说明:
1 CMainFrame
成员变量:
CSplitterWnd m_wndSplitter1;//窗口分割器1
CSplitterWnd m_wndSplitter2;//窗口分割器2
bool m_flag_drag;//是否按下拖动工具
bool m_flag_cross;//是否按下刺点工具
成员函数:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
//分割窗口,分别是CPhotoView,CphotoView2,CMeasuringView类型窗口。
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//设置窗口大小,取消最大化按钮
afx_msg void OnBtnDrag();
//按下拖动按钮的响应函数,设置变量m_flag_drag为true
afx_msg void OnUpdateBtnDrag(CCmdUI* pCmdUI);
//设置工具按钮激活显示状态
afx_msg void OnBtnCross();
//按下刺点按钮的响应函数,设置变量m_flag_cross为true
afx_msg void OnUpdateBtnCross(CCmdUI* pCmdUI);
//设置工具按钮激活显示状态
2 CImageMeasuringApparatusDoc
成员变量:
CvvImage m_img_l;//原始左影像
CvvImage m_img_r; //原始右影像
IplImage *m_l; //3波段左影像
IplImage *m_r; //3波段右影像
IMAPOINT m_pt_l[5000]; //左影像量测点
IMAPOINT m_pt_r[5000]; //右影像量测点
int m_ptl_num;//左影像量测点数
int m_ptr_num; //右影像量测点数
成员函数:
afx_msg void OnFileOpen();
//打开影像得到m_img_l与m_img_r,然后将其转换为三波段的m_l与m_r,方便实现像点量测时精细调节视图显示。
afx_msg void OnOpenPoints();
//打开已量测点文件,保存左右影像量测点数以及像点坐标
afx_msg void OnBtnSavept();
//保存量测的点至当前文件
typedef struct
{
int no;
float x;
float y;
}IMAPOINT;
3 CMeasuringView
成员变量:
CStatic m_point_r; //右影像微调视图控件
CStatic m_point_l; //左影像微调视图控件
CStatic m_overview_r; //右影像缩略视图控件
CStatic m_overview_l; //左影像缩略视图控件
double zoom_l,zoom_r; //缩略图放缩比例
unsigned char *m_ptl_lpBits;//当前所刺点块像素值
unsigned char *m_ptr_lpBits;//当前所刺点块像素值
CPoint offset_l;//微调偏移量
CPoint offset_r;//微调偏移量
成员函数:
virtual void OnDraw(CDC* pDC);
//从Doc中读取影像数据,计算并保存缩略图放缩比例,在缩略图中画出左右影像缩略图,
//从Doc中读取像点数据,根据缩略图显示方式及放缩比例换算到缩略图并画出十字丝显示
//在微调视图中画出十字丝
void AddPoints();
//向Doc中保存量测点的数组m_pt_l以及m_pt_r加入新量测的点
//m_pt_l以及m_pt_r由CPhotoView和CPhotoView2中m_pt以及CMeasuringView中的//offset_l核offset_r求和而得到。
void DrawPoints(CDC *pDC, int flag);
//在左右缩略图上画出所有像点,为pDC传入m_overview_l以及m_overview_r对应的DC,
//flag相应为LEFT及RIGHT
void DrawRedCross();
//画出像点微调视图的中心十字丝
void DrawPointView(int flag);
//在微调视图中画出所刺的像点块,参数flag为LEFT或RIGHT. 利用API函数//StretchDIBits实现该功能,图像数据块为成员变量m_ptl_lpBits和m_ptr_lpBits。为了实//现微调功能,不能直接在m_point_l和m_point_r控件的pDC上画小影像块,而要先利用//兼容DC画出放大的影像并根据微调量移动到合适的位置,最后将其利用BitBlt直接复制//到原来的控件DC中即可。
//要注意的是图像数据块的行宽必需为4的整数倍。
virtual BOOL PreTranslateMessage(MSG* pMsg);
//为了响应空格键按下时的消息,以便输入点号,重载该函数,在其中调用AddPoints()
afx_msg void OnBtnLl();
afx_msg void OnBtnLd();
afx_msg void OnBtnLr();
afx_msg void OnBtnLu();
afx_msg void OnBtnRd();
afx_msg void OnBtnRl();
afx_msg void OnBtnRr();
afx_msg void OnBtnRu();
//响应微调按钮操作,微调小影像快并重绘
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//在缩略视图中点击图像上某点,就把CPhotoView和CPhotoView2移动到相应的位置
4 CPhotoView
成员变量:
CPoint offset;//大影像偏移量
CPoint m_pt;//当前所刺的点
CPoint oriPoint;//开始拖动影像时鼠标坐标
bool m_flag_draging;//是否正在拖动影像
成员函数:
virtual BOOL PreTranslateMessage(MSG* pMsg);
//为了响应空格键按下时的消息,以便输入点号,重载该函数,在其中调用CMeasuring类//的成员函数AddPoints()
virtual void OnDraw(CDC* pDC);
//将打开的左影像显示在窗口中心,采用双缓存方式:先用兼容DC显示然后直接复制
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//左键按下时如果当前激活工具为拖动,则记录初始移动点位坐标,并捕获鼠标
//左键按下时如果当前激活工具为刺点,则利用GetPtBits(CPoint point, unsigned char *lpBits)
//获取当前点为中心31*31范围中的影像块像素值
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//鼠标移动时根据激活工具状态设置光标类型,如果状态为拖动则还要实时更新影像偏移量//同时重置拖动初始点位
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
//如果激活工具状态为拖动,则释放鼠标并设置m_flag_draging为false
//如果激活工具状态为刺点,则在当前点画出十字丝,同时把当前点保存到m_pt中
CImageMeasuringApparatusDoc* GetDocument();
//自添加的函数,用以获取文档类指针
void GetPtBits(CPoint point, unsigned char *lpBits);
//将point换算到影像上然后取以相应点为中心31*31范围的像素值保存在lpBits中
void DrawPoints(CDC *pDC);
//把Doc中的点位数据显示在影像上。