目前在人脸识别领域中,网络摄像头的使用很普遍,但接入网络摄像头和人脸识别SDK有一定门槛,在此篇中介绍过虹软人脸识别SDK的接入流程,本文着重介绍网络摄像头获取视频流并处理的流程(红色框内),以下内容仅供参考。

市面上目前有很多款网络摄像头,以海康摄像头为例。海康SDK包含很多接口,接入有一定难度,这里只介绍获取视频帧相关的接口。
1.海康SDK接入基本流程
a.初始化并登录验证

    NET_DVR_Init();
NET_DVR_DEVICEINFO_V30 struDeviceInfo = { 0 };
long lUserID = NET_DVR_Login_V30(m_cameraIp, m_cameraPort,
m_cameraUser, m_cameraPwd, &struDeviceInfo);
if (lUserID < 0)
{
NET_DVR_Cleanup();
return false;
}

b.创建线程并注册回调函数

    thread videoThread(&HCNetCamera::getCameraPreview, this);
videoThread.detach(); bool HCNetCamera::getCameraPreview()
{
NET_DVR_CLIENTINFO ClientInfo;
ClientInfo.lChannel = 1; //Channel number 设备通道号
ClientInfo.hPlayWnd = NULL; //窗口为空,设备SDK不解码只取流
ClientInfo.lLinkMode = 0; //Main Stream
ClientInfo.sMultiCastIP = NULL;
//预览取流
g_realHandle = NET_DVR_RealPlay_V30(g_cameraUserId, &ClientInfo, fRealDataCallBack, NULL, TRUE);
if (g_realHandle < 0)
{
qDebug() << "NET_DVR_RealPlay_V30 failed! Error number: " << NET_DVR_GetLastError();
return false;
}
return true;
}

  

c.使用回调接口,获取实时的视频帧数据

    void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
UNREFERENCED_PARAMETER(lRealHandle);
UNREFERENCED_PARAMETER(pUser); DWORD dRet = 0;
BOOL inData = FALSE; switch (dwDataType)
{
case NET_DVR_SYSHEAD:
if (g_cameraPort >= 0)
{
break; //同一路码流不需要多次调用开流接口
}
if (!PlayM4_GetPort(&g_cameraPort))
{
break;
}
if (!PlayM4_OpenStream(g_cameraPort, pBuffer, dwBufSize, 1024 * 1024))
{
dRet = PlayM4_GetLastError(g_cameraPort);
break;
}
//设置解码回调函数
if (!PlayM4_SetDecCallBack(g_cameraPort, DecCBFun))
{
dRet = PlayM4_GetLastError(g_cameraPort);
break;
}
//打开视频解码
if (!PlayM4_Play(g_cameraPort, NULL))
{
dRet = PlayM4_GetLastError(g_cameraPort);
break;
}
dRet = PlayM4_GetLastError(g_cameraPort);
break; case NET_DVR_STREAMDATA: //视频流数据
default:
inData = PlayM4_InputData(g_cameraPort, pBuffer, dwBufSize);
while (!inData)
{
Sleep(10);
inData = PlayM4_InputData(g_cameraPort, pBuffer, dwBufSize);
dRet = PlayM4_GetLastError(g_cameraPort);
OutputDebugString(L"PlayM4_InputData failed \n");
}
break;
}
} //解码回调 视频为YUV数据(YV12)
void CALLBACK DecCBFun(long port, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
UNREFERENCED_PARAMETER(nReserved1);
UNREFERENCED_PARAMETER(nReserved2);
UNREFERENCED_PARAMETER(nSize);
UNREFERENCED_PARAMETER(port); //图像格式转换
if (pFrameInfo->nType == T_YV12)
{
{
lock_guard<mutex> locker(g_CameraMutex); Utils_ns::ImageUtils_ns::YV12ToBGR24_FFMPEG((unsigned char*)pBuf, (unsigned char*)g_curRGBImage->imageData,
pFrameInfo->nWidth, pFrameInfo->nHeight);//得到全部RGB图像
}
}
}

  

d.应用层获取视频帧,这里为了简化操作,只获取了当前帧;大家也可以使用线程安全队列来处理

    int HCNetCamera::getFrame(Mat& image)
{
lock_guard<mutex> locker(g_CameraMutex);
if (g_curRGBImage && g_curRGBImage->imageData)
{
image = g_curRGBImage;
return 0;
}
return -1;
} //以下是线程函数的一部分,主要是取帧,然后进行人脸检测
{
lock_guard<std::mutex> locker(g_CameraMutex);
int ret = m_camera->getFrame(curFrame);
if (ret == -1)
{
continue;
}
} ftProcessor->faceDetect(curFrame);

  

2.基本图像格式转换
       目前虹软SDK支持以下几种图像数据格式:

