Python调用海康威视网络相机C++的SDK

Python调用海康威视网络相机C++的SDK

本文参考:https://blog.csdn.net/c20081052/article/details/95082377 笔者将此文章中遗漏或未遇见的问题归纳总结,并分享自己在运行过程中遇到的问题,与君共勉。


一、运行环境

Win10系统 (64位)

Python3.7 (若是有用Python2.x版本的童鞋,尽量升级Python版本。在实验过程中,有其他童鞋由于Python版本问题,无法读取Python库)

Opencv3_4_2 、Visual Studio 2015(尽量使用同一版本)

海康硬件IDS-2DC7S420MW-A

海康SDK:CH-HCNetSDKV6.1.6.45_build20210302_win64


二、相关下载

建议在D盘或者E盘新建文件夹 ,名字为:App_install 。
在这里插入图片描述

1、海康SDK下载(文末有百度网盘,可自提)

下载地址:https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10
Python调用海康威视网络相机C++的SDK_第1张图片

选择自己对应的系统版本,笔者选的是设备网络SDK_Win64。

将其解压,主要会用到里面的头文件与库文件。将其汉语名称头文件改为include、库文件改为lib。

2、安装Opencv(文末有百度网盘,可自提)

笔者安装的是Opencv3_4_2。
在这里插入图片描述

在官网上下载3.4.2版本,双击即可解压。建议放到App_install文件夹,解压后将其bin路径添加到系统环境变量path下。

3、安装Swigwin(文末有百度网盘,可自提)

Swigwin是用来封装库的。

下载链接:http://www.swig.org/index.php

笔者选择的是3.0.12这个版本(本人之前选择的是最新版本,但是由于不明确的错误问题,还是按引用文章的版本安装),将其安装并解压到App_install目录下。

将此文件名路径添加到系统环境变量path中。

4、下载boost库(文末有百度网盘,可自提)

下载地址:https://www.boost.org/users/history/

笔者选择的是 boost_1_70_0.zip (因为本文主要是将win10环境下的), 将其解压到待会工程需要用的路径下即可;此处还是先解压到App_install文件夹下,待会会将解压后的文件夹拷贝到后面要用的路径下。

5、下载OpenCV-swig接口文件(文末有百度网盘,可自提)

下载地址:https://github.com/renatoGarcia/opencv-swig 将其下载并解压到App_install文件夹下。

需要用到其目录下lib文件夹中的文件opencv和opencv.i接口文件。

至此,前期准备工作搞定。App_install文件夹下的文件如下图:

Python调用海康威视网络相机C++的SDK_第2张图片


三、实现步骤

1、使用swigwin编译生成Python和C++数据交换 的 .py和.cxx文件

复制以下三个文件(注意不要更改文件名),保存到同一文件夹下。笔者均将文件都放在新建文件夹 HicVision_python_SDK (建议名字一样)

HKIPcamera.h

#include 
using namespace cv;

void init(char* ip, char* usr, char* password);
Mat getframe();
void release();

HKIPcamera.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "HCNetSDK.h"
#include "plaympeg4.h"

#define USECOLOR 1

using namespace cv;
using namespace std;

//--------------------------------------------
int iPicNum = 0;//Set channel NO.
LONG nPort = -1;
HWND hWnd = NULL;
CRITICAL_SECTION g_cs_frameList;
list<Mat> g_frameList;
LONG lUserID;
NET_DVR_DEVICEINFO_V30 struDeviceInfo;
HANDLE hThread;
LONG lRealPlayHandle = -1;

void yv12toYUV(char *outYuv, char *inYv12, int width, int height, int widthStep)
{
	int col, row;
	unsigned int Y, U, V;
	int tmp;
	int idx;
	for (row = 0; row < height; row++)
	{
		idx = row * widthStep;
		int rowptr = row * width;

		for (col = 0; col < width; col++)
		{
			tmp = (row / 2)*(width / 2) + (col / 2);
	
			Y = (unsigned int)inYv12[row*width + col];
			U = (unsigned int)inYv12[width*height + width * height / 4 + tmp];
			V = (unsigned int)inYv12[width*height + tmp];
	 
			outYuv[idx + col * 3] = Y;
			outYuv[idx + col * 3 + 1] = U;
			outYuv[idx + col * 3 + 2] = V;
		}
	}

}

