虹軟人臉識別SDK在網絡攝像頭中的實際應用

      目前在人臉識別領域中,網絡攝像頭的使用很廣泛,但接入網絡攝像頭和人臉識別SDK有必定門檻,在此篇中介紹過虹軟人臉識別SDK的接入流程,本文着重介紹網絡攝像頭獲取視頻流並處理的流程(紅色框內),如下內容僅供參考。html

      市面上目前有不少款網絡攝像頭,以海康攝像頭爲例。海康SDK包含不少接口,接入有必定難度,這裏只介紹獲取視頻幀相關的接口。
1.海康SDK接入基本流程
a.初始化並登陸驗證windows

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 BGR24ide

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 I42函數

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 NV2post

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?oschina

參考:
[1] 虹軟AI 人臉識別SDK接入 — 性能優化篇(多線程)
[2] 圖像實戰 - 圖像格式轉換
[3] 微軟官方解析圖像格式
[4]一文讀懂 YUV 的採樣與格式

相關文章
相關標籤/搜索