1.使用MFC搭建框架
配置:
- Window10
- VS2013
- opencv249
如果VS和opencv配置不一样,让版本对应
Opencv与VS版本
1.1 MFC项目搭建
通过这些步骤就创建了一个MFC基础项目。
1.2项目属性配置
本项目因为要使用opencv,所以就要配置以下opencv的环境
首先在opencv官网下载opencv,此次使用opencv2.4.9,下载完并且完成安装
接下来就是VS项目配置(Release)发行版
1.3项目页面设计
点击左边的Toolbox(工具栏),选择相应的控件。这里我们的Win端用作服务器,界面主要显示传过来的图片,所以只需要一个Picture Control(图像控件)
可以右键单击界面三个控件,删除,然后将界面窗口右键拉到合适的大小,然后点击工具栏的控件,长按右键进行布置。
界面设计好后就可以进行代码的编写。
1.3代码编写
编写代码主要是以下两个文件
- xxxDlg.h
- xxxDlg.cpp
xxxDlg.h头文件编写
头文件主要引用要使用的头文件,定义socket变量,定义消息函数和消息宏
// MFCApplication1Dlg.h : 头文件
//
#pragma once
#include <opencv2\opencv.hpp>
#include "afxsock.h"
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace cv;
#define WM_CLIENT_READVIDEO WM_USER+103
// CMFCApplication1Dlg 对话框
class CMFCApplication1Dlg : public CDialogEx
{
// 构造
public:
CMFCApplication1Dlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_MFCAPPLICATION1_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()
//添加代码,定义消息函数
LRESULT OnReadVideo(WPARAM wParam, LPARAM lParam); //自定义
public:
//文件预留的两个按钮
afx_msg void OnBnClickedCancel();
afx_msg void OnBnClickedOk();
//添加代码,接收视频socket
SOCKET videoSock;
SOCKADDR_IN videoSockLocal;
SOCKADDR_IN rcv_addr; //存放不需要的IP
int sockLen = sizeof(SOCKADDR);
CStatic m_picture; //picture control控件
};
xxxDlg.cpp文件编写
主要就是编写MFC能够嵌入Opencv的图像,消息函数,确定和取消函数。
// MFCApplication1Dlg.cpp : 实现文件
//
#include "stdafx.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.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()
// CMFCApplication1Dlg 对话框
CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CMFCApplication1Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
//绑定变量
DDX_Control(pDX, IDC_PIC1, m_picture);
}
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDCANCEL, &CMFCApplication1Dlg::OnBnClickedCancel)
ON_BN_CLICKED(IDOK, &CMFCApplication1Dlg::OnBnClickedOk)
//添加消息
ON_MESSAGE(WM_CLIENT_READVIDEO, &CMFCApplication1Dlg::OnReadVideo)
END_MESSAGE_MAP()
// CMFCApplication1Dlg 消息处理程序
BOOL CMFCApplication1Dlg::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: 在此添加额外的初始化代码
//将opencv嵌入到MFC
CRect rect1;
m_picture.GetWindowRect(rect1); //获取picture control控件变量的rect
namedWindow("client", CV_WINDOW_NORMAL);//可以改变窗口大小
resizeWindow("client", rect1.Width(), rect1.Height());//根据piccontrol的大小设置opencv窗口的大小
HWND hWnd = (HWND)cvGetWindowHandle("client");//嵌套opencv窗口
HWND hParent = ::GetParent(hWnd);
::SetParent(hWnd, GetDlgItem(IDC_PIC1)->m_hWnd);
::ShowWindow(hParent, SW_HIDE);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMFCApplication1Dlg::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 CMFCApplication1Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//接收视频消息函数
LRESULT CMFCApplication1Dlg::OnReadVideo(WPARAM wParam, LPARAM lParam)
{
char rcv_video[640*480];
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
//定义图片数据
Mat img_decode; // = Mat::zeros(3, 3, CV_8UC1)
vector<uchar>img_data;
int srcLen = recvfrom(videoSock, rcv_video, sizeof(rcv_video), 0, (SOCKADDR*)&rcv_addr, &sockLen);
if (srcLen>0)
{
vector<uchar> decode(&rcv_video[0], &rcv_video[srcLen]);//直接告诉buf的地址空间 减少一次内存拷贝
Mat srcimg = imdecode(decode, CV_LOAD_IMAGE_COLOR);//opencv的解码函数imdecode将每一帧图片的字节序decode解码为image图片
if (srcimg.rows != 0 && srcimg.cols != 0)
{
imshow("client" + 0, srcimg);//循环显示美贞图片成为视屏。
}
}
}
return 0;
}
//软件退出
void CMFCApplication1Dlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
closesocket(videoSock);
WSACleanup(); //释放DLL资源
exit(0);
}
//确定开启
void CMFCApplication1Dlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
//初始化与绑定
WSADATA wsaData;
int iErrorCode;
if (WSAStartup(MAKEWORD(2, 1), &wsaData))//调用Windows Socket DLL
{
WSACleanup();
return;
}
//绑定本机IP地址
videoSock = socket(AF_INET, SOCK_DGRAM, 0);
videoSockLocal.sin_family = AF_INET;
videoSockLocal.sin_addr.S_un.S_addr = INADDR_ANY;
//确定端口,客户端也要一致
videoSockLocal.sin_port = htons(8898);
if (::bind(videoSock, (SOCKADDR *)&videoSockLocal, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
WSACleanup();
return;
}
else
{
iErrorCode = WSAAsyncSelect(videoSock, m_hWnd, WM_CLIENT_READVIDEO, FD_READ);
if (iErrorCode == SOCKET_ERROR)
{
return;
}
}
}
最终效果演示。
此项目Window端