把 SDK 从 C++ 搬到 Python 需要解决两个主要问题,一个是从 IP 摄像头中获取图像(码流),另一个是对云台进行控制(角度、倍率等)。为了解决以上两个问题,一共尝试了三个方案,实现过程从简单到复杂,在此总结一下其中各自的利弊。1. RTSP 协议获取图像在选择 HikVision 之前也关注了这方面的支持。RTSP 是流媒体协议,可以很方便地通过 URL 获取码流。它的协议如下1:
// 说明:
// username:用户名,例如admin
// passwd:密码,例如12345
// ip:设备的ip地址,例如192.0.0.64
// port:端口号默认554,若为默认可以不写
// codec:有h264、MPEG-4、mpeg4这几种
// channel:通道号,起始为1
// subtype:码流类型,主码流为main,子码流为sub
rtsp://[username]:[passwd]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
在 OpenCV 中,很简单就能读取 IP 摄像头。
import cv2
cam = cv2.VideoCapture("rtsp://[username]:[passwd]@[ip]:554/h264/ch1/sub/av_stream")
while True:
ret, frame = cam.read()
cv2.imshow("test", frame)
key = cv2.waitKey(100) & 0xff
if key == 27: # ESC
break
cam.release()
cv2.destroyAllWindows()
然而这么做存在一个很大的缺陷,即通过流媒体协议获得的码流没有办法满足实时要求,返回图像存在 3-5 秒延时,在实际项目中并不可行。另一方面,仅仅取得码流对于实际项目也并不足够,还需要想方设法对云台进行控制。于是,找到了下一种解决方案。
既然已经到走投无路的阶段,只能考虑利用 C++ 实现功能,再自行封装 Python 库了。这种实现方案确实很费劲,操作十分繁琐,但毕竟 SDK 有给出 C++ 的实现,算是最靠谱的方案,所以万不得已的我尝试了这种解决方案。
Swig 用于封装库,在 Windows 系统下,对应的「swigwin」版本,解压后将目录添加到系统「环境变量」。
该文件用于预编译 OpenCV 相关函数,是一系列.i后缀的文件。点击下载并解压
将上述接口文件中 lib 文件夹的所有文件拷贝到项目所在目录,并与三个源文件放置在一起。(注:源文件包括HKIPcamera.cpp、HKIPcamera.h和HKIPcamera.i,请回顾出处原文。代码基本与原文一致,需要新增功能也在HKIPcamera.cpp中实现,所以不在此展示。)
通过命令行使用 swig 生成HKIPcamera_wrap.cxx文件。cd到HKIPcamera.i源文件文件夹下,并修改 OpenCV 路径。如:
cd OpenCV Project\HKIPCamera\HKIPCamera
swig -I"D:\Open CV\opencv\build\include" -python -c++ HKIPCamera.i
修改plaympeg4.h文件。这一问题在原文章中有所提及:在extern “C” __declspec(dllexport)的"C"和__之间需要增加空格,否则会导致编译报错。
下载 boost 库。boost 库提供了一系列扩展的 C++ 方法,文件稍大。点击下载(Windows 平台)
编译动态链接库。新建Visual Studio 动态链接库工程并配置如下:
配置包含目录:
配置库目录:
配置依赖项:
不使用预编译头:
文件配置
在「属性管理器」添加 Python 头文件目录和库目录。再将HKIPcamera.h文件添加到头文件、HKIPcamera_wrap.cxx和HKIPcamera.cpp添加到源文件 、HKIPcamera.i 添加到工程目录下,进行编译。将生成的.dll文件改名为_HKIPcamera.pyd,并与HKIPcamera.py放置在同一文件目录下,即可在 Python 中引用。
由于 Python 只能接收Mat类型,原文3中解码回调DecCBFun部分可以改写为:
void CALLBACK DecCBFun(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, long nReserved1, long nReserved2)
{
long lFrameType = pFrameInfo->nType;
Mat g_BGRImage;
if (lFrameType == T_YV12)
{
if (g_BGRImage.empty())
{
g_BGRImage.create(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);
}
Mat YUVImage(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (unsigned char*)pBuf);
cvtColor(YUVImage, g_BGRImage, COLOR_YUV2BGR_YV12);
EnterCriticalSection(&g_cs_frameList);
g_frameList.push_back(g_BGRImage);
LeaveCriticalSection(&g_cs_frameList);
}
}