//解码回调 视频为YUV数据(YV12),音频为PCM数据
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
	long lFrameType = pFrameInfo->nType;

	if (lFrameType == T_YV12)
	{

#if USECOLOR
		//int start = clock();
		static IplImage* pImgYCrCb = cvCreateImage(cvSize(pFrameInfo->nWidth, pFrameInfo->nHeight), 8, 3);//得到图像的Y分量  
		yv12toYUV(pImgYCrCb->imageData, pBuf, pFrameInfo->nWidth, pFrameInfo->nHeight, pImgYCrCb->widthStep);//得到全部RGB图像
		static IplImage* pImg = cvCreateImage(cvSize(pFrameInfo->nWidth, pFrameInfo->nHeight), 8, 3);
		cvCvtColor(pImgYCrCb, pImg, CV_YCrCb2RGB);
		//int end = clock();
#else
		static IplImage* pImg = cvCreateImage(cvSize(pFrameInfo->nWidth, pFrameInfo->nHeight), 8, 1);
		memcpy(pImg->imageData, pBuf, pFrameInfo->nWidth*pFrameInfo->nHeight);
#endif

		EnterCriticalSection(&g_cs_frameList);
		//g_frameList.push_back(pImg);
		g_frameList.push_back(cv::cvarrToMat(pImg));
		LeaveCriticalSection(&g_cs_frameList);
    } 
}


///实时流回调
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
	DWORD dRet;
	switch (dwDataType)
	{
	case NET_DVR_SYSHEAD:    //系统头
		if (!PlayM4_GetPort(&nPort)) //获取播放库未使用的通道号
		{
			break;
		}
		if (dwBufSize > 0)
		{
			if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 1024))
			{
				dRet = PlayM4_GetLastError(nPort);
				break;
			}
			//设置解码回调函数 只解码不显示
			if (!PlayM4_SetDecCallBack(nPort, DecCBFun))
			{
				dRet = PlayM4_GetLastError(nPort);
				break;
			}
			//打开视频解码
			if (!PlayM4_Play(nPort, hWnd))
			{
				dRet = PlayM4_GetLastError(nPort);
				break;
			}
		}
		break;

	case NET_DVR_STREAMDATA:   //码流数据
		if (dwBufSize > 0 && nPort != -1)
		{
			BOOL inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
			while (!inData)
			{
				Sleep(10);
				inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
				OutputDebugString(L"PlayM4_InputData failed \n");
			}
		}
		break;
	}

}

void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
	char tempbuf[256] = { 0 };
	switch (dwType)
	{
	case EXCEPTION_RECONNECT:    //预览时重连
		printf("----------reconnect--------%d\n", time(NULL));
		break;
	default:
		break;
	}
}

bool OpenCamera(char* ip, char* usr, char* password)
{
	lUserID = NET_DVR_Login_V30(ip, 8000, usr, password, &struDeviceInfo);
	if (lUserID == 0)
	{
		cout << "Log in success!" << endl;
		return TRUE;
	}
	else
	{
		printf("Login error, %d\n", NET_DVR_GetLastError());
		NET_DVR_Cleanup();
		return FALSE;
	}
}
DWORD WINAPI ReadCamera(LPVOID IpParameter)
{
	//---------------------------------------
	//设置异常消息回调函数
	NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);

	//cvNamedWindow("Mywindow", 0);
	//cvNamedWindow("IPCamera", 0);
	 
	//HWND  h = (HWND)cvGetWindowHandle("Mywindow");
	//h = cvNamedWindow("IPCamera");
	//---------------------------------------
	//启动预览并设置回调数据流 
	NET_DVR_CLIENTINFO ClientInfo;
	ClientInfo.lChannel = 1;        //Channel number 设备通道号
	ClientInfo.hPlayWnd = NULL;     //窗口为空,设备SDK不解码只取流
	ClientInfo.lLinkMode = 1;       //Main Stream
	ClientInfo.sMultiCastIP = NULL;
	 
	LONG lRealPlayHandle;
	lRealPlayHandle = NET_DVR_RealPlay_V30(lUserID, &ClientInfo, fRealDataCallBack, NULL, TRUE);
	if (lRealPlayHandle < 0)
	{
		printf("NET_DVR_RealPlay_V30 failed! Error number: %d\n", NET_DVR_GetLastError());
		return -1;
	}
	else
		cout << "码流回调成功!" << endl;
	Sleep(-1);
	if (!NET_DVR_StopRealPlay(lRealPlayHandle))
	{
		printf("NET_DVR_StopRealPlay error! Error number: %d\n", NET_DVR_GetLastError());
		return 0;
	}
	NET_DVR_Logout(lUserID);
	NET_DVR_Cleanup();
	return 0;

}


void init(char* ip, char* usr, char* password) {
	//HANDLE hThread;
	//LPDWORD threadID;
	//---------------------------------------
	// 初始化
	NET_DVR_Init();
	//设置连接时间与重连时间
	NET_DVR_SetConnectTime(2000, 1);
	NET_DVR_SetReconnect(10000, true);
	OpenCamera(ip, usr, password);
	InitializeCriticalSection(&g_cs_frameList);
	hThread = ::CreateThread(NULL, 0, ReadCamera, NULL, 0, 0);
}

Mat getframe() {
	Mat frame1;
	EnterCriticalSection(&g_cs_frameList);
	while (!g_frameList.size()) {
		LeaveCriticalSection(&g_cs_frameList);
		EnterCriticalSection(&g_cs_frameList);
	}
	list<Mat>::iterator it;
	it = g_frameList.end();
	it--;
	Mat dbgframe = (*(it));
	(*g_frameList.begin()).copyTo(frame1);
	frame1 = dbgframe;
	g_frameList.pop_front();
	//imshow("camera", frame1);
	//waitKey(1);

	g_frameList.clear();   // 丢掉旧的帧
	LeaveCriticalSection(&g_cs_frameList);
	return(frame1);

}

void release() {
	::CloseHandle(hThread);
	NET_DVR_StopRealPlay(lRealPlayHandle);
	//关闭预览
	NET_DVR_Logout(lUserID);
	//注销用户
	NET_DVR_Cleanup();
}

HKIPcamera.i(若此文件不明白如何写的,可以多复制一份下文的opencv.i文件,将下面代码内容与文件名替换,点击保存即可)

/*  Example of wrapping a C function that takes a C double array as input using
 *  numpy typemaps for SWIG. */
%module HKIPcamera
%include <opencv/mat.i>
%cv_mat__instantiate_defaults
%header %{
    /*  Includes the header in the wrapper code */
    #include "HKIPcamera.h"
%}
 
%include "HKIPcamera.h"

将第二步相关下载中的opencv-swig-master\lib下的opencv文件夹和opencv.i 这两个文件复制到 HicVision_python_SDK文件夹下。

将解压的boost_1_70_0文件夹也一并拷贝到这个目录下。

将App_install\opencv3_4_2\build\include中opencv2文件夹复制到HicVision_python_SDK文件夹下。(注:此处与之前博主不同,是因为笔者在生成.py与.cxx文件时报错,将此文件复制后,运行成功)

Python调用海康威视网络相机C++的SDK_第3张图片

然后打开cmd,切到HicVision_python_SDK 这个文件夹下。

举例:本人HicVision_python_SDK 文件夹在D盘下。win+r,输入cmd,回车;输入d:,转到D盘,输入cd+空格+文件名 转到文件夹下。

在这里插入图片描述

然后输入指令

E:\Win32-MFC-Projects\HicVision_python_SDK>swig -IE:/App_install/opencv3_4_2/opencv/build/include -python -c++ HKIPcamera.i

指定你opencv安装的头文件路径,会生成上图所示的HKIPcamera_wrap.cxxHKIPcamera.py 两个文件(蓝色方框文件)。

2、修改海康SDK下的plaympeg4.h文件

该文件在App_install \ CH-HCNetSDKV6.0.2.35_build20190411_Win64 \ include 目录下:

Python调用海康威视网络相机C++的SDK_第4张图片

第五行,extern “C” __declspec(dllexport)的"C"和____之间需要增加空格,否则会导致编译报错。如下:

Python调用海康威视网络相机C++的SDK_第5张图片

3、用VS2015编译生成动态链接库文件

新建win32控制台应用 工程,选择"DLL",“空项目”;取消选择"安全开发生命周期(SDL)检测"。若为Visual Studio2019版,请自行在属性页中更改。

应用程序设置页参考如下:

Python调用海康威视网络相机C++的SDK_第6张图片

在资源方案管理器页将如下图所示的文件导入,即将上文提到的生成的文件拷贝到项目中(笔者起名为HKIPcamera项目名,建议一致)

img

编译平台是release x64下的,因此将配置该平台环境的属性页,点击 视图->属性页

在这里插入图片描述
Python调用海康威视网络相机C++的SDK_第7张图片

其中 VC++目录->包含目录中添加如下头文件路径:(请根据实际安装位置补全其全路径名称,笔者省去了有些安装路径名)

C:\Python3.7\include
C:\Python3.7\Lib\site-packages\numpy\core\include(以下类似)
HicVision_python_SDK\boost_1_70_0
CH-HCNetSDKV6.0.2.35_build20190411_Win64\include
opencv3_4_2\opencv\build\include\opencv2
opencv3_4_2\opencv\build\include\opencv
opencv3_4_2\opencv\build\include

VC++目录->库目录中添加如下库的路径:(自行补全实际安装路径名)

C:\Python3.7\libs
HicVision_python_SDK\boost_1_70_0\libs
CH-HCNetSDKV6.0.2.35_build20190411_Win64\lib
CH-HCNetSDKV6.0.2.35_build20190411_Win64\lib\HCNetSDKCom
opencv3_4_2\opencv\build\x64\vc14\lib

预处理器->预处理器定义中内容如下:

WIN32
NDEBUG
_CONSULE
_CRT_SECURE_NO_WARNINGS

链接器->输入->附加依赖项中添加:

opencv_world342.lib  (release下是这个,debug下需要改为opencv_world342d.lib)
HCNetSDK.lib
GdiPlus.lib
HCAlarm.lib
HCCore.lib
HCGeneralCfgMgr.lib
HCPreview.lib
PlayCtrl.lib

在通用属性->常规->字符集中选择使用多字节字符集

在C/C+±>代码生成->运行库选的是多线程DLL(/MD)

Python3.7中 include文件夹下:object.h 注释掉第56行 在这里插入图片描述

​ pyconfig.h 注释掉第330~332行
在这里插入图片描述


完成以上步骤后,右键工程项目名选择生成,之后会在工程x64文件夹下的Release文件夹下生成.dll和.lib文件如下(红色框):

Python调用海康威视网络相机C++的SDK_第8张图片

若生成成功 将注释掉的行数 恢复原样(不恢复貌似也没问题)。

在生成 HKIPcamera.dll文件与HKIPcamera.lib文件之后,复制两份 HKIPcamera.dll 文件,

其中一份重命名为 _HKIPcamera.pyd (注意文件名前面 _ ) 到 HicVision_python_SDK目录下,并将海康SDK中的 HCNetSDKCom 文件夹拷贝过来

以及将上文中用swig生成的HKIPcamera.py 也复制到HicVision_python_SDK文件夹下。

将 _HKIPcamera.pyd文件,包含目录路径,添加到系统环境变量Path中,并将 _HKIPcamera.pyd所在文件夹目录,也添加到系统环境变量目录中。如下:

Python调用海康威视网络相机C++的SDK_第9张图片

4、遇到问题

生成时遇到未定义问题,可以先查看本文引用文章,后面关于此问题解法。若仍无法解决,可能是C++版本问题,下载DirectX修复工具,修复Visual Studio后,再进行生成操作。

DirectX下载地址:https://blog.csdn.net/vbcom/article/details/6962388

下载后解压,点击在这里插入图片描述

允许其更改电脑;出来界面后,点击工具-> 选项->扩展->开始扩展

Python调用海康威视网络相机C++的SDK_第10张图片

扩展完成后,进入首页,点击检测并修复,若无问题,退出。

Python调用海康威视网络相机C++的SDK_第11张图片

若有其他问题,请查看DirectX官网解决方法。


四、测试

最后编写了个test.py也放在上图目录下,主要用来检测是否编译成功:

import HKIPcamera
import time
import numpy as np
import matplotlib.pyplot as plt
import cv2


ip = str('10.167.93.111')  # 摄像头IP地址,要和本机IP在同一局域网
name = str('admin')       # 管理员用户名
pw = str('abcd1234')        # 管理员密码
HKIPcamera.init(ip, name, pw)

HKIPcamera.getfram()

#for i in range(100):
while(True):
    t = time.time()
    fram = HKIPcamera.getframe()
    t2 = time.time()
    cv2.imshow('123', np.array(fram))
    cv2.waitKey(1)
    print(t2-t)
    time.sleep(0.1)
HKIPcamera.release()
time.sleep(5)
HKIPcamera.init(ip, name, pw)

笔者未一次成功,报错DLL找不到指定模块。

究其原因,是找不到.dll文件,故将所有链接的.dll文件移到.pyd同文件夹下。

首先,将此文件夹下文件全部复制到.pyd文件夹下,其中.dll文件会重复,跳过或者覆盖均可。

Python调用海康威视网络相机C++的SDK_第12张图片

其次,将海康威视文件夹中lib文件夹下所有文件复制到.pyd文件夹下。

Python调用海康威视网络相机C++的SDK_第13张图片

进行完上述操作后,多数人运行test均成功,但笔者依旧报错dll找不到指定模板。

不能放弃呀。

笔者又编写测试test1代码,目的是查看_HKIPcamera.pyd是否正常

import _HKIPcamera

print(dir(_HKIPcamera))
print(help(_HKIPcamera))

不失所望,它依旧运行不出来。

下载depends(后文有网盘链接可自行下载),运行其应用程序,将_HKIPcamera.pyd拖到其界面,系统自动分析,分析过后出现找不到文件的情况。

Python调用海康威视网络相机C++的SDK_第14张图片

寻找其主要原因。笔者这边主要是找不到三个.dll文件:分别是python37.dll,opencv_WORLD342.dll,VCRUNTIME140_1.dll。在我的电脑中,搜索这三个文件,将其复制到.pyd同目录文件中。

Python调用海康威视网络相机C++的SDK_第15张图片

若是你缺少与笔者不同的.dll文件,按照与笔者相同的方法即可。

然后重启电脑,打开test1。它就成功了~~


本文中用到的文件:

Swigwin-3.0.12:链接:https://pan.baidu.com/s/1VK_Pb7VcbwVxwwJzKDWuhA 提取码:g3mo

Opencv-swig-master:链接:https://pan.baidu.com/s/1qAa2P4040AROx_a16XZ63Q 提取码:vnw0

boost_1_70_0:链接:https://pan.baidu.com/s/17i4cutVICiiFRy-UEqiCyw 提取码:jgb2

海康威视SDK软件包:链接:https://pan.baidu.com/s/1vMV1bk4EfDrEzJLTweCRzg 提取码:xhwc

Visual Studio2015(64):链接:https://pan.baidu.com/s/1FvxGZWW19I7rIgBuo1itSQ 提取码:noby

DirectX_Repair:链接:https://pan.baidu.com/s/1iNnb6YzJGqCUKxOTCJP_-g 提取码:qruk

depends(汉化版):链接:https://pan.baidu.com/s/1xOAfAQIj-QrR5bYD9bYGhw 提取码:s0r0

Python-3.7.0:链接:https://pan.baidu.com/s/13QdAw4zeV4D4WJfKAOJFUQ 提取码:iyfl


再次声明:本文参考此文https://blog.csdn.net/c20081052/article/details/95082377,并在此基础上查漏补缺。

你可能感兴趣的:(python,c++,opencv)