当有多个GPU时,调用tensorflow 出现的问题和解决方法

使用PYTHON 调用tensorflow的GPU的一般方法如下:

physical_devices = tf.config.experimental.list_physical_devices(‘GPU’)
tf.config.experimental.set_memory_growth(physical_devices[0], True)

但这样有个问题,当主机有多个GPU时,会抛出一个错误,大致意思是不能使用不同GPU的显存。遇到这个问题,在百度上找到大多数的解决方法是,在这段代码之前加入如下:

os.environ[‘CUDA_VISIBLE_DEVICES’]=’0′

这样,代码是能正常运行了,但这样会有另一个问题,就是主机上明明有两个甚至更多的GPU,但程序只能使用其中一个运行,这大大浪费了主机的机能。之后,阅读了外国的社区和网站,看到遇到同样问题的技术人员的回复和解决方法,终于找到了最好的解决方法。修改GPU调用的代码如下:

例如有两个GPU
os.environ[‘CUDA_VISIBLE_DEVICES’]=’0,1′
physical_devices = tf.config.experimental.list_physical_devices(‘GPU’)
for gpu_instance in physical_devices:
tf.config.experimental.set_memory_growth(gpu_instance,True)

经测试,这样就可以同一个程序同时使用多个GPU处理了。

使用C++调用海康SDK取摄像头视频,使用SWIG 封装成PYTHON

先说明一下这么干的背景。因为项目需要,视频需要转成可以直接在浏览器上播放的流格式,或者直接在浏览器显示的格式。浏览器使用的是FIREFOX 或者CHROME 之类的浏览器。另外,还需要极低的视频延迟,最好少于1秒的延迟。一开始走了不少弯路,海康摄像头本身支持RTSP 流协议,但经测试,使用这个协议取流的话,本身就有大约0.5秒到1秒的延迟,如果再对流作进一步的处理的话,就会产生约2秒或以上的延迟。最后,只有使用海康SDK的私有协议来取流。经测试,SDK私有协议取流是几乎没有延迟的,但海康SDK取流只有C++,所以,就有了C++调用SDK取视频流,再封装接口给PYTHON输出到浏览器这个做法。

C++核心部分的代码:

int cam_midware_cc::InitCam(char* camId,char* userName,char* userPwd)
{
//—————————————
// 初始化
NET_DVR_Init();
//设置连接时间与重连时间
NET_DVR_SetConnectTime(2000, 1);
NET_DVR_SetReconnect(10000, true);
//—————————————
// 注册设备
NET_DVR_DEVICEINFO_V30 struDeviceInfo;
GUserID = NET_DVR_Login_V30((char*)camId, 8000, (char*)userName, (char*)userPwd, &struDeviceInfo);
if (GUserID < 0)
{
printf(“Login error, %d\n”, NET_DVR_GetLastError());
NET_DVR_Cleanup();
}
//—————————————
//设置异常消息回调函数
NET_DVR_SetExceptionCallBack_V30(0, NULL, G_ExceptionCallBack, NULL);
    returnGUserID;
}
int cam_midware_cc::StartVideo(int userId)
{
LONG lRealPlayHandle;
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = 0; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
struPlayInfo.lChannel = 1; //预览通道号
struPlayInfo.dwStreamType = 1; //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
lRealPlayHandle = NET_DVR_RealPlay_V40(userId, &struPlayInfo, G_RealDataCallBack, NULL);
if (lRealPlayHandle < 0)
{
printf(“NET_DVR_RealPlay_V40 error\n”);
printf(“%d\n”, NET_DVR_GetLastError());
NET_DVR_Logout(userId);
NET_DVR_Cleanup();
return -1;
}
return0;
}
//解码回调 视频为YUV数据(YV12),音频为PCM数据
void CALLBACK G_DecCBFun(int nPort, char * pBuf, int nSize, FRAME_INFO * pFrameInfo, void* nReserved1, int nReserved2)
{
if (G_gbHandling)
{
G_gbHandling–;
return;
}
long lFrameType = pFrameInfo->nType;
if (lFrameType == T_YV12)
{
Mat pImg(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);
Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);
cvtColor(src, pImg, CV_YUV2BGR_YV12);
outMat=pImg;
G_video_index++;
}
G_gbHandling = 3;
}
///实时流回调
void CALLBACK G_RealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
switch (dwDataType)
{
caseNET_DVR_SYSHEAD: //系统头
if (!PlayM4_GetPort(&G_nPort)) //获取播放库未使用的通道号
{
break;
}
//m_iPort = lPort; //第一次回调的是系统头,将获取的播放库port号赋值给全局port,下次回调数据时即使用此port号播放
if (dwBufSize > 0)
{
if (!PlayM4_SetStreamOpenMode(G_nPort, STREAME_REALTIME)) //设置实时流播放模式
{
break;
}
if (!PlayM4_OpenStream(G_nPort, pBuffer, dwBufSize, 10 * 1024 * 1024)) //打开流接口
{
break;
}
if (!PlayM4_Play(G_nPort, NULL)) //播放开始
{
break;
}
if (!PlayM4_SetDecCallBack(G_nPort, G_DecCBFun))
{
break;
}
}
break;
case NET_DVR_STREAMDATA: //码流数据
if (dwBufSize > 0 && G_nPort != -1)
{
if (!PlayM4_InputData(G_nPort, pBuffer, dwBufSize))
{
cout<<“error”<<PlayM4_GetLastError(G_nPort) <<endl;
break;
}
}
break;
default: //其他数据
if (dwBufSize > 0 && G_nPort != -1)
{
if (!PlayM4_InputData(G_nPort, pBuffer, dwBufSize))
{
break;
}
}
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;
}
}
Mat cam_midware_cc::getVideoFrame()
{
return outMat;
}
这里使用了opencv 用来,所,需要下载opencv-swig 用于把C++的代码封装成SO或者DLL库
opencv-swig下载地址:
swig下载地址:
swig 在linux 上需要编译,直接 make make install 就可以了
最后还需要编写一个用来让SWIG生成对应的中间代码的 .i 文件
我写的.i 文件如下:

%module cam_midware_cc

%include <opencv.i>
%cv_instantiate_all_defaults
%{
#include “cam_midware_cc.h”
%}
%include “cam_midware_cc.h”

SWIG 的文件弄好后使用SWIG 编译,我使用的是LINUX的方法,命令如下:
swig -c++ -python cam_medware_cc.i
然后再把SWIG 生成的 .cxx 文件连同自己编写的CPP 文件编译一下就可以生成需要的.SO 或 .DLL 文件。
PYTHON 调用的核心代码如下:
import cam_midware_cc
import cv2
import numpy as np
import time
camApi=cam_midware_cc.cam_midware_cc()
testStr=camApi.testLib()
testInitCam=camApi.InitCam(IP,USERNAME,PASSWORD)
print(testStr)
print(testInitCam)
if testInitCam>-1 :
testStartVideo=camApi.StartVideo(testInitCam)
print(testStartVideo)
time.sleep(5)
whileTrue:
    cv_img=camApi.getVideoFrame()
    img_cv2=cv2.blur(np.asarray(cv_img),(1,1))
    cv2.imshow(‘aa’,img_cv2)
    c= cv2.waitKey(30) & 0xff

一个编译opencv+contrib+cuda+cudnn 的命令与参数

在ubuntu 下使用cmake 编译opencv +contrib+cuda+cudnn

cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=/root/opencv_contrib-4.5.1/modules -D BUILD_OPENCV_PYTHON2=OFF -D BUILD_opencv_python3=ON -D PYTHON_DEFAULT_EXECUTABLE=/usr/bin/python3 -D PYTHON_INCLUDE_DIR=/usr/include/python3.8 -D PYTHON_LIBRARY=/usr/lib/x86_64-index-gnu/python3.8 -D WITH_TBB=ON -D WITH_CUDA=ON -D OPENCV_DNN_CUDA=ON -D CUDA_ARCH_BIN=6.1 -D OPENCV_PC_FILE_NAME=opencv.pc ..