时间一晃,我已经是一名即将步入研三的老学长,趁着这个假期抓紧时间把毕业设计的大体框架完成,后续细节的优化工作再慢慢处理。毕设的课题是ROV采集与通信系统,简单来说就是ROV水下实时采集高清图像信息及各种传感器数据,通过光纤传输至水上经DDR3进行缓存,最后通过千兆以太网上传至上位机进行数据的可视化操作。整体的工作量相对来说还是比较大的,硬件部分设计会在之后的博客进行更新,今天主要来谈一下上位机设计,主要介绍udp数据的接收、图像数据的显示、传感器数据的可视化分析三部分。
udp—–数据报文协议,是一个无连接的简单的面向数据报的运输层协议,UDP不提供可靠性,他只是将应用程序传送给IP层的数据报文发送出去,并不保证能否达到目的地。由于UDP在传输的过程中不需要和服务器建立链接。且没有超时重发的的机制。故而传输速率较快,非常适合作为图像传输的方式。
udp数据的接收部分代码相对固定化,下面直接上代码:
def udp_connect(self):
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#local_addr = ('192.168.0.3', 8080)
# 绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配,下面是从pyqt5的文本框中获取ip地址 和端口号
local_addr = (str(self.ui.lineEdit_2.text()), int(self.ui.lineEdit.text()))
udp_socket.bind(local_addr)
#关闭套接字
udp_socket.close()
图像的显示此部分比较闹心,当时查阅资料并没有发现有关与RGB格式的图像数据流接收显示这部分内容,有的大多是服务器和客户端都采用Python实现,传输的视频流格式多是JPIE。由于传输数据是二进制流数据,所以客户端把需要传输的数据编码成二进制码流,服务器接收到再解码进行显示。
那么我的这个设计该怎么实现的图像显示呢???
像素点摆放
按像素点顺序一个像素一个像素摆放,当一帧图像的像素全部摆放完成,那么这帧数据也就成功显示出来。首先进行测试的是RGB565格式的图像数据,一个udp数据包传输一行图像数据,开头两字节为图像的行号。代码如下所示:
for y in range(480): # 接收一帧图像数据
data, addr = udp_socket.recvfrom(1282)
data_list[y] = data
# 创建一个三维数组,填充图像数据后准备成像
picture = np.zeros((480, 640, 3), dtype=np.uint8)
for y in range(480):
linenumber = (data_list[y][0] << 8) + data_list[y][1]
for x in range(640):
# 由于上传图像数据格式为RGB565,即两个字节表示一个像素值,所以需要进行通道分离,这里运用到RGB568——RGB888的方法:采用移位方式实现高位对齐;低位补零。
picture [linenumber, x] = [((data_list[y][2*x+2])& 0xF8), ((((data_list[y][2*x+2])& 0x07) <<3) + (data_list[y][2*x+3]& 0xE0) >> 5)<<2, ((data_list[y][2*x+3])& 0x1F)<<3]
# 调用Image将数组转换为图像
img = Image.fromarray(data5, 'RGB')
# 显示图像
img.imshow()
按像素点实现还有一种函数可采用,上代码:
def draw_picture(self):
self.image = QtGui.QImage()
self.im = Image.open("timg.jpg")
im_1 = self.im.resize((640, 480))#缩放
im_2 = Image.new("RGB",(640,480))#创建图片
for x in range(640):
for y in range(480):
im_2.putpixel((x, y),(255,255,0))
单通道函数显示黑白图像
为什么会有第2小节呢? em em em,还不是因为第一种没有成功…
这种结果其实也在意料之中,虽然C和C++的图像成像的确有这种方式,但是由于C和C++的执行效率远超Python,所以这种成像方式虽比较费事,也在承受范围之内。Python就不一样了,640*480分辨率的图像按像素操作要执行30多万次,结果可想而知。即使这样也不能放弃哈,因为Python最大的优势就行集成度高,拥有大量的现成函数可用。
现在进行测试的是单通道的图像数据,同样是一个udp数据包传输一行图像数据,开头两字节为图像的行号。代码如下所示:
while True:
frame_data, addr = self.udp_socket.recvfrom(642) # 接收数据
if((frame_data[0]<<8)+frame_data[1] == 1): # 按行号接收一帧分辨率为640*480的图像数据
break
for y in range(479):
row_data, addr = self.udp_socket.recvfrom(642) # 接收数据
frame_data = frame_data +row_data
print('ok')
# 数组拷贝
frame_list = list(frame_data)
self.video_display() # 图像显示
def video_receiving(self):
frame = np.array(frame_list).reshape(480,642) # 将数组转换为二维矩阵进行图像显示前的准备工作
frame_img = Image.fromarray(np.uint8(frame), 'L') # L代表每个像素8bit的灰度图
# im_1 = new_im.resize((320, 240)) # 缩放
self.image = ImageQt.toqpixmap(frame_img)
self.image.imshow()
三通道函数显示彩色图像
话不多说,直接上彩色图像的参考代码,如下所示:
def draw_color_image(self):
h, w = 640, 480
data = np.zeros((w, h, 3), dtype=np.uint8)
print(data)
data[240, 320] = [255, 255, 0]
img = Image.fromarray(data, 'RGB')
图像放置在pyqt5的Label中
要想将不同类生成的图像放置在pyqt5的Label中显示,首先需要将不同类的图像进行转换为QPixmap格式,然后调用函数就能成功显示。代码如下所示:
# PIL Image 与 pyqt5中Qimage互转
# self.image = ImageQt.toqimage(im_2)
self.image = ImageQt.toqpixmap(im_2)
self.ui.label_11.setPixmap(self.image)
# 调用QPixmap将本地图片显示至Label
pixmap = QtGui.QPixmap("timg.jpg").scaled(640,480)
self.ui.label_11.setPixmap(pixmap)
这部分后续更新