终于到了不写博客不行的地步了,菜鸡的技术终于要袒露出来了,唉,话不多说直入正题。
第一次写博客,结构有点乱,各位凑合着看吧。
1.核心功能:显示摄像头画面
先来张图直观理解下
第一个画面是外接的一个USB摄像头传上来的图像,第二个是本菜鸡笔记本上的摄像头传上来的图片(渣渣像素无力吐槽)
打开摄像头并上传图像用opencv来做的话非常简单
UINT CShowThreeDlg::ShowImg(LPVOID lpParam)
{
CShowThreeDlg *pDlg= (CShowThreeDlg*)lpParam;
VideoCapture capture; // 创建个VideoCapture类的实例
capture.open(); // 打开USB设备
if (!capture.isOpened()) // 判断是否打开成功
return false;
while (true)
{
Mat img;
capture >> img;
if(img.empty())
return false;
CRect drect;
pDlg->GetDlgItem(IDC_SHOW3_PIC);
pDlg->GetClientRect(&drect);
CClientDC dc(pDlg);
HDC hDC =dc.GetSafeHdc();
//内存中的图像数据拷贝到屏幕上
BYTE *bitBuffer = NULL;
BITMAPINFO *bitMapinfo = NULL;
int ichannels =img.channels(); // 获取并判断通道数
if( ichannels == 1)
{
bitBuffer = new BYTE[40+4*256];
}
else if( ichannels == 3)
{
bitBuffer = new BYTE[sizeof(BITMAPINFO)];
}
else
{
return false;
}
if(bitBuffer == NULL)
{
return false;
}
bitMapinfo = (BITMAPINFO *)bitBuffer;
bitMapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitMapinfo->bmiHeader.biHeight = -img.rows; //如果高度为正的,位图的起始位置在左下角。如果高度为负,起始位置在左上角。
bitMapinfo->bmiHeader.biWidth = img.cols;
bitMapinfo->bmiHeader.biPlanes = 1; // 目标设备的级别,必须为1
bitMapinfo->bmiHeader.biBitCount = ichannels *8;// 每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一
bitMapinfo->bmiHeader.biCompression = BI_RGB; //位图压缩类型,必须是 0(不压缩), 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
bitMapinfo->bmiHeader.biSizeImage = 0; // 位图的大小,以字节为单位
bitMapinfo->bmiHeader.biXPelsPerMeter = 0; // 位图水平分辨率,每米像素数
bitMapinfo->bmiHeader.biYPelsPerMeter = 0; // 位图垂直分辨率,每米像素数
bitMapinfo->bmiHeader.biClrUsed = 0; // 位图实际使用的颜色表中的颜色数
bitMapinfo->bmiHeader.biClrImportant = 0; // 位图显示过程中重要的颜色数
if(ichannels == 1)
{
for(int i=0; i<256; i++)
{ //颜色的取值范围 (0-255)
bitMapinfo->bmiColors[i].rgbBlue =bitMapinfo->bmiColors[i].rgbGreen =bitMapinfo->bmiColors[i].rgbRed = (BYTE) i;
}
bitMapinfo->bmiHeader.biClrUsed = 256; // 位图实际使用的颜色表中的颜色数
}
SetStretchBltMode(hDC, COLORONCOLOR);
StretchDIBits(hDC,
0,
0,
drect.right, //显示窗口宽度
drect.bottom, //显示窗口高度
0,
0,
img.cols, //图像宽度
img.rows, //图像高度
img.data,
bitMapinfo,
DIB_RGB_COLORS,
SRCCOPY
);
delete []bitBuffer;
waitKey(30); //延时30
if (pDlg->m_bCloseShow == TRUE)
{
pDlg = NULL;
capture.release();
return TRUE;
}
}
return TRUE;
}
void CShowThreeDlg::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// CAddNewShow dlg;
// dlg.DoModal();
hThread = AfxBeginThread(ShowImg,
this);//创建一个新线程ShowImg
CFormView::OnRButtonUp(nFlags, point);
}
以上就是整个图像显示的流程了,写到线程里面让他努力干活吧,我们继续去搞事情
2.附加功能:截图
既然都已经显示出来了那么顺手就把截图给做出来吧,反正挺简单的
先来张效果图

很简单,按下空格,然后获取当前显示的那一帧图像,然后保存下来
if (pDlg->m_bWabei == TRUE) // 截图存图 后续界面上补个提示
{
Mat frame;
const char* path;
path=".\\image\\test.bmp"; // 路径自己设,可以弄个for循环来实现多张截图
capture.read(frame); // 当前帧图像读取到Mat类型里
imwrite(path,frame); // 图像保存
pDlg->m_bWabei = FALSE; // 截图标志状态改回来 构造函数里就初始为false
}
这段放到显示图像后面(放到
waitKey(30); //延时30 后面就挺好的) ,m_bWabei是个状态标志,用来判断什么时候截图
重载PreTranslateMessage,截获按键消息
BOOL CShowThreeDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if (pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_ESCAPE)
{
m_bCloseShow = TRUE;
return TRUE;
}
if (pMsg->wParam == VK_SPACE)
{
m_bWabei = TRUE; // 按下空格键的时候标志置为true,然后线程里截图
}
}
return CFormView::PreTranslateMessage(pMsg);
}
撤了,接女票下班了,先写到这,有空补接下来的功能
