如何使用 pyqt 读取串口传输的图像
前言
这学期选修了嵌入式系统的课程,大作业选择的题目是人脸口罩检测。由于课程提供的开发板搭载的芯片是 STM32F103ZET6,跑不动神经网络,所以打算将 OV7725 拍摄到的图像通过串口传输给上位机处理。关于人脸口罩检测可以参见上一篇博客《如何使用 Yolov4 训练人脸口罩检测模型》,本篇博客的代码放在了 https://github.com/zhiyiYo/Face-Mask-Detector,下面进入正题。
串口传输图像
为了让上位机知道什么时候开始传输一张图像,需要在传输像素之前先发一段 header,这里使用的是 image:。在 header 的最后添加换行符的理由是方便上位机可以一次读入一行字符串来和 image: 进行比对。
发送完 header 就可以开始传输像素了。OV7725 输出的图像色彩模式为 RGB565,图像深度只有 16 位,用 2 个字节表示一个像素。在读取摄像头的 FIFO 芯片中存储的像素时,先读到的是像素的高 8 位(R<<5 | G[:3]),接着是低 8 位(G[3:]<<3 | B)。使用 HAL_UART_Transmit() 将像素发送给上位机即可,这里没有使用 printf 来传输,因为 printf 速度慢一些。每传输完一列像素就发送一个换行符,这样上位机通过 serial.readline() 读取 header 的时候就不会把上一张图像最后一列的像素给读进来了。
// 发送帧头
printf("image:\n");
// 在 LCD 上显示图像并将像素发给上位机
uint8_t high, low;
for (uint32_t i = 0; i < HEIGHT; ++i)
{
// 一列数据
for (uint32_t j = 0; j < WIDTH; ++j)
{
// 读颜色的高八位
setReadClock(GPIO_PIN_RESET);
// high = GPIOC->IDR & 0XFF;
high = GPIOC->IDR & 0XFF;
setReadClock(GPIO_PIN_SET);
// 读颜色的低八位
setReadClock(GPIO_PIN_RESET);
low = GPIOC->IDR & 0XFF;
setReadClock(GPIO_PIN_SET);
lcd_->drawPoint((high << 8) | low);
// 发送像素给上位机
HAL_UART_Transmit(&huart1, &high, 1, 100);
HAL_UART_Transmit(&huart1, &low, 1, 100);
}
printf("\n");
}
串口读取图像
如果直接在主线程读取串口的数据会造成主界面卡顿,所以创建一个子线程 SerialThread 来读图,每读完一张图像就发送 loadImageFinished 信号给主界面来显示图像。
在读取图像的像素之前需要将 s.readline()[:-1].decode("utf-8", "replace") 与 image: 进行比较,如果相等就说明下面拿到的就是图像数据,否则当前读取的是还没传输完成的图像的像素。之后使用 s.read(column_len) 读入一列像素并将其添加到列表 data 中,由于 OV7725 工作在 QVGA 模式,输出的图像是 320*240 分辨率的,一个像素 2 字节,data 的长度等于 320*240*2 即 153600 就表明完成了一张完整图像的读取。接下来把 RGB565 的像素缩放到 RGB888 的像素并组装为 320*240的图像即可。
实验过程中发现波特率不能取太高,否则读取的图像会有奇怪的条纹,所以这里使用的波特率为 1500000。
# coding: utf-8
from PIL import Image
import numpy as np
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QPixmap, QImage
from serial import Serial
def imageToQPixmap(image: Image.Image):
""" 将图像转换为 `QPixmap`
Parameters
----------
image: `~PIL.Image` or `np.ndarray`
RGB 图像
"""
image = np.array(image) # type:np.ndarray
h, w, c = image.shape
format = QImage.Format_RGB888 if c == 3 else QImage.Format_RGBA8888
return QPixmap.fromImage(QImage(image.data, w, h, c * w, format))
def rgb565ToImage(pixels: list) -> QPixmap:
""" 将 RGB565 图像转换为 RGB888 """
image = []
for i in range(0, len(pixels), 2):
pixel = (pixels[i] << 8) | pixels[i+1]
r = pixel >> 11
g = (pixel >> 5) & 0x3f
b = pixel & 0x1f
r = r * 255.0 / 31.0
g = g * 255.0 / 63.0
b = b * 255.0 / 31.0
image.append([r, g, b])
image = np.array(image, dtype=np.uint8).reshape(
(240, 320, 3)).transpose((1, 0, 2))
return imageToQPixmap(Image.fromarray(image))
class SerialThread(QThread):
""" 串口线程 """
loadImageFinished = pyqtSignal(QPixmap)
def __init__(self, parent=None):
super().__init__(parent)
self.serial = Serial(baudrate=1500000)
self.isStopped = False
def run(self):
""" 将串口传输的字节转换为图像 """
data = []
self.serial.port = config.get(config.serialPort)
with self.serial as s:
while not self.isStopped:
if not s.isOpen():
s.open()
# 等待 header
header = s.readline()[:-1]
if header.decode("utf-8", "replace") != "image:":
continue
# 读入像素,丢弃换行符
column_len = 320*2+1
while len(data) < 2*320*240:
image_line = s.read(column_len)
data.extend(image_line[:-1])
self.loadImageFinished.emit(rgb565ToImage(data))
data.clear()
def stop(self):
""" 停止从串口读取图像 """
self.isStopped = True
self.serial.close()
def loadImage(self):
""" 开始从串口读取图像 """
self.isStopped = False
self.start()
软件界面如下图所示,只要点击工具栏最左侧的摄像头按钮就能从串口读取图像。

后记
至此使用 pyqt 读取串口传输图像的方法就介绍完毕了,以上~~
如何使用 pyqt 读取串口传输的图像的更多相关文章
- ARM 开发板嵌入式linux系统与主机PC通过串口传输文件
本文转载自http://useless20.blog.163.com/blog/static/237409982010227127576/ 嵌入式linux系统与主机通过串口传输文件 我想如果要从PC ...
- SecureCRT连接开发板 串口传输、tftp传输
1.串口传输 使用命令:rx r是service, x是X-model模式 ①.rx 文件名 再按Enter键 ②.将需要传到板子上的文件 拖到SecureCRT里面,选择发送X-model选项 注 ...
- C#利用控件mscomm32.ocx读取串口datalogic扫描枪数据
1).开发环境VS12,语言C# 2).扫描枪品牌:datalogic 4470 3).通讯协议:串口 1.首先,第一步创建一个新工程,windows窗体应用程序,命名为TestScanner,如下: ...
- python3 读取串口数据
python3 读取串口数据 demo import serial import time ser = serial.Serial("COM3",115200,timeout = ...
- (5)air202读取串口数据并上传到阿里云显示
一.首先进行云端设置 根据串口助手显示的信息,以及模块文档说明我们可以知道 其中red和ir是红光LED的原始数据, HR表示心率值, HRvalid是心率是否有效标识, SP02是血氧数值,,SPO ...
- C#SerialPort如何读取串口数据并显示在TextBox上
SerialPort中串口数据的读取与写入有较大的不同.由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取.一.线程实时读串口:二.事件触发方式实现. 由于线程实时读串口的效率不是十分高 ...
- VB.net 利用SerialPort进行读取串口操作
Imports SystemImports System.IO.Ports Public Class Form1 Private Sub Form1_Load(ByVal sender As Syst ...
- 串口传输文件 lrzsz
假设有一种开发环境,一块板子,除了串口,没有任何外部出入输出设备,没有sd卡,没有网线,这个时候如果你想跟这块板子传输交互文件,要怎么办? 根据modem所采用的文件传输协议:xmodem,ymode ...
- 使用sz/rz基于串口传输文件
关键词:lrzsz.minicom.ZMODEM.MD5sum等. 在环境受限的嵌入式系统上,往往只有串口可以使用. 此时如果需要传输文件,需要借助rz/sz工具,可以使用的传输协议有ZMODEM.Y ...
随机推荐
- 技术分析 | 浅谈在MySQL体系下SQL语句是如何在系统中执行的及可能遇到的问题
欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 SQL语句大家并不陌生,但某种程度上来看,我们只是知道了这条语句是什么功能,它可 ...
- Redis 07 有序集合
参考源 https://www.bilibili.com/video/BV1S54y1R7SB?spm_id_from=333.999.0.0 版本 本文章基于 Redis 6.2.6 Zset 就是 ...
- 用VS Code搞Qt6:至简窗口部件——QWidget
在正题开始之前,老周照例扯点别的.嗯,咱们扯一下在 VS 2022 下结合 CMake 开发 Qt6 时的环境变量设置问题.在VS Code 中,通够通过 CMake Tools 扩展的配置来设置环境 ...
- java-循环
1.循环:反复执行一段相同或相似的代码(逻辑相似或者相同)2.循环三要素: 1.循环变量的初始化 2.循环的条件(以循环变量为基础) 3.循环变量的改变(向着循环的结束变)循环变量:在整个循环过程中所 ...
- Bert不完全手册7. 为Bert注入知识的力量 Baidu-ERNIE & THU-ERNIE & KBert
借着ACL2022一篇知识增强Tutorial的东风,我们来聊聊如何在预训练模型中融入知识.Tutorial分别针对NLU和NLG方向对一些经典方案进行了分类汇总,感兴趣的可以去细看下.这一章我们只针 ...
- Spring 16: SM(Spring + MyBatis) 注解式事务 与 声明式事务
Spring事务处理方式 方式1:注解式事务 使用@Transactional注解完成事务控制,此注解可添加到类上,则对类中所有方法执行事务的设定,注解添加到方法上,则对该方法执行事务处理 @Tran ...
- B树-插入
B树系列文章 1. B树-介绍 2. B树-查找 3. B树-插入 4. B树-删除 插入 根据B树的以下两个特性 每一个结点最多有m个子结点 有k个子结点的非叶子结点拥有 k − 1 个键 可以得出 ...
- spring native 初体验实现 小米控制美的空调
目前关于 spring native 分享的文章还比较少 写这篇文章的主要目前是分享一下自己写的一个 小米控制美的空调 的程序 集成 spring native 过程中碰到的一些问题和解决方法 先放地 ...
- [机器学习]-分类问题常用评价指标、混淆矩阵及ROC曲线绘制方法
分类问题 分类问题是人工智能领域中最常见的一类问题之一,掌握合适的评价指标,对模型进行恰当的评价,是至关重要的. 同样地,分割问题是像素级别的分类,除了mAcc.mIoU之外,也可以采用分类问题的一些 ...
- dotnet 为大型应用接入 ApplicationStartupManager 启动流程框架
对于大型的应用软件,特别是客户端应用软件,应用启动过程中,需要执行大量的逻辑,包括各个模块的初始化和注册等等逻辑.大型应用软件的启动过程都是非常复杂的,而客户端应用软件是对应用的启动性能有所要求的,不 ...