当前位置:网站首页>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;
}
}
边栏推荐
- Programmer Qixi Gift - How to quickly build an exclusive chat room for your girlfriend in 30 minutes
- 聪明的儿子处理婆媳关系的方法(处理婆媳关系的方法)
- Yolov5 test and train own dataset
- 如何治理资源浪费?百度云原生成本优化最佳实践
- 中电资讯 - 一路“标”升,喜迎Q3开门红
- 分布式链路追踪Jaeger + 微服务Pig在Rainbond上的实践分享
- Practical sharing of distributed link tracking Jaeger + microservice Pig on Rainbond
- Django使用腾讯云发送短信并存入redis
- 1314元的七夕礼盒,收割了多少直男?
- 全面认识MOS管,一篇文章就够了
猜你喜欢

倒计时 3 天|一起看云原生 Meetup 的六大议题

企业应当实施的5个云安全管理策略

项目里的各种配置,你都了解吗?

Do you understand the various configurations in the project?

考研概率论与数理统计(知识点梳理)

Shell loop statement (for, while, until)

什么是 DevOps?看这一篇就够了!

Hit the interview!The latest interview booklet of Ali Jin, nine silver and ten is stable!

Programmer Qixi Gift - How to quickly build an exclusive chat room for your girlfriend in 30 minutes

Hands-on Deep Learning_LeNet
随机推荐
划重点!2022面试必刷461道大厂架构面试真题汇总+面经+简历模板
Shell loop statement (for, while, until)
“蔚来杯“2022牛客暑期多校训练营5 B、C、F、G、H、K
两年独立开发经验程序员告诉我们赚钱的经验(听听真正赚到钱的高手做法)
手搓一个“七夕限定”,用3D Engine 5分钟实现烟花绽放效果
【VSCode】一文详解vscode下安装vim后无法使用Ctrl+CV复制粘贴 使用Vim插件的配置记录
持续交付(二)PipeLine基本使用
Motion Rule (16)-Union Check Basic Questions-Relations
推荐一款优秀的通用管理后台
Yolov5 测试和训练自己的数据集
MATLAB——图像分块
《独行月球》猛药,治不了开心麻花内耗
【UML】信息系统分析与设计知识点总结
Django框架MySQL数据库到models模型的映射关系
Focus!2022 interview must brush 461 interview questions summary + interview + resume template
新消费、出海、大健康......电子烟寻找“避风港”
持续交付(三)Jenkinsfile语法使用介绍
【软考 系统架构设计师】软件架构设计② 软件架构风格
Hit the interview!The latest interview booklet of Ali Jin, nine silver and ten is stable!
密码设置十准则