1.新建基于对话框的MFC程序,界面布局如下
2.修改控件ID,为控件绑定变量
3.创建全局变量,构造函数中初始化变量,初始化对话框界面,补齐各控件按钮响应函数
全文程序如下:
// MFC_GrabimageDlg.h : 头文件
//#pragma once
#include"MvCameraControl.h"
#include "afxwin.h"// CMFC_GrabimageDlg 对话框
class CMFC_GrabimageDlg : public CDialogEx
{
// 构造
public:CMFC_GrabimageDlg(CWnd* pParent = NULL); // 标准构造函数~CMFC_GrabimageDlg(); //
// 对话框数据enum { IDD = IDD_MFC_GRABIMAGE_DIALOG };protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持// 实现
protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()public:void* handle;//用于存放相机句柄MV_SAVE_IAMGE_TYPE m_enSaveImageType;//用于存储保存图片的类型MV_DISPLAY_FRAME_INFO stDisplayInfo;//原始数据转换int m_imagenum;//图像采集的帧号afx_msg void OnBnClickedButtonOpen();afx_msg void OnBnClickedButtonClose();afx_msg void OnBnClickedButtonGrabimage();CStatic m_picture;//PictureCtrl控件变量afx_msg void OnBnClickedButtonSave();afx_msg void OnBnClickedRadioBmp();afx_msg void OnBnClickedRadioIpg();afx_msg void OnBnClickedRadioPng();afx_msg void OnBnClickedRadioTif();
};
// MFC_GrabimageDlg.cpp : 实现文件
//#include "stdafx.h"
#include "MFC_Grabimage.h"
#include "MFC_GrabimageDlg.h"
#include "afxdialogex.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据enum { IDD = IDD_ABOUTBOX };protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CMFC_GrabimageDlg 对话框CMFC_GrabimageDlg::CMFC_GrabimageDlg(CWnd* pParent /*=NULL*/): CDialogEx(CMFC_GrabimageDlg::IDD, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);handle = NULL;m_enSaveImageType = MV_Image_Bmp;//先默认为bmp格式,后续添加单选按钮来获取,保存的图像类型格式
}void CMFC_GrabimageDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_PICTURE, m_picture);
}BEGIN_MESSAGE_MAP(CMFC_GrabimageDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_OPEN, &CMFC_GrabimageDlg::OnBnClickedButtonOpen)ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CMFC_GrabimageDlg::OnBnClickedButtonClose)ON_BN_CLICKED(IDC_BUTTON_GRABIMAGE, &CMFC_GrabimageDlg::OnBnClickedButtonGrabimage)ON_BN_CLICKED(IDC_BUTTON_SAVE, &CMFC_GrabimageDlg::OnBnClickedButtonSave)ON_BN_CLICKED(IDC_RADIO_BMP, &CMFC_GrabimageDlg::OnBnClickedRadioBmp)ON_BN_CLICKED(IDC_RADIO_IPG, &CMFC_GrabimageDlg::OnBnClickedRadioIpg)ON_BN_CLICKED(IDC_RADIO_PNG, &CMFC_GrabimageDlg::OnBnClickedRadioPng)ON_BN_CLICKED(IDC_RADIO_TIF, &CMFC_GrabimageDlg::OnBnClickedRadioTif)
END_MESSAGE_MAP()// CMFC_GrabimageDlg 消息处理程序BOOL CMFC_GrabimageDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动// 执行此操作SetIcon(m_hIcon, TRUE); // 设置大图标SetIcon(m_hIcon, FALSE); // 设置小图标// TODO: 在此添加额外的初始化代码// 默认选中IDC_RADIO_BMPCheckRadioButton(IDC_RADIO_BMP, IDC_RADIO_TIF, IDC_RADIO_BMP);//单选按钮设置return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}void CMFC_GrabimageDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。void CMFC_GrabimageDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFC_GrabimageDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}void CMFC_GrabimageDlg::OnBnClickedButtonOpen()
{// TODO: 在此添加控件通知处理程序代码//https://blog.csdn.net/weixin_44840658/article/details/89174782//枚举相机//先定义一个设备列表,用来存放枚举到的设备MV_CC_DEVICE_INFO_LIST cam_List;//列表 用于存放枚举到的相机列表//枚举int nRet = MV_OK;nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE, &cam_List);if (MV_OK != nRet){MessageBox(_T("枚举相机失败"));return ;}else//找到相机{//枚举到相机后,就可以获取到相机的一些设备信息//打印设备的SN码、设备名称、制造商//获取枚举到的相机数量int cam_num = cam_List.nDeviceNum;CString str;str.Format(_T("相机数量:%d"), cam_num);MessageBox(str);//留给后边研究,没有运行成功MV_CC_DEVICE_INFO* p_cam_Info = cam_List.pDeviceInfo[0];//相机相关参数输出//PrintDeviceInfo(p_cam_Info);//检查指定设备是否可访问int MV_A = MV_CC_IsDeviceAccessible(p_cam_Info, MV_ACCESS_Control);if (!MV_A){MessageBox(_T("设备不允许访问!"));return;}else{MessageBox(_T("设备允许访问!"));}}//创建相机句柄int nRet2 = MV_OK;nRet2 = MV_CC_CreateHandle(&handle, cam_List.pDeviceInfo[0]);//枚举相机列表中的第一个相机if (MV_OK != nRet2){MessageBox(_T("创建句柄失败!"));return;}//打开相机int nRet3 = MV_OK;nRet3 = MV_CC_OpenDevice(handle);if (MV_OK != nRet3){MessageBox(_T("相机打开失败!"));return;}
}void CMFC_GrabimageDlg::OnBnClickedButtonClose()
{// TODO: 在此添加控件通知处理程序代码//关闭相机int nRet4 = MV_OK;nRet4 = MV_CC_CloseDevice(handle);if (MV_OK != nRet4){MessageBox(_T("相机关闭失败!"));return;}else{MessageBox(_T("相机关闭成功!"));}//销毁句柄if (handle != NULL){MV_CC_DestroyHandle(handle);handle = NULL;}
}void CMFC_GrabimageDlg::OnBnClickedButtonGrabimage()
{// TODO: 在此添加控件通知处理程序代码int nRet = MV_OK;//开始取流// Start grab imagenRet = MV_CC_StartGrabbing(handle);if (MV_OK != nRet){MessageBox(_T("取流失败!!"));return ;}MV_FRAME_OUT stImageInfo = { 0 };//MV_DISPLAY_FRAME_INFO stDisplayInfo = { 0 };stDisplayInfo = { 0 };HWND p_hwnd = m_picture.m_hWnd;//获取控件的窗口句柄CRect rect;m_picture.GetClientRect(&rect);int width = rect.Width(); // 控件的宽度int height = rect.Height(); // 控件的高度nRet = MV_CC_GetImageBuffer(handle, &stImageInfo, 1000);if (nRet == MV_OK){CString str;str.Format(_T("相机采集图像的宽度:%d"), stImageInfo.stFrameInfo.nWidth);MessageBox(str);str.Format(_T("相机采集图像的高度:%d"), stImageInfo.stFrameInfo.nHeight);MessageBox(str);m_imagenum = stImageInfo.stFrameInfo.nFrameNum;str.Format(_T("相机采集图像的帧号:%d"), m_imagenum);MessageBox(str);//将图片显示在PictureCtrl控件窗口上if (p_hwnd){stDisplayInfo.hWnd = p_hwnd;stDisplayInfo.pData = stImageInfo.pBufAddr;// 指向图像数据的指针stDisplayInfo.nDataLen = stImageInfo.stFrameInfo.nFrameLen;//数据长度 stDisplayInfo.nWidth = stImageInfo.stFrameInfo.nWidth; // 图像的宽度(像素)//stDisplayInfo.nWidth = width; // 图像的宽度(像素)用控件的宽度来代替stDisplayInfo.nHeight = stImageInfo.stFrameInfo.nHeight; // 图像的高度(像素//stDisplayInfo.nHeight = height;// 图像的高度(像素)用控件的高度来代替stDisplayInfo.enPixelType = stImageInfo.stFrameInfo.enPixelType; // 像素类型,如黑白、RGB、Bayer等MV_CC_DisplayOneFrame(handle, &stDisplayInfo);//显示图像}MV_CC_FreeImageBuffer(handle, &stImageInfo);//需要用 MV_CC_FreeImageBuffer() 接口将pstFrame内的数据指针权限进行释放}else{MessageBox(_T("没有数据!"));}//停止采集// Stop grab imagenRet = MV_CC_StopGrabbing(handle);if (MV_OK != nRet){MessageBox(_T("停止采集失败!"));return ;}}CMFC_GrabimageDlg::~CMFC_GrabimageDlg()
{handle=NULL;
}void CMFC_GrabimageDlg::OnBnClickedButtonSave()
{// TODO: 在此添加控件通知处理程序代码MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam;memset(&stSaveFileParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM));stSaveFileParam.enImageType = m_enSaveImageType; // ch:需要保存的图像类型 | en:Image format to savestSaveFileParam.enPixelType = stDisplayInfo.enPixelType; // ch:相机对应的像素格式 | en:Camera pixel typestSaveFileParam.nWidth = stDisplayInfo.nWidth; // ch:相机对应的宽 | en:WidthstSaveFileParam.nHeight = stDisplayInfo.nHeight; // ch:相机对应的高 | en:HeightstSaveFileParam.nDataLen = stDisplayInfo.nDataLen;stSaveFileParam.pData = stDisplayInfo.pData;stSaveFileParam.iMethodValue = 0;// ch:jpg图像质量范围为(50-99], png图像质量范围为[0-9] | en:jpg image nQuality range is (50-99], png image nQuality range is [0-9]if (MV_Image_Bmp == stSaveFileParam.enImageType){sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_imagenum);//这段代码使用了 `sprintf_s` 函数来格式化一个字符串,该字符串将用于存储图像文件的路径。`sprintf_s` 是一个安全版本的 `sprintf` 函数,用于格式化输出到一个字符数组中,它可以防止缓冲区溢出。//让我们详细分析一下这段代码://sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_stImageInfo.nFrameNum);// `stSaveFileParam.pImagePath`: 这是指向字符数组的指针,该数组将用来存储生成的文件路径字符串。这个数组应该足够大以容纳生成的字符串。//`256`: 这个数字是 `pImagePath` 指向的字符数组的大小,单位是字节。`sprintf_s` 需要知道目标缓冲区的大小,以避免写入超出边界。// `"Image_w%d_h%d_fn%03d.bmp"`: 这是格式化字符串,其中包含占位符 `%d` 和 `%03d`。`%d` 用于插入十进制整数,而 `%03d` 表示一个至少三位数的十进制数,不足部分用前导零填充。// `stSaveFileParam.nWidth`: 这是一个整数值,表示图像的宽度,它将被插入到第一个 `%d` 占位符的位置。// `stSaveFileParam.nHeight`: 这也是一个整数值,表示图像的高度,它将被插入到第二个 `%d` 占位符的位置。// `m_stImageInfo.nFrameNum`: 这是帧号,一个整数值,它将被插入到 `%03d` 占位符的位置,表示图像的序号,并确保至少有三位数字,不足则用零填充。//整个字符串 `"Image_w%d_h%d_fn%03d.bmp"` 将会被格式化成类似于 `"Image_w640_h480_fn001.bmp"` 的形式,其中 `w640`、`h480` 和 `fn001` 分别代表图像的宽度、高度和帧号。//这样做的目的是为了生成一个唯一的文件名,便于在保存图像时区分不同的图像文件,尤其是当有多张图像需要按顺序保存时。这种格式化字符串的方式在处理大量图像文件或需要动态生成文件名时非常常见。}else if (MV_Image_Jpeg == stSaveFileParam.enImageType){stSaveFileParam.nQuality = 80;sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.jpg", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_imagenum);}else if (MV_Image_Tif == stSaveFileParam.enImageType){sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.tif", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_imagenum);}else if (MV_Image_Png == stSaveFileParam.enImageType){stSaveFileParam.nQuality = 8;sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.png", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_imagenum);}int nRet = MV_CC_SaveImageToFile(handle, &stSaveFileParam);if (MV_OK != nRet){MessageBox(_T("图像保存失败!"));return;}
}void CMFC_GrabimageDlg::OnBnClickedRadioBmp()
{// TODO: 在此添加控件通知处理程序代码m_enSaveImageType = MV_Image_Bmp;//设置图片为bmp格式
}void CMFC_GrabimageDlg::OnBnClickedRadioIpg()
{// TODO: 在此添加控件通知处理程序代码m_enSaveImageType = MV_Image_Jpeg;//设置图片为jpg格式
}void CMFC_GrabimageDlg::OnBnClickedRadioPng()
{// TODO: 在此添加控件通知处理程序代码m_enSaveImageType = MV_Image_Png;//设置图片为png格式
}void CMFC_GrabimageDlg::OnBnClickedRadioTif()
{// TODO: 在此添加控件通知处理程序代码m_enSaveImageType = MV_Image_Tif;//设置图片为tif格式
}