人机交互量测同名点程序--双像三维建模小软件开发实例(四)

时间:2021-04-22 16:28:56

一个量测同名点的程序是图像几何定向的必要准备,为此我们开发了ImageMeasuringApparatus(图1),它是一款用来进行双像立体量测的程序,其功能有:

1、  影像对的显示和漫游;

2、  显示缩略图,进行手动粗定位;

3、  载入已量测点、手动亚像素级刺点。

该程序源代码(VC++)和详细的开发说明文档(.doc)已经添加在了本人的资源里,可以下载查阅。

人机交互量测同名点程序--双像三维建模小软件开发实例(四)

图 1. ImageMeasuringApparatus运行界面

下面我们阐述改程序的开发方法,通过学习本方法或许有助于加强对于MFC多视图图形界面程序开发的掌握:

本程序是基于MFC的单文档应用程序,布局上采用分割窗口形式。其中涉及的主要的类有CMainFrame,CImageMeasuringApparatusDoc,CMeasuringView,CPhotoView以及CPhotoView2,另外还有一些对话框用来接收参数,原理简单此处不予介绍。

CMainFrame类主要实现了主框架窗口的分割,分割方式如图2所示。

CImageMeasuringApparatusDoc 类用来存储影像和量测点数据。

CMeasuringView类用以显示缩略图,微调视图,并响应用户微调操作。

CPhotoViewCPhotoView2类用以显示原始影像,相应用户漫游刺点等操作。

人机交互量测同名点程序--双像三维建模小软件开发实例(四)

图 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_dragtrue

afx_msg void OnUpdateBtnDrag(CCmdUI* pCmdUI);

//设置工具按钮激活显示状态

afx_msg void OnBtnCross();

//按下刺点按钮的响应函数,设置变量m_flag_crosstrue

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_dragingfalse

//如果激活工具状态为刺点,则在当前点画出十字丝,同时把当前点保存到m_pt

CImageMeasuringApparatusDoc* GetDocument();

//自添加的函数,用以获取文档类指针

void GetPtBits(CPoint point, unsigned char *lpBits);

//point换算到影像上然后取以相应点为中心31*31范围的像素值保存在lpBits

void DrawPoints(CDC *pDC);

//Doc中的点位数据显示在影像上。