在实际开发过程中一般使用opencv,opencv默认的图像数据格式是BGR24,而我使用的海康摄像头视频编码格式是H264,视频帧数据格式是YV12,因此需要将YV12转换为BGR24 ,同时也会说明下怎么转换为虹软SDK支持的其它格式,主要参考了[2],以下的代码仅供参考。
a.YV12 To BGR24

void yv12ToBGR24(unsigned char* yv12, unsigned  char* bgr24, int width, int height)
{
unsigned char* y_yv12 = yv12;
unsigned char* v_yv12 = yv12 + width*height;
unsigned char* u_yv12 = yv12 + width*height + width*height / 4; unsigned char* b = bgr24;
unsigned char* g = bgr24 + 1;
unsigned char* r = bgr24 + 2; int yIndex, uIndex, vIndex;
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
yIndex = i * width + j;
vIndex = (i / 2) * (width / 2) + (j / 2);
uIndex = vIndex; *b = (unsigned char)(y_yv12[yIndex] + 1.732446 * (u_yv12[vIndex] - 128));
*g = (unsigned char)(y_yv12[yIndex] - 0.698001 * (u_yv12[uIndex] - 128) - 0.703125 * (v_yv12[vIndex] - 128));
*r = (unsigned char)(y_yv12[yIndex] + 1.370705 * (v_yv12[uIndex] - 128)); b += 3;
g += 3;
r += 3;
}
}
}

  

b.YV12 To I420

void yv12ToI420(unsigned char yv12, unsigned char i420, int width, int height)
{ unsigned char* y_yv12 = yv12;
unsigned char* v_yv12 = yv12 + width*height;
unsigned char* u_yv12 = yv12 + width*height + width*height / 4; unsigned char* y_i420 = i420;
unsigned char* u_i420 = i420 + width*height;
unsigned char* v_i420 = i420 + width*height + width*height / 4; memcpy(i420, yv12, width*height);
memcpy(v_i420, v_yv12, width*height / 4);
memcpy(u_i420, u_yv12, width*height / 4);
}

  

c.YV12 To NV21

void yv12ToNV21(unsigned char yv12, unsigned char nv21, int width, int height)
{ unsigned char* y_yv12 = yv12;
unsigned char* v_yv12 = yv12 + width*height;
unsigned char* u_yv12 = yv12 + width*height + width*height / 4; unsigned char* y_nv21 = nv21;
unsigned char* v_nv21 = nv21 + width*height;
unsigned char* u_nv21 = nv21 + width*height + 1; memcpy(nv21, yv12, width*height); for (int i = 0; i < width*height / 4; ++i)
{
*v_nv21 = *v_yv12;
*u_nv21 = *u_yv12; v_nv21 += 2;
u_nv21 += 2; ++v_yv12;
++u_yv12;
}
}

  

d.YV12 To NV12

void yv12ToNV12(unsigned char yv12, unsigned char nv12, int width, int height)
{ unsigned char* y_yv12 = yv12;
unsigned char* v_yv12 = yv12 + width*height;
unsigned char* u_yv12 = yv12 + width*height + width*height / 4; unsigned char* y_nv12 = nv12;
unsigned char* u_nv12 = nv12 + width*height;
unsigned char* v_nv12 = nv12 + width*height + 1; memcpy(nv12, yv12, width*height); for (int i = 0; i < width*height / 4; ++i)
{
*v_nv12 = *v_yv12;
*u_nv12 = *u_yv12; v_nv12 += 2;
u_nv12 += 2; ++v_yv12;
++u_yv12;
}
}

  

e.YV12 To YUYV

void yv12ToYUYV(unsigned char yv12, unsigned char yuyv, int width, int height)
{ unsigned char* y_yv12 = yv12;
unsigned char* v_yv12 = yv12 + width*height;
unsigned char* u_yv12 = yv12 + width*height + width*height / 4; unsigned char* y_yuyv = yuyv;
unsigned char* u_yuyv = yuyv + 1;
unsigned char* v_yuyv = yuyv + 3; for (int i = 0; i < width; ++i)
{
for (int j = 0; j < height; ++j)
{
*y_yuyv = *y_yv12; y_yuyv += 2;
++y_yv12;
}
} for (int j = 0; j < height / 2; ++j)
{
for (int i = 0; i < width / 2; ++i)
{
*u_yuyv = *u_yv12;
*(u_yuyv + width * 2) = *u_yv12;
u_yuyv += 4;
++u_yv12; *v_yuyv = *v_yv12;
*(v_yuyv + width * 2) = *v_yv12;
v_yuyv += 4;
++v_yv12;
}
u_yuyv += width * 2;
v_yuyv += width * 2;
}
}

 

虹软免费的sdk下载: https://ai.arcsoft.com.cn/third/mobile.html?cnblogs

 

参考:
[1] 虹软AI 人脸识别SDK接入 — 性能优化篇(多线程)
[2] 图像实战 - 图像格式转换
[3] 微软官方解析图像格式
[4]一文读懂 YUV 的采样与格式

虹软人脸识别SDK在网络摄像头中的实际应用的更多相关文章

  1. 虹软人脸识别SDK接入Milvus实现海量人脸快速检索

    一.背景 人脸识别是近年来最热门的计算机视觉领域的应用之一,而且现在已经出现了非常多的人脸识别算法,如:DeepID.FaceNet.DeepFace等等.人脸识别被广泛应用于景区.客运.酒店.办公室 ...

  2. Android 关于虹软人脸识别SDK引擎使用总结

    虹软 最近开放了人脸识别的SDK引擎(免费的哦),刚好有Android版的,就体验了一波.下面来说说Android版的SDK使用心得: ArcFace 虹软人脸认知引擎简介 目前开放的版本有人脸比对( ...

  3. 关于虹软人脸识别SDK的接入

    背景: 虹软的人脸识别还是不错的,在官方注册一个账号,成为开发者,下载SDK的jar包,在开发者中心,找一个demo就可以开始做了,安装里边的逻辑,先看理解代码,然后就可以控制代码,完成自己想要的功能 ...

  4. 虹软人脸识别SDK的接入方法

    背景: 虹软的人脸识别还是不错的,在官方注册一个账号,成为开发者,下载SDK的jar包,在开发者中心,找一个demo就可以开始做了,安装里边的逻辑,先看理解代码,然后就可以控制代码,完成自己想要的功能 ...

  5. Android虹软人脸识别sdk使用工具类

    public class FaceUtil { private static final String TAG = FaceUtil.class.getSimpleName(); private st ...

  6. 虹软人脸识别SDK(java+linux/window)

    虹软官网:http://www.arcsoft.com.cn/ 登录后要实名认证才可以使用sdk. 下图这两个是我选择的,window版本地开发测试,linux版是生产环境使用. 1. 保存激活码,下 ...

  7. Android开发 打开已存在的项目(以虹软人脸识别sdk的demo为例)

    详细流程参考博客https://blog.csdn.net/z979451341/article/details/79468785 个人遇到的问题与注意点 1.下载Demo后忘记修改appid和sdk ...

  8. asp.net 虹软人脸识别sdk 释放内存

    初始化时申请内存,用完记得释放,不然就会报“内存已满”的. 使用时: pMem = Marshal.AllocHGlobal(detectSize); 释放内存: Marshal.FreeHGloba ...

  9. 虹软人脸识别 - Android Camera实时人脸追踪画框适配

    在使用虹软人脸识别Android SDK的过程中 ,预览时一般都需要绘制人脸框,但是和PC平台相机应用不同,在Android平台相机进行应用开发还需要考虑前后置相机切换.设备横竖屏切换等情况,因此在人 ...

随机推荐

  1. Java_jdbc 基础笔记之十 数据库连接 (ResultSetMetaData 类)

    ResultSetMetaData 类 调用ResultSet 的getMetaData()方法得到ResultSetMetaData 类对象: 可用于获取关于 ResultSet 对象中列的类型和属 ...

  2. Wise Force Deleter 强制删除文件工具

    https://www.xitmi.com/3321.html   Wise Force Deleter v1.49 中文绿色版 强制删除文件工具

  3. Windows10下安装Git

    Git是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理.具体安装步骤如下: 第一步:先从官网下载最新版本的Git 官网地址:https://git-scm.com/do ...

  4. 必备Docker命令

    Dockerfile常用指令 Docker常用操作指令 Docker管理指令 文章来源:https://macrozheng.github.io/mall-learning/#/reference/d ...

  5. [LeetCode] 109. Convert Sorted List to Binary Search Tree 把有序链表转成二叉搜索树

    Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...

  6. consul多数据中心搭建 【h】

    自建IDC后面简称own.阿里云机房ali.腾讯云机房txown机房:内网10.10.10.0/24,边界节点,10.10.10.100/101.xxx.80.xxxali机房:内网10.10.10. ...

  7. 进入docker 内部

    $ sudo docker ps $ sudo docker exec -it 775c7c9ee1e1 /bin/bash

  8. 使用 pthread_cancel 引入的死锁问题

    先来说一下 pthread_cancel 基本概念. pthread_cancel 调用并不是强制终止线程,它只提出请求.线程如何处理 cancel 信号则由目标线程自己决定,可以是忽略.可以是立即终 ...

  9. Maven中解决jar包冲突的三种方式

    首先我们在idea中创建一个maven工程,我们只关注pom.xml以及External Libraries中导入的jar包 导入spring-beans.jar <dependency> ...

  10. pip修改成国内镜像源

    临时指定镜像源 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple seaborn 永久修改镜像源 linux下,修改 ~/.pip/pip ...