前言

这学期选修了嵌入式系统的课程,大作业选择的题目是人脸口罩检测。由于课程提供的开发板搭载的芯片是 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 的时候就不会把上一张图像最后一列的像素给读进来了。

  1. // 发送帧头
  2. printf("image:\n");
  3. // 在 LCD 上显示图像并将像素发给上位机
  4. uint8_t high, low;
  5. for (uint32_t i = 0; i < HEIGHT; ++i)
  6. {
  7. // 一列数据
  8. for (uint32_t j = 0; j < WIDTH; ++j)
  9. {
  10. // 读颜色的高八位
  11. setReadClock(GPIO_PIN_RESET);
  12. // high = GPIOC->IDR & 0XFF;
  13. high = GPIOC->IDR & 0XFF;
  14. setReadClock(GPIO_PIN_SET);
  15. // 读颜色的低八位
  16. setReadClock(GPIO_PIN_RESET);
  17. low = GPIOC->IDR & 0XFF;
  18. setReadClock(GPIO_PIN_SET);
  19. lcd_->drawPoint((high << 8) | low);
  20. // 发送像素给上位机
  21. HAL_UART_Transmit(&huart1, &high, 1, 100);
  22. HAL_UART_Transmit(&huart1, &low, 1, 100);
  23. }
  24. printf("\n");
  25. }

串口读取图像

如果直接在主线程读取串口的数据会造成主界面卡顿,所以创建一个子线程 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。

  1. # coding: utf-8
  2. from PIL import Image
  3. import numpy as np
  4. from PyQt5.QtCore import QThread, pyqtSignal
  5. from PyQt5.QtGui import QPixmap, QImage
  6. from serial import Serial
  7. def imageToQPixmap(image: Image.Image):
  8. """ 将图像转换为 `QPixmap`
  9. Parameters
  10. ----------
  11. image: `~PIL.Image` or `np.ndarray`
  12. RGB 图像
  13. """
  14. image = np.array(image) # type:np.ndarray
  15. h, w, c = image.shape
  16. format = QImage.Format_RGB888 if c == 3 else QImage.Format_RGBA8888
  17. return QPixmap.fromImage(QImage(image.data, w, h, c * w, format))
  18. def rgb565ToImage(pixels: list) -> QPixmap:
  19. """ 将 RGB565 图像转换为 RGB888 """
  20. image = []
  21. for i in range(0, len(pixels), 2):
  22. pixel = (pixels[i] << 8) | pixels[i+1]
  23. r = pixel >> 11
  24. g = (pixel >> 5) & 0x3f
  25. b = pixel & 0x1f
  26. r = r * 255.0 / 31.0
  27. g = g * 255.0 / 63.0
  28. b = b * 255.0 / 31.0
  29. image.append([r, g, b])
  30. image = np.array(image, dtype=np.uint8).reshape(
  31. (240, 320, 3)).transpose((1, 0, 2))
  32. return imageToQPixmap(Image.fromarray(image))
  33. class SerialThread(QThread):
  34. """ 串口线程 """
  35. loadImageFinished = pyqtSignal(QPixmap)
  36. def __init__(self, parent=None):
  37. super().__init__(parent)
  38. self.serial = Serial(baudrate=1500000)
  39. self.isStopped = False
  40. def run(self):
  41. """ 将串口传输的字节转换为图像 """
  42. data = []
  43. self.serial.port = config.get(config.serialPort)
  44. with self.serial as s:
  45. while not self.isStopped:
  46. if not s.isOpen():
  47. s.open()
  48. # 等待 header
  49. header = s.readline()[:-1]
  50. if header.decode("utf-8", "replace") != "image:":
  51. continue
  52. # 读入像素,丢弃换行符
  53. column_len = 320*2+1
  54. while len(data) < 2*320*240:
  55. image_line = s.read(column_len)
  56. data.extend(image_line[:-1])
  57. self.loadImageFinished.emit(rgb565ToImage(data))
  58. data.clear()
  59. def stop(self):
  60. """ 停止从串口读取图像 """
  61. self.isStopped = True
  62. self.serial.close()
  63. def loadImage(self):
  64. """ 开始从串口读取图像 """
  65. self.isStopped = False
  66. self.start()

软件界面如下图所示,只要点击工具栏最左侧的摄像头按钮就能从串口读取图像。

后记

至此使用 pyqt 读取串口传输图像的方法就介绍完毕了,以上~~

如何使用 pyqt 读取串口传输的图像的更多相关文章

  1. ARM 开发板嵌入式linux系统与主机PC通过串口传输文件

    本文转载自http://useless20.blog.163.com/blog/static/237409982010227127576/ 嵌入式linux系统与主机通过串口传输文件 我想如果要从PC ...

  2. SecureCRT连接开发板 串口传输、tftp传输

    1.串口传输 使用命令:rx  r是service, x是X-model模式 ①.rx 文件名 再按Enter键 ②.将需要传到板子上的文件 拖到SecureCRT里面,选择发送X-model选项 注 ...

  3. C#利用控件mscomm32.ocx读取串口datalogic扫描枪数据

    1).开发环境VS12,语言C# 2).扫描枪品牌:datalogic 4470 3).通讯协议:串口 1.首先,第一步创建一个新工程,windows窗体应用程序,命名为TestScanner,如下: ...

  4. python3 读取串口数据

    python3 读取串口数据 demo import serial import time ser = serial.Serial("COM3",115200,timeout = ...

  5. (5)air202读取串口数据并上传到阿里云显示

    一.首先进行云端设置 根据串口助手显示的信息,以及模块文档说明我们可以知道 其中red和ir是红光LED的原始数据, HR表示心率值, HRvalid是心率是否有效标识, SP02是血氧数值,,SPO ...

  6. C#SerialPort如何读取串口数据并显示在TextBox上

    SerialPort中串口数据的读取与写入有较大的不同.由于串口不知道数据何时到达,因此有两种方法可以实现串口数据的读取.一.线程实时读串口:二.事件触发方式实现. 由于线程实时读串口的效率不是十分高 ...

  7. VB.net 利用SerialPort进行读取串口操作

    Imports SystemImports System.IO.Ports Public Class Form1 Private Sub Form1_Load(ByVal sender As Syst ...

  8. 串口传输文件 lrzsz

    假设有一种开发环境,一块板子,除了串口,没有任何外部出入输出设备,没有sd卡,没有网线,这个时候如果你想跟这块板子传输交互文件,要怎么办? 根据modem所采用的文件传输协议:xmodem,ymode ...

  9. 使用sz/rz基于串口传输文件

    关键词:lrzsz.minicom.ZMODEM.MD5sum等. 在环境受限的嵌入式系统上,往往只有串口可以使用. 此时如果需要传输文件,需要借助rz/sz工具,可以使用的传输协议有ZMODEM.Y ...

随机推荐

  1. JS计算文本字符串字节长度和像素长度的方法

    来源:js获取字符长度并计算px宽度 - [云]风过无痕 - 博客园 (cnblogs.com) <!DOCTYPE html> <html lang="en"& ...

  2. cache2go-源码阅读

    简介 cache2go 是非常简短的 go 开源项目了,很适合作为第一个读源码项目. 如果你有一定的 go 开发经验,读起来会感觉到比较容易. 如果你刚刚接触 go 语音,基础知识还不完全了解,希望阅 ...

  3. 美女 Committer 手把手教你使用海豚调度

    还在为选哪个调度发愁么?还在为查使用手册愁眉不展么?来来来,先瞧一眼海豚调度的 Slogan:调度选的好,下班回家早.调度用的对,半夜安心睡.为充分贯彻这一宗旨,海豚调度一条龙服务来了,特地邀请海豚社 ...

  4. C++11实现的数据库连接池

    它什么是? 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:类似的还有线程池. 为什么要用? 一个数据库连接对象均对应一个物理数据库连接, ...

  5. kafka详解(一)--kafka是什么及怎么用

    kafka是什么 在回答这个问题之前,我们需要先了解另一个东西--event streaming. 什么是event streaming 我觉得,event streaming 是一个动态的概念,它描 ...

  6. JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容

    大家好,又见面啦. 在前一篇文档<JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率>中,我们探讨了如何通过自定义注解的方式扩展swagger的能力让Swag ...

  7. linux中cd后自动 ls的设置

    根据不同的shell设置不太一样.常见的有bash csh两种.可以用echo $SHELL来查询当前是哪一种. bash设置是在用户的home下打开.bashrc在里面加上如下: cd() { bu ...

  8. Linux做bond4

    一.编辑bond网络配置 vim /etc/sysconfig/network-scripts/ifcfg-bond4 DEVICE=bond4 NAME=bond4 TYPE=Bond ONBOOT ...

  9. 【Java面试】面试遇到宽泛的问题,这么回答就稳了,谈谈你对Redis的理解

    "谈谈你对Redis的理解"! 面试的时候遇到这类比较宽泛的问题,是不是很抓狂? 是不是不知道从何开始说起? 没关系,今天我用3分钟教你怎么回答. 大家好,我是Mic,一个工作了1 ...

  10. C#非托管泄漏中HEAP_ENTRY的Size对不上是怎么回事?

    一:背景 1. 讲故事 前段时间有位朋友在分析他的非托管泄漏时,发现NT堆的_HEAP_ENTRY 的 Size 和 !heap 命令中的 Size 对不上,来咨询是怎么回事? 比如下面这段输出: 0 ...