在我们使用docker、wsl或者使用ssh连接linux服务器处理图像时,由于只存了命令行,无法在调试代码的过程中直观的显示图像辅助调试。为了解决这个痛点,可以将图像实时发送到本地带图像界面的电脑上并且显示图像!原理就是在本地建立一个tcp服务器一直接收发送过来的图像。python(c++后续有需要会写)例程代码如下:
"""
Remote display image
远程显示图像的库
"""
import socket
import cv2
import numpy
class RDIs():
"""
Remote display image server 远程显示图像的服务端
通过传入 address = ('0.0.0.0', 8001) 来改变本地ip的地址和端口
"""
localAddress = ''
connect = ''
frame = ''
sock = ''
def __init__(self, address=('0.0.0.0', 8001)):
# 首先检查输入参数是否正确
if not (isinstance(address, tuple) and isinstance(address[0], str) and isinstance(address[1], int)):
print(
"\033[31m错误: 请输入正确的ip:port, 例如: address=('0.0.0.', 8002)\033[0m")
return
# 配置本地socket
self.localAddress = address
# socket.AF_INET:服务器之间网络通信 socket.SOCK_STREAM:流式socket , for TCP
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind(address)
self.sock.listen(1)
def __del__(self):
self.sock.close()
def recvFrame(self):
""" 开始接收图像并显示 """
self.connect, self.localAddress = self.sock.accept()
print(self.localAddress[0]+':',
self.localAddress[1], " 连接成功!\n正在接收数据并显示图像...")
cv2.namedWindow('ReceivedImage', cv2.WINDOW_NORMAL)
cv2.resizeWindow('ReceivedImage', 1600, 900)
cv2.moveWindow('ReceivedImage', 0, 0)
# 循环接收图像,socket.MSG_WAITALL参数保证每次必须接收到足够的数据才返回
while True:
length = self.connect.recv(16, socket.MSG_WAITALL)
if length is not b'':
strData = self.connect.recv(
int(length), socket.MSG_WAITALL) # 根据获得的文件长度,获取图片文件
if strData is not b'':
imgData = numpy.frombuffer(
strData, numpy.uint8) # 将获取到的字符流数据转换成1维数组
frame = cv2.imdecode(imgData, cv2.IMREAD_COLOR) # 将数组解码成图像
self.frame = frame
cv2.imshow('ReceivedImage', self.frame) # 显示图像
if cv2.waitKey(1) & 0xFF == ord('q'):
break # 退出
cv2.destroyAllWindows()
self.sock.close()
class RDIc():
"""
Remote display image client 远程显示图像的客户端
通过传入 address = ('127.0.0.1', 8001) 来改变本地ip的地址和端口
"""
address = ''
frame = ''
sock = ''
def __init__(self, address=('127.0.0.1', 8001)):
# 首先检查输入参数是否正确
if not (isinstance(address, tuple) and isinstance(address[0], str) and isinstance(address[1], int)):
print(
"\033[31m错误: 请输入正确的ip:port, 例如: address=('127.0.0.1', 8001)\033[0m")
return
self.address = address
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接失败的话会一直尝试重连
while True:
try:
self.sock.connect(self.address)
print(self.address[0]+':', self.address[1], " 连接成功!")
break
except:
print('\033[31m', self.address[0]+':',
self.address[1], " 连接失败,尝试重连!\033[0m")
def __del__(self):
self.sock.close()
def sendFrame(self, frame, encode_param=[int(cv2.IMWRITE_JPEG_QUALITY), 10]):
""" 发送一帧图像 """
self.frame = frame
retval, imgencode = cv2.imencode('.jpg', frame, encode_param)
if retval is True:
data = numpy.array(imgencode)
stringData = data.tobytes()
self.sock.send(str.encode(str(len(stringData)).ljust(16)))
self.sock.send(stringData)
# 使用例程
if __name__ == '__main__':
# 开启另一个线程来开启RDI服务端用于接收和显示图像
# 实际使用过程中,两行代码就行了。实例化对象后运行recvFrame
# server = RDIs(address=('0.0.0.0', 8001))
# server.recvFrame()
import threading
server = RDIs(address=('0.0.0.0', 8001))
serverThread = threading.Thread(target=server.recvFrame)
serverThread.start()
# 开启RDI客户端,然后用sendFrame来发送图像
import datetime
rdic = RDIc(address=('127.0.0.1', 8001))
while True:
# 使用Numpy创建一张A4(1600*900)图像
img = numpy.zeros((900, 1600, 3), numpy.uint8)
cv2.putText(img, str(datetime.datetime.now()), (200, 400),
cv2.FONT_HERSHEY_COMPLEX, 2, (255, 255, 255))
rdic.sendFrame(img)
由于这里是单项通信,所以要求客户端主机能够ping通服务端主机就行。也就是说,可以将服务端部署在有公网ip的服务器上的,客户端在本地,也是同样能工作的。但是反过来不行。
如果要云端服务器向本地发送图像的话,由于本地没有公网ip,可以采用先本地去连接云端,然后连接建立之后再从服务器发送图像到本地。(请自行修改代码)