/* 这个类目前封装了三种采集卡的摄像头:两个模拟摄像头采集卡MV-750,MV-E8000;个数字摄像头采集卡MV-1300。 与一个学长共同完成,是完成的第一个感觉很满意的类,今天整理了一下发出来。 对外接口:载入,打开,关闭,拍照。 希望给对学习类封装的同学有点帮助。 采集卡、摄像头是北京维视的。 by l0g1n CameraV1.h */ #pragma once #include"stdafx.h" #include <string> using namespace std; #include "HVDAILT.h" #include "Raw2Rgb.h" #include "HVDef.h" #include "HVUtil.h" #include "DVRSDK.H" #include "DSStream.h" #pragma comment(lib,"hvdailt.lib") #pragma comment(lib,"raw2rgb.lib") #pragma comment(lib,"HVUtil.lib") #pragma comment(lib,"DVRSDK.lib") #pragma comment(lib,"DSStream.lib") class Camera { public: Camera():m_pWindow(NULL),m_image_buffer(NULL),m_nCameraID(-1), m_nWindowID(0) {} ~Camera(); public: //传入参数,窗口显示所在类指针、窗口ID、摄像头通道号、摄像头名称 void Attach(CWnd* pWnd,int nWindowID,int m_CameraID,string CameraName); bool open(); void close(); bool capture(char* s_pic_name); bool capture(string s_pic_name); bool capture(CString s_pic_name); protected: CWnd* m_pWindow; char* m_image_buffer; unsigned m_nWindowID; int m_nCameraID; protected: void InitCamera(string& CameraName); public: static int CALLBACK SnapThreadCallback(HV_SNAP_INFO *pInfo); ///English: ///Chinese: 回调函数声明 afx_msg LRESULT OnSnapChange(WPARAM wParam, LPARAM lParam); void HVGetDeviceInfo_Resolution(); HV_BAYER_LAYOUT HVGetDeviceInfo_Bayer_Layout(); void InitMV1300(); void InitMV750(); void InitMVE8000(); bool openMV1300(); bool openMV750(); bool openMVE8000(); void closeMV1300(); void closeMV750(); void closeMVE8000(); bool captureMV750(char* s_pic_name); bool captureMV1300(char* s_pic_name); bool captureMVE8000(char* s_pic_name); private: BITMAPINFO *m_pBmpInfo; ///English: ///Chinese: BITMAPINFO 结构指针,显示图像时使用 BYTE *m_pImageBuffer; ///English: ///Chinese: Bayer转换后缓冲区 BYTE *m_pRawBuffer; ///English: ///Chinese: 采集图像原始数据缓冲区 char m_chBmpBuf[2048]; ///English: ///Chinese: BIMTAPINFO 存储缓冲区,m_pBmpInfo即指向此缓冲区 int m_nMaxWid; ///English: ///Chinese: 分辨率最大宽度 int m_nMaxHei; ///English: ///Chinese: 分辨率最大高度 /*///English: ///Chinese: 颜色查找表*/ BYTE m_pLutR[256]; BYTE m_pLutG[256]; BYTE m_pLutB[256]; HV_BAYER_LAYOUT m_BayerType; HHV m_hhv; ///English: ///Chinese: 数字摄像机句柄 HVSTATUS status; ///English: ///Chinese: 设备状态 string m_sCameraName; };
//CameraV1.cpp #pragma once #include "stdafx.h" #include "CameraV1.h" const HV_BAYER_CONVERT_TYPE ConvertType = BAYER2RGB_NEIGHBOUR; ///English: ///Chinese: Raw2RGB算法 Camera::~Camera() { } void Camera::InitCamera(string& CameraName) { if(CameraName=="MV_1300") { m_sCameraName=CameraName; InitMV1300(); return; } if(CameraName=="MV_750") { m_sCameraName=CameraName; InitMV750(); return; } if(CameraName=="MV_E8000") { m_sCameraName=CameraName; InitMVE8000(); return; } throw runtime_error("Camera"+CameraName+"not supported!"); } void Camera::HVGetDeviceInfo_Resolution() ///English: ///Chinese: 获得摄像机分辨率 { int nResolution_Size; ///English: ///Chinese: 获得DESC_RESOLUTION所需空间的大小 status = HVGetDeviceInfo(m_hhv, DESC_RESOLUTION, NULL,&nResolution_Size); if (STATUS_OK != status) { AfxMessageBox(HVGetErrorString(status)); } else { BYTE *pbContext = NULL; pbContext = new BYTE[nResolution_Size]; DWORD *pdContext = (DWORD *)pbContext; status = HVGetDeviceInfo(m_hhv, DESC_RESOLUTION, pdContext,&nResolution_Size); if (STATUS_OK != status) { if (NULL !=pbContext) { delete []pbContext; ///English: ///Chinese: 释放空间 pbContext=NULL; } AfxMessageBox(HVGetErrorString(status)); } else { int nWid,nHei; int nCon = 0; for(int i = 0;i < (nResolution_Size/8);i++) /*///English: ///Chinese: (nResolution_Size/8)表示摄像机分辨率的数量*/ { /*///English: ///Chinese: CCD摄像机只有一种分辨率 */ nWid = *(pdContext + 2*nCon + 0 ); nHei = *(pdContext + 2*nCon + 1 ); m_nMaxWid = (m_nMaxWid>nWid)? m_nMaxWid:nWid; ///English: ///Chinese: 获得分辨率最大宽 m_nMaxHei = (m_nMaxHei>nHei)? m_nMaxHei:nHei; ///English: ///Chinese: 获得分辨率最大高 nCon+=1; } } if (NULL !=pbContext) { delete []pbContext; ///English: ///Chinese: 释放空间 pbContext=NULL; } } } HV_BAYER_LAYOUT Camera::HVGetDeviceInfo_Bayer_Layout() ///English: ///Chinese: 获得摄像机的BAYER数据格式 { int nBayerLayout_Size; ///English: ///Chinese: 获得DESC_DEVICE_BAYER_LAYOUT所需空间的大小 HV_BAYER_LAYOUT Layout; ///English: ///Chinese: Bayer格式 status = HVGetDeviceInfo(m_hhv, DESC_DEVICE_BAYER_LAYOUT, NULL,&nBayerLayout_Size); if(STATUS_OK != status) { AfxMessageBox(HVGetErrorString(status)); } else { BYTE *pbBayerLayout = NULL; pbBayerLayout = new BYTE[nBayerLayout_Size]; DWORD *pdBayerLayout = (DWORD *)pbBayerLayout; status = HVGetDeviceInfo(m_hhv, DESC_DEVICE_BAYER_LAYOUT, pdBayerLayout,&nBayerLayout_Size); if(STATUS_OK != status) { if (NULL != pbBayerLayout) { delete []pbBayerLayout; ///English: ///Chinese: 释放空间 pbBayerLayout=NULL; } AfxMessageBox(HVGetErrorString(status)); } else { Layout = (HV_BAYER_LAYOUT)*pdBayerLayout;///English: ///Chinese: 得到具体的Bayer格式信息 } if (NULL != pbBayerLayout) { delete []pbBayerLayout; ///English: ///Chinese: 释放空间 pbBayerLayout=NULL; } } return Layout; } int CALLBACK Camera::SnapThreadCallback(HV_SNAP_INFO *pInfo) ///English: ///Chinese: 回调函数 { Camera* pDlg=(Camera*)(pInfo->pParam); pDlg->OnSnapChange(NULL,NULL); return 1; } LRESULT Camera::OnSnapChange(WPARAM wParam, LPARAM lParam) { CRect panesize; CRect position; //获取窗口客户区的坐标 ((CStatic*)(m_pWindow->GetDlgItem(m_nWindowID)))->GetClientRect(&panesize); //返回指定窗口的边框矩形的尺寸 ((CStatic*)(m_pWindow->GetDlgItem(m_nWindowID)))->GetWindowRect(&position); CClientDC dc(m_pWindow); CDC* pDC=&dc; UINT panewidth; UINT paneheight; panewidth=panesize.Width(); paneheight=panesize.Height(); //把屏幕上指定点的屏幕坐标转换成用户坐标 m_pWindow->ScreenToClient(&position); SetStretchBltMode(dc.m_hDC,COLORONCOLOR);//使图片不失真 ///English: ///Chinese: 将原始图像数据进行Bayer转换,转换后为24位。 ///English: ///Chinese: 同时将原始数据进行上下翻转 ConvertBayer2Rgb(m_pImageBuffer, ///English: ///Chinese: Bayer转换后缓冲区(输出) m_pRawBuffer, ///English: ///Chinese: 原始数据缓冲区(输入) m_nMaxWid, ///English: ///Chinese: 分辨率最大宽度 m_nMaxHei, ///English: ///Chinese: 分辨率最大高度 ConvertType, ///English: ///Chinese: Raw2RGB算法 m_pLutR, ///English: ///Chinese: 颜色查找表(红) m_pLutG, ///English: ///Chinese: 颜色查找表(绿) m_pLutB, ///English: ///Chinese: 颜色查找表(蓝) true, ///English: ///Chinese: 是否翻转 m_BayerType ///English: ///Chinese: Bayer格式 ); ///English: ///Chinese: 在视图客户区显示图像 StretchDIBits(pDC->GetSafeHdc(), position.left, position.top, panewidth, ///English: ///Chinese: 显示窗口宽度 paneheight, ///English: ///Chinese: 显示窗口高度 0, 0, m_nMaxWid, ///English: ///Chinese: 图像宽度 m_nMaxHei, ///English: ///Chinese: 图像高度 m_pImageBuffer, ///English: ///Chinese: 8位灰度图像数据缓冲区 m_pBmpInfo, ///English: ///Chinese: BMP图像描述信息 DIB_RGB_COLORS, SRCCOPY ); return 1; } bool Camera::open() { if(m_sCameraName=="MV_1300") return openMV1300(); if(m_sCameraName=="MV_750") return openMV750(); if(m_sCameraName=="MV_E8000") return openMVE8000(); } void Camera::close() { if(m_sCameraName=="MV_1300") { closeMV1300(); return; } if(m_sCameraName=="MV_750") { closeMV750(); return; } if(m_sCameraName=="MV_E8000") { closeMVE8000(); return; } } bool Camera::capture(char* s_pic_name) { if(m_sCameraName=="MV_1300") return captureMV1300(s_pic_name); if(m_sCameraName=="MV_750") return captureMV750(s_pic_name); } bool Camera::capture(string s_pic_name) { return false; } bool Camera::capture(CString s_pic_name) { char *name = s_pic_name.GetBuffer(); return capture(name); } void Camera::Attach(CWnd* pWnd,int nWindowID,int nCameraID,string CameraName) { m_pWindow=pWnd; m_nWindowID=nWindowID; m_nCameraID=nCameraID; InitCamera(CameraName); } void Camera::InitMV1300() { status = BeginHVDevice(m_nCameraID,&m_hhv); ///English: ///Chinese: 打开摄像机 HV_VERIFY(status); m_pBmpInfo = NULL; ///English: ///Chinese: BMP图像信息 m_pRawBuffer = NULL; ///English: ///Chinese: 原始图像数据缓冲初始化 m_pImageBuffer = NULL; ///English: ///Chinese: Bayer转换后图像数据缓冲区初始化 m_nMaxWid = 0; m_nMaxHei = 0; for(int i=0;i<256;i++) ///English: ///Chinese: 颜色查找表初始化hao { m_pLutR[i] = i; m_pLutG[i] = i; m_pLutB[i] = i; } HVGetDeviceInfo_Resolution(); ///English: ///Chinese: 获得摄像机分辨率 m_BayerType = HVGetDeviceInfo_Bayer_Layout();///English: ///Chinese: Bayer格式 m_pBmpInfo = (BITMAPINFO *)m_chBmpBuf; ///English: ///Chinese: 初始化BITMAPINFO 结构,此结构在保存bmp文件、显示采集图像时使用 m_pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); ///English: ///Chinese: 图像宽度,一般为输出窗口宽度 m_pBmpInfo->bmiHeader.biWidth = m_nMaxWid; ///English: ///Chinese: 图像宽度,一般为输出窗口高度 m_pBmpInfo->bmiHeader.biHeight = m_nMaxHei; m_pBmpInfo->bmiHeader.biPlanes = 1; m_pBmpInfo->bmiHeader.biBitCount = 24; m_pBmpInfo->bmiHeader.biCompression = BI_RGB; m_pBmpInfo->bmiHeader.biSizeImage = 0; m_pBmpInfo->bmiHeader.biXPelsPerMeter = 0; m_pBmpInfo->bmiHeader.biYPelsPerMeter = 0; m_pBmpInfo->bmiHeader.biClrUsed = 0; m_pBmpInfo->bmiHeader.biClrImportant = 0; /* ///English: ///Chinese: 分配原始图像缓冲区,一般用来存储采集图像原始数据 ///English: ///Chinese: 一般图像缓冲区大小由输出窗口大小和视频格式确定。 */ m_pRawBuffer = new BYTE[m_nMaxWid * m_nMaxHei]; ASSERT(m_pRawBuffer); /* ///English: ///Chinese: 分配Bayer转换后图像数据缓冲 */ m_pImageBuffer = new BYTE[m_nMaxWid * m_nMaxHei * 3]; ASSERT(m_pImageBuffer); } void Camera::InitMV750() { HxnDVR_UnInit(); HxnDVR_Init(m_pWindow->m_hWnd); } bool Camera::openMV1300() { // TODO: 在此添加控件通知处理程序代码 HVSTATUS status = STATUS_OK; status = HVOpenSnap(m_hhv, SnapThreadCallback, this); HV_VERIFY(status); if (HV_SUCCESS(status)) { //m_bOpen = TRUE; ///English: ///Chinese: 标志已经打开Snap环境 } // TODO: 在此添加控件通知处理程序代码 status = STATUS_OK; /* ///English: ///Chinese: 启动数字摄像机采集图像到内存 */ BYTE *ppBuf[1]; ppBuf[0] = m_pRawBuffer; status = HVStartSnap(m_hhv, ppBuf,1); HV_VERIFY(status); if (HV_SUCCESS(status)) { return true; }else return false; } bool Camera::openMV750() { if(!HxnDVR_ConnectDevice(m_nCameraID)) return false; if(!HxnDVR_SetWindowPos(m_nCameraID,(m_pWindow->GetDlgItem(m_nWindowID))->m_hWnd, NULL)) return false; return true; } void Camera::closeMV1300() { HVSTATUS status =STATUS_OK; ///English: ///Chinese: 停止采集图像到内存 status = HVStopSnap(m_hhv); HV_VERIFY(status); if (HV_SUCCESS(status)) { //m_bStart = FALSE; } status = STATUS_OK; /* ///English: ///Chinese: 终止数字摄像机采集图像到内存,同时释放所有采集环境, ///English: ///Chinese: 再次启动数字摄像机采集,必须重新初始化 */ status = HVCloseSnap(m_hhv); HV_VERIFY(status); if (HV_SUCCESS(status)) { //m_bOpen = FALSE; //m_bStart = FALSE; } } void Camera::closeMV750() { HxnDVR_DisconnectDevice (m_nCameraID); HxnDVR_UnInit (); } bool Camera::captureMV750(char* s_pic_name) { return HxnDVR_SaveToJpgFile(m_nCameraID, s_pic_name,100); } bool Camera::captureMV1300(char* s_pic_name) { return HVSaveJPEG(s_pic_name, m_pImageBuffer, (int)(m_pBmpInfo->bmiHeader.biWidth), (int)(m_pBmpInfo->bmiHeader.biHeight), (int)(m_pBmpInfo->bmiHeader.biBitCount), TRUE, 100); } bool Camera::captureMVE8000(char* s_pic_name) { HRESULT hr; hr=DSStream_SaveToJpgFile(m_nCameraID,s_pic_name,100); if(FAILED(hr)) { MessageBox(NULL,TEXT("MV-E8000拍照失败"),TEXT("Error"),MB_OK); return false; } return true; } void Camera::InitMVE8000() { HRESULT hr; hr=DSStream_Initialize(); BOOL bIsConnected=TRUE; int iCardNumber; char szDeviceName[MAX_DEVICE_NUM][MAX_DEVICE_NAME_LEN]; const char szMV[]={"MV"}; hr=DSStream_EnumVideoCaptureDev(szDeviceName,&iCardNumber); if(FAILED(hr)) { MessageBox(NULL,TEXT("枚举失败"),TEXT("Error"),MB_OK); return; } hr=DSStream_IsConnected(m_nCameraID,&bIsConnected); if(FAILED(hr)) { bIsConnected=TRUE; } if(bIsConnected) { MessageBox(NULL,TEXT("无可用卡"),TEXT("Error"),MB_OK); return; } const BOOL bOverlay=FALSE;//多个卡不能同时使用该模式 hr=DSStream_ConnectDevice(m_nCameraID,bOverlay); if(FAILED(hr)) { MessageBox(NULL,TEXT("连接视频卡失败"),TEXT("Error"),MB_OK); return; } DSStream_SetOwnerWnd(m_nCameraID,m_pWindow->m_hWnd); } bool Camera::openMVE8000() { CRect panesize,position; ((CStatic*)(m_pWindow->GetDlgItem(m_nWindowID)))->GetClientRect(&panesize); //返回指定窗口的边框矩形的尺寸 ((CStatic*)(m_pWindow->GetDlgItem(m_nWindowID)))->GetWindowRect(&position); m_pWindow->ScreenToClient(&position); DSStream_SetWindowPos(m_nCameraID,position); HRESULT hr; hr=DSStream_SetStreamStatus(m_nCameraID, RUN); if(FAILED(hr)) { MessageBox(NULL,TEXT("MV-E8000打开摄像头失败"),TEXT("Error"),MB_OK); return false; } return true; } void Camera::closeMVE8000() { DSStream_SetStreamStatus(m_nCameraID, STOP); }