当前位置:网站首页>MFC的相机双目标定界面设计
MFC的相机双目标定界面设计
2022-08-04 12:29:00 【郑学长】
MFC的相机双目标定界面设计
一、先上整体的界面图
由于是新手,所以采用的拖控件的方法,而且界面的美化基本没有。

二、主要使用的控件
按钮控件:BUTTON (3个)分别用来实现添加图片,标定,查看标定结果。
静态文本控件:Static Text 这里主要用来做标记。
编辑框控件: Edit Control(2个) 用来显示标定结果和选择结果。
列表控件:List Control(1个) 用来显示读取的图片列表。
图片显示控件: Picture Control(2个) 用来显示每组的左右图片。
三、具体实现代码
1.标定主要使用的Opencv,所以环境就先配置好,这里不赘述,主要就是几个按钮功能的实现。
添加图片的实现代码如下:
void CStereoCameraCalibratorDlg::OnBnClickedButton1Addimages()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog OpenFileDlg(TRUE/*FALSE为保存*/, _T("ALL(*.*)|*.*|BMP(*.bmp)|*.bmp|JPEG(*.jpg)|*.jpg|PNG(*.png)|*.png||"),
NULL, OFN_ALLOWMULTISELECT | OFN_ENABLESIZING | OFN_HIDEREADONLY, _T("All(*.*)|*.*|BMP(*.bmp)|*.bmp|JPEG(*.jpg)|*.jpg|PNG(*..png)|*.png||"));
TCHAR *pBuffer = new TCHAR[MAX_PATH * 50];
OpenFileDlg.m_ofn.lpstrFile = pBuffer;
OpenFileDlg.m_ofn.nMaxFile = MAX_PATH * 50;
OpenFileDlg.m_ofn.lpstrFile[0] = '\0';
std::vector<CString> BatchImgpaths;
CString imgPath;
if (BatchLimg.size() == 0) {
MessageBox(TEXT("请先选择左图!"));
}
if (OpenFileDlg.DoModal() == IDOK)
{
CString BatchImgpath = _T("");
CString imgName = _T("");
POSITION pos = OpenFileDlg.GetStartPosition();
while (pos != NULL)
{
BatchImgpath = OpenFileDlg.GetNextPathName(pos);
CString temp = OpenFileDlg.GetFileTitle();
BatchImgpaths.push_back(BatchImgpath);
int length = BatchImgpath.GetLength();
for (int i = length - 1; i > 0; i--)
{
if (BatchImgpath[i] == '\\')
{
//判断当前字符是否是'\'
imgName = BatchImgpath.Right(length - i - 1);
BatchLimg.size() == 0 ? BatchLImgNames.push_back(imgName.GetBuffer()) : BatchRImgNames.push_back(imgName.GetBuffer());
break; //跳出循环
}
}
}
}
if (BatchLimg.size() == 0)
{
for (int i = 0; i < BatchImgpaths.size(); i++)
{
cv::Mat Limg = cv::imread(BatchImgpaths[i].GetBuffer());
BatchLimg.push_back(Limg);
}
return;
}
for (int i = 0; i < BatchImgpaths.size(); i++)
{
cv::Mat Rimg = cv::imread(BatchImgpaths[i].GetBuffer());
BatchRimg.push_back(Rimg);
}
//在列表视图控件中插入数据 行插
for (int i = 0; i < BatchLimg.size(); i++)
{
int a = 1;
CString str;
str.Format(_T("%d"), i);
int nRow = m_PicList.InsertItem(i, str);
CString LItem = "L" + str;
m_PicList.SetItemText(i, a, LItem);
CString RItem = "R" + str;
m_PicList.SetItemText(i, a + 1, RItem);
}
//添加完图片,先在Picture Control控件中显示左右的第一张图
cv::Mat Lmat = BatchLimg[0];
cv::Mat Rmat = BatchRimg[0];
CWnd* pWnd_l = GetDlgItem(IDC_STATIC_LIMAGE);
ComputeShowImageSize(pWnd_l, Lmat.cols, Lmat.rows);
cv::Mat resultImage;
cv::resize(Lmat, resultImage, cv::Size(m_Control_Col, m_Control_Row), 0, 0, CV_INTER_AREA);
imshow(NAME_LEFT_WINDOW, resultImage);
cv::resize(Rmat, resultImage, cv::Size(m_Control_Col, m_Control_Row), 0, 0, CV_INTER_AREA);
imshow(NAME_RIGHT_WINDOW, resultImage);
}
标定按钮的实现代码如下(头文件以及变量的命名,调用的函数实在不好上传):
void CStereoCameraCalibratorDlg::OnBnClickedButton1Calibrator()
{
// TODO: 在此添加控件通知处理程序代码
cal.InitChessboardCorners(BatchLimg[0], objectPoints_L, corners_seq_L, imageSize, patternSize, chessboardSize);
for (int numCountL = 1; numCountL < BatchLimg.size(); numCountL++)
{
cal.InitChessboardCorners(BatchLimg[numCountL], objectPoints_L, corners_seq_L, imageSize, patternSize, chessboardSize);
}
cal.singleCameraCalibrate(singleCalibrate_result_L, objectPoints_L, corners_seq_L, cameraMatrix_L,
distCoeffs_L, imageSize, patternSize);
cout << "已完成左相机的标定!" << endl;
cal.InitChessboardCorners(BatchRimg[0], objectPoints_R, corners_seq_R, imageSize, patternSize, chessboardSize);
for (int numCountR = 1; numCountR < BatchRimg.size(); numCountR++)
{
cal.InitChessboardCorners(BatchRimg[numCountR], objectPoints_R, corners_seq_R, imageSize, patternSize, chessboardSize);
}
cal.singleCameraCalibrate(singleCalibrate_result_R, objectPoints_R, corners_seq_R, cameraMatrix_R,
distCoeffs_R, imageSize, patternSize);
cout << "已完成右相机的标定!" << endl;
cal.stereoCalibrate1(stereoCalibrate_result, objectPoints_L, corners_seq_L, corners_seq_R, cameraMatrix_L, distCoeffs_L,
cameraMatrix_R, distCoeffs_R, imageSize, R, T, E, F);
UINT i;
i = MessageBox(TEXT("完成双目标定!")); //弹出小提示
}
标定结果按钮的实现代码如下:
void CStereoCameraCalibratorDlg::OnBnClickedButton1Calibratorresult()
{
//TODO: 在此添加控件通知处理程序代码
CFileDialog fdlg(TRUE, NULL, _T("*.*"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("文本文件(*.txt)|*.txt|视频文件(*.avi)|所有文件(*.*)|*.*||"));
if (IDCANCEL == fdlg.DoModal())
{
return;
}
//读取当前对话框选择的文件路径
CString strPathName = fdlg.GetPathName();
读取路径名到编辑框
//SetDlgItemText(IDC_EDIT1, strPathName);
//打开选择的文件
//构造文件,同时增加异常处理
CFile cFile;
CFileException e;
if (FALSE == cFile.Open(strPathName, CFile::modeRead, &e))
{
CString strException;
strException.Format(_T("无法打开此文件\n\t"), e.m_cause);
MessageBox(strException, _T("警告!"), MB_OK | MB_ICONERROR);
return;
}
//读取打开的文件内容
DWORD dwCount, dwFilelenth = (DWORD)cFile.GetLength();
char* szBuffer = new char[dwFilelenth + 1];
ZeroMemory(szBuffer, dwFilelenth + 1);
CString strContent;
while (dwCount = cFile.Read(szBuffer, dwFilelenth))
{
//拼接读取数据
strContent += szBuffer;
}
//将读取的文本数据显示到编辑框中
SetDlgItemText(IDC_EDIT1_RESULT, strContent);
cFile.Close();
delete[] szBuffer;
szBuffer = nullptr;
}
用列表显示读取到的图片组并进行选择,将选择的组号显示到编辑框,将选择的图片显示到Picture Control控件的部分实现代码如下(此代码是双击List Control的响应事件下的代码):
效果如下:
void CStereoCameraCalibratorDlg::OnLvnItemchangedList3Showpic(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
/*int nIndex = 0;*/
选中
//m_PicList.SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
取消选中
//m_PicList.SetItemState(nIndex, 0, LVIS_SELECTED | LVIS_FOCUSED);
CString strPic;
NMLISTVIEW *pNMListView = (NMLISTVIEW*)pNMHDR;
if (-1 != pNMListView->iItem) //如果iTtem不是-1,就说明有列表项被选择
{
//获取备选列表项第一个子项的文本
strPic = m_PicList.GetItemText(pNMListView->iItem, 0);
//将选择的内容显示在编辑框中
CString tempStr = "L(" + strPic + ")+" + "R(" + strPic + ")";
SetDlgItemText(IDC_EDIT1_SEL_PICTURE, tempStr);
//将选中的图片显示在Picture Control控件中
cv::Mat resultImage;
cv::resize(BatchLimg[_ttoi(strPic)], resultImage, cv::Size(m_Control_Col, m_Control_Row), 0, 0, CV_INTER_AREA);
imshow(NAME_LEFT_WINDOW, resultImage);
cv::resize(BatchRimg[_ttoi(strPic)], resultImage, cv::Size(m_Control_Col, m_Control_Row), 0, 0, CV_INTER_AREA);
imshow(NAME_RIGHT_WINDOW, resultImage);
}
}
2.如何实现用 List Control进行选择,再用Picture Control显示选中的图片
首先,右键单击List Control控件添加成员变量,添加完后,在你的主对话框的头文件中,会自动生该成员变量的声明,同时你也可以在类向导中查看你添加的变量。
添加完成员变量后,我们需要在OnInitDialog下对List Control控件进行初始化,并设置该控件的属性View为Report。
初始化代码如下:
这里有一段代码是为了如期显示图片的代码。
// TODO: 在此添加额外的初始化代码
CRect rect;
//获取列表视图控件的位置和大小
m_PicList.GetClientRect(&rect);
//为列表视图控件添加全行选中和栅格风格
m_PicList.SetExtendedStyle(m_PicList.GetExtendedStyle() | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);
//为列表添加三列
m_PicList.InsertColumn(0, _T("序号"), LVCFMT_CENTER, rect.Width() / 3, 0);
m_PicList.InsertColumn(1, _T("左图"), LVCFMT_CENTER, rect.Width() / 3, 1);
m_PicList.InsertColumn(2, _T("右图"), LVCFMT_CENTER, rect.Width() / 3, 2);//
//将新建的显示的图片的window与Picture Control控件关联
CWnd* pWnd_l = GetDlgItem(IDC_STATIC_LIMAGE);
AttachWindow(pWnd_l, NAME_LEFT_WINDOW);
CWnd* pWnd_r = GetDlgItem(IDC_STATIC_RIMAGE);
AttachWindow(pWnd_r, NAME_RIGHT_WINDOW);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
另外显示图片还涉及到两个函数,具体的函数实现代码如下:
//创建两个窗口显示图片, 之后再将这两个窗口与Picture Control控件关联起来
void CCalibratorDlg::AttachWindow(CWnd *pWnd, std::string wndName)
{
cv::namedWindow(wndName.data(), cv::WINDOW_AUTOSIZE); //设置窗口名
HWND hWndl = (HWND)cvGetWindowHandle(wndName.data()); //hWnd 表示窗口句柄,获得窗口句柄
HWND hParent1 = ::GetParent(hWndl); //GetParent函数获得一个指定子窗口的父窗口句柄
::SetParent(hWndl, pWnd->m_hWnd);
::ShowWindow(hParent1, SW_HIDE); //ShowWindow指定窗口显示
}
void CCalibratorDlg::ComputeShowImageSize(CWnd *pWnd, int width, int height)
{
CRect Show_Rect;
pWnd->GetClientRect(&Show_Rect);
double Show_Ratio = (double)Show_Rect.Height() / (double)Show_Rect.Width(); //便于控件显示的高宽比率
int Roi_Row = height;
int Roi_Col = width;
double Roi_ratio = (double)Roi_Row / (double)Roi_Col; //图片的高宽比率
if (Show_Ratio > Roi_ratio)
{
m_Control_Col = Show_Rect.Width();
m_Control_Row = (int)((double)m_Control_Col*Roi_ratio);
}
else
{
m_Control_Row = Show_Rect.Height();
m_Control_Col = (int)((double)m_Control_Row / Roi_ratio);
}
m_ratio = (double)Roi_Row / (double)m_Control_Row;
}
当出现角点提取顺序不一致的情况时,可以手动删除选中的那组图,然后点击标定,将对剩下的图进行重新标定步骤如下:
首先在资源视图那里,点击右键添加资源,选择Menu,选择新建
然后按自己的要求进到该菜单的编辑界面,进行菜单编辑。
不要直接在右键菜单的位置,直接写成删除,你右键点击后弹出的其实是删除这个菜单,如果这里二级菜单没有,那么在下面的代码menu.GetSubMenu(0)->TrackPopupMenu(0, pt.x, pt.y, this);会报返回值为空的情况。
加载菜单部分实现代码如下,在列表控件上右键单击,选择类向导,添加消息事件处理程序,消息部分选择右键单击NM_RCLICK,再点击添加处理程序:
void CStereoCameraCalibratorDlg::OnRclickList3Showpic(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
CMenu menu;
POINT pt = {
0 }; //用于储存鼠标位置pt.x和pt.y
GetCursorPos(&pt); //得到鼠标点击位置
menu.LoadMenuA(IDR_MENU1_DELETE); //加载菜单ID
menu.GetSubMenu(0)->TrackPopupMenu(0, pt.x, pt.y, this);
*pResult = 0;
}
删除功能的实现:
void CStereoCameraCalibratorDlg::OnDelete()
{
// TODO: 在此添加命令处理程序代码
int nItem = m_PicList.GetSelectionMark(); //获取点击的序号
m_PicList.DeleteItem(nItem); //删除
BatchLimg.erase(BatchLimg.begin() + nItem); //删除容器中存的对应图片,后面再进行重新标定
BatchRimg.erase(BatchRimg.begin() + nItem);
corners_L.erase(corners_L.begin() + nItem);
corners_R.erase(corners_R.begin() + nItem);
}
修改后的文件读取
点击到文件夹自动获取文件夹下的内容,添加图片控件下的代码:
BeginWaitCursor();
vector<imgGroup>().swap(imgPassive);//清空上次运行结果
vector<imgFileName> file = cal.SelectFolder();
if (file.size() == 0)
{
AfxMessageBox("选择文件夹错误!");
}
for (int i = 0; i < file.size(); i++)
{
imgGroup img_Group;
for (int j = 0; j < file[i].LimgFileName.size(); j++)
{
Mat img = imread(file[i].LimgFileName[j]);
BatchLimg.push_back(img);
img = imread(file[i].RimgFileName[j]);
BatchRimg.push_back(img);
}
imgPassive.push_back(img_Group);
}
其中,代码的实现,以及变量的申明
//cal.h中
typedef struct _imgFileName
{
vector<cv::String> LimgFileName;
vector<cv::String> RimgFileName;
}imgFileName;
std::vector<imgFileName> SelectFolder();
void GetFilePath(vector<imgFileName>& vFilePathList, CString strDir);
//cal.cpp中
std::vector<imgFileName> Calibrator::SelectFolder()
{
std::vector<imgFileName> vFilePathList;
BROWSEINFO bi;
char name[MAX_PATH];
ZeroMemory(&bi, sizeof(BROWSEINFO));
bi.hwndOwner = AfxGetMainWnd()->GetSafeHwnd();
bi.pszDisplayName = (LPTSTR)name;
bi.lpszTitle = _T("选择文件夹目录");
bi.ulFlags = BIF_RETURNFSANCESTORS;
LPITEMIDLIST idl = SHBrowseForFolder(&bi);
if (idl == NULL)
return vFilePathList;
CString strDirectoryPath;
SHGetPathFromIDList(idl, strDirectoryPath.GetBuffer(MAX_PATH));
strDirectoryPath.ReleaseBuffer();
if (strDirectoryPath.IsEmpty())
return vFilePathList;
if (strDirectoryPath.Right(1) != "\\")
strDirectoryPath += "\\";
GetFilePath(vFilePathList, strDirectoryPath);
return vFilePathList;
}
//得到文件夹下所有文件
void Calibrator::GetFilePath(vector<imgFileName>& vFilePathList, CString strDir)
{
CFileFind finder;
BOOL isNotEmpty = finder.FindFile(strDir + _T("*.*"));//总文件夹,开始遍历?
imgFileName imgFilePath;
int flag = 0;
while (isNotEmpty)
{
isNotEmpty = finder.FindNextFile();//查找文件?
CString filename = finder.GetFilePath();
if (finder.IsDirectory())
{
if (!(finder.IsDots() || finder.IsHidden() || finder.IsSystem() || finder.IsTemporary() || finder.IsReadOnly()))
{
CString fileTitle = finder.GetFileTitle();
if (fileTitle == "L")
{
cv::glob(filename.GetBuffer(), imgFilePath.LimgFileName);
flag++;//L文件夹数据储存
continue;
}
else if (fileTitle == "R")
{
cv::glob(filename.GetBuffer(), imgFilePath.RimgFileName);
flag++;//R文件夹数据储存
continue;
}
GetFilePath(vFilePathList, filename + "\\");
}
}
}
if (flag == 2)
{
vFilePathList.push_back(imgFilePath);
flag = 0;
return;
}
}
边栏推荐
猜你喜欢

Why is Luo Zhenyu's A-share dream so difficult to fulfill?

【PHP实现微信公众平台开发—基础篇】第2章 微信公众账号及申请流程详解

中电资讯 - 一路“标”升,喜迎Q3开门红

【自动微分实现】反向OO实现自动微分(Pytroch核心机制)

高速电路PCB布局布线参考

技术分享| 小程序实现音视频通话

Do you understand the various configurations in the project?

如何让 WPF 程序更好地适配 UI 自动化

Cool and efficient data visualization big screen, it's really not that difficult to do!丨Geek Planet

分布式链路追踪Jaeger + 微服务Pig在Rainbond上的实践分享
随机推荐
Js获取当前页面url参数
什么是 DevOps?看这一篇就够了!
罗振宇的A股梦,咋这么难圆?
接到“网站动态换主题”的需求,我是如何踩坑的
rpm安装提示error: XXX: not an rpm package (or package manifest):
COMSOL空气反应 模型框架
RobotFramework二次开发(一)
为什么密码云服务平台是云时代的必然之选?
绩效考核带给员工的不能只是压力
广告电商系统开发之订单处理
Oracle 19c 单实例 19.3.0 升级到19.11.0 详细教程
抽奖/秒杀/竞价/评分/权威/投票,技术教你用合适的方法做好活动
"Lonely Walking on the Moon" is a powerful medicine, it can't cure the internal friction of happy twist
从零开始配置 vim(7)——自动命令
【UML】信息系统分析与设计知识点总结
Geoffrey Hinton:深度学习的下一个大事件
论文翻译:2022_Time-Frequency Attention for Monaural Speech Enhancement
高速电路PCB布局布线参考
电源输出的Overshoot和Undershoot 测试
免费翻译软件哪个好用