当有多个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 ..

编译opencv + cuda + cudnn 的注意事项

linux 版,需要使用ubuntu 发行版,避免使用centos 或其他RED HAT 系列的发行版,RED HAT 系列的发行版包不太好找,必须注意需要从nvidia 上下载官方的显卡或运算卡驱动,不要使用第三方的驱动。建议下载直接运行的后缀 .run  的包不要使用tar.gz  需要编译的包,这样可以更省事。另外需要注意的是.run  的包必须在命令行下运行,并且不要运行x window。如有x window  的进程在运行,需使用 systemctl stop gdm  关闭。安装显卡驱动后,下载并安装对应版本的nvidia  官方cuda tool kit。 下载 .run 版本即可。 需要注意的是,在安装过程中不要安装tool kit 包里的 显卡驱动,否则会报错。下载并解压 nvidia 官方的 cudnn 库,需要注意的是,新版本需要把include 和 lib  全部复制到 cuda  对应的目录,这里大多数网上文档都说只把cudnn.h 和 某几个头文件复制过去,这是错的,这是个坑,新版本这么干的话在编译OPENCV时会报找不到 cudnn 。最后,需要注意的是在编译OPENCV时,他会查找系统里的PYTHON JAVA等其他平台的相关库和运行环境并编译对应的库,LINUX上他默认找到并编译的是PYTHON2.7  对于现在流行的PYTHON 3.X 他默认是不查找的,需要在cmake  命令上加上对应的参数,让他查找PYTHON3.X 并且强制关闭PYTHON2 ,才能编译PYTHON3的库。编译后,需要在opencv build 目录下找到并运行python setup.py install 安装编译生成的OPENCV到python3环境,如果用的是VENV 的话,就要先activate VENV 然后再用VENV 的python 执行安装

Tensorflow 环境搭建

$ python3 -m venv _pml_

$ source _pml_/bin/activate

$ python -m pip install –upgrade pip

$ python -m pip install –upgrade setuptools

$ pip list

# 安装库

$ pip install tensorflow

$ pip install matplotlib seaborn

$ pip install Pillow opencv-python opencv-contrib-python

$ pip install scikit-learn

# linux 上需要的

$ sudo apt install python3-tk

springboot 打包外置配置文件

以springboot 2.2.1为例,在系统启动方法如 WebApplication 里添加注解

@PropertySource(value = {"file:${spring.profiles.path}/schedule_settings.properties"})

并在对应的配置文件实体添加同样的注解,如:

@PropertySource(value = {"file:${spring.profiles.path}/schedule_settings.properties"})
@ConfigurationProperties(prefix = "schedule")

在application.yml 或 application.properties 或 application-xxx.yml里添加spring.profiles.path 如:

profiles:
  path: ~/xxxxx/xxxxxxx/scheduletask/target

打包后,把自定义的配置文件如schedule_settings.properties 放在jar 包的同一个目录 或使用如下启动参数启动:

java -jar xxxxxxx.jar –spring.profiles.path=/xxxx/xxxxx/target

PPTP的 客户端之间的互相访问问题

两台主机,在两个不同的网络下,使用PPTP连接到在公网上的一台WIN2012 SERVER VPN上。这两台主机一台使用LINUX,一台使用WIN7/WIN10 。在两台主机分别成功连接到VPN服务器后,WIN7/WIN10 主机无法通过VPN的内网IP或LINUX的外网IP访问到LINUX主机。在LINUX 主机主通过 netstat -nr 命令,查看网关信息,显示LINUX主机的默认网关为原来所在网络的192.168.0.1,所以可以通过

sudo route del default (删除原来默认网关指向)

sudo route add default dev ppp0 (添加指向VPN的默认网关指向)

添加后用netstat -nr 查看默认网关如图,就可以通过VPN的内网IP访问LINUX主机