基于MaixPy的摄像头模块---MicroPython图像处理

MaixPy是基于Micropython的K210开发平台,支持机器视觉和麦克风阵列,适用于低成本AIOT应用。K210是一款高性能、低成本的SoC,内置机器视觉和听觉能力。MaixPy简化了K210的编程,提供了丰富的API,如人脸检测、物体识别、视频播放和录制等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 什么是 MaixPy

MaixPy 是将 Micropython 移植到 K210( 一款64位双核带硬件FPU和卷积加速器的 RISC-V CPU, ) 的一个项目, 支持 MCU 常规操作, 更集成了机器视觉和麦克风阵列, 以快速开发具有极低成本和体积实用的 AIOT 领域智能应用。

MicroPython 是基于 Python3 的语法做的一款解析器,包含了 Python3 的大多数基础语法, 主要运行在性能和内存有限的嵌入式芯片上。(注意 Micropython 不包含 Python3 的所有语法)

K210 在硬件上集成了机器视觉和机器听觉能力, 是一款定位 AI 和 AIOT 市场的SoC,同时也是一颗方便的MCU, 性能强劲的同时却有着极低的价格。

Micropython 让我们在 K210 上编程更加简单快捷, 我们也将源代码开源在 github 上。

比如我们需要寻找I2C总线上的设备,只需要使用如下代码即可实现:

from machine import I2C

i2c = I2C(I2C.I2C0, freq=100000, scl=28, sda=29)
devices = i2c.scan()
print(devices)

同样,我们需要实现一个呼吸灯,只需要如下代码:

from machine import Timer,PWM
import time

tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)
ch = PWM(tim, freq=500000, duty=50, pin=board_info.LED_G)
duty=0
dir = True
while True:
    if dir:
        duty += 10
    else:
        duty -= 10
    if duty>100:
        duty = 100
        dir = False
    elif duty<0:
        duty = 0
        dir = True
    time.sleep(0.05)
    ch.duty(duty)

实时拍照

import sensor
import image
import lcd

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
while True:
    img=sensor.snapshot()
    lcd.display(img)

2.  Maix Camera能做哪些事

大多数是在 MaixPy 上已经有 API, 部分可能来自 Maixduino 或者其它开发者~

  • 人脸检测
  • MobileNet 物体识别
  • tiny yolov2 20分类
  • 识别颜色值、找形状、找直线、面部识别等
  • MNIST 手写数字识别
  • Feature map 显示
  • 视频播放和录制
  • NES 游戏模拟器 (FC 红白机)
  • GBA 游戏模拟器
  • 游戏 Quake I
  • 游戏 Doom
  • MMD 3D 实时渲染
  • GUI 界面 littlevGL
  • 云台脸部跟踪
  • 麦克风阵列
  • FFT 频谱图

3.  移植了OpenMV的机器视觉库

支持单目摄像头和双目摄像头,传感器模块,进行摄像头配置及图像抓取等,用于控制开发板摄像头完成摄像任务。

DIY相关的项目制作,比如追踪小球的车,云台,或者解魔方的机器人。

对成本要求很高的嵌入式工业方案,比如流水线物品的分拣。

视频录制、视频播放、音频录制、音频播放、人脸识别、人眼追踪、特征检测等等,大量的图像处理功能

4.最简单的摄像头图像例程

例程说明:实时采集并显示图像在TFT屏上,并获取和显示实时帧数

import sensor
import image
import lcd
import time

clock = time.clock()
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
sensor.skip_frames(30)
while True:
    clock.tick()
    img = sensor.snapshot()
    fps =clock.fps()
    img.draw_string(40,2, ("%2.1ffps" %(fps)), color=(128,0,0), scale=2)
    lcd.display(img)

5.MaixCAM摄像头模块

 

理解下面maixcam代码,修改代码1里的yolo5模型识别逻辑改成代码2的ocr模型识别逻辑。当ocr识别到01、02、03、04、05、06、07时,运行代码1的elif 1 <= obj.class_id <= 18:逻辑。当ocr识别到08,执行if obj.class_id == 19的逻辑。 代码1:import logging from maix import camera, display, image, nn, app, uart, time import requests import json import os import threading from datetime import datetime # 日志配置 logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ logging.FileHandler("/root/operation.log"), logging.StreamHandler() ] ) logger = logging.getLogger("MAIX_PRO") # 状态定义 class SystemState: NORMAL = 0 # 正常检测模式 OBJECT_DETECTED = 1 # 物体检测处理中 SPECIAL_HANDLING = 2# 特殊处理模式(标签19) WAIT_FOR_LABEL1 = 3 # 等待标签1状态 # 模型加载 try: detector = nn.YOLOv5(model="/root/models/mymodels/model_195130.mud", dual_buff=True) logger.info("YOLOv5 model loaded") except Exception as e: logger.critical(f"Model load failed: {str(e)}") exit(1) # 保存目录 # SAVE_DIR = "/root/models/mymodel/" SAVE_DIR = "/boot/Pictures/" os.makedirs(SAVE_DIR, exist_ok=True) # 硬件初始化(调整为硬件支持的分辨率) try: cam = camera.Camera(width=1080, height=640) # 使用标准VGA分辨率 logger.debug(f"Camera resolution: {cam.width()}x{cam.height()}") except RuntimeError as e: logger.critical(f"Camera init failed: {str(e)}") exit(1) disp = display.Display() # UART初始化 device = "/dev/ttyS0" serial0 = uart.UART(device, 38400) logger.info("UART initialized") # 登录获取token login_url = "http://111.230.114.23/api/user/login" headers_login = {'Content-Type': 'application/json'} login_data = {"userAccount": "lanyating", "userPassword": 12345678} json_data = json.dumps(login_data) try: login_response = requests.post(login_url, data=json_data, headers=headers_login) response_json = login_response.json() token = response_json.get('data') if token: logger.info(f"Login successful, token obtained: {token[:10]}...") # 只显示部分token以保护隐私 else: logger.error(f"Login failed: No token returned in response") exit(1) except Exception as e: logger.critical(f"Login failed: {str(e)}") exit(1) def send_uart(data): try: serial0.write_str(data + "\n") logger.info(f"UART sent: {data}") except Exception as e: logger.error(f"UART send failed: {str(e)}") class OperationController: def __init__(self): self.state = SystemState.NORMAL self.current_label = None self.last_detect_time = 0 self.upload_complete = False self.lock = threading.Lock() self.timers = [] # 初始发送forward self.send_uart("forward") # 初始化 photo_url 和 data_url self.photo_url = "http://111.230.114.23/api/file/upload" self.data_url = "http://111.230.114.23/api/data/add" # 确保 token 在整个类中可用 self.token = token def send_uart(self, command): try: serial0.write_str(command + "\n") logger.info(f"UART sent: {command}") except Exception as e: logger.error(f"UART send failed: {str(e)}") def save_and_upload(self, img, label): try: # 生成文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{SAVE_DIR}{label}_{timestamp}.jpg" # 保存图片 if img.save(filename, quality=90): logger.info(f"Image saved: {filename}") # 同步上传 with open(filename, 'rb') as file: files = { 'file': ('image.jpg', file, 'image/jpeg') } params = { 'biz': 'plant_picture', } headers = { "token": self.token } logger.info(f"Uploading {filename} with label {label}, Token: {self.token[:10]}...") response = requests.post( self.photo_url, files=files, headers=headers, params=params ) if response.json().get('code') == 0 : logger.info(f"Upload success: {filename}, Response: {response.text}") return response.json().get('data') else: logger.warning(f"Upload failed: {response.text}") else: logger.error("Image save failed") except Exception as e: logger.error(f"Capture failed: {str(e)}") return None def save_data(self, data): try: params = [{ "deviceName": 1, "plantId": 1, "growthStage": "flowering", "healthStage": "healthy", "height": "5", "crownWidth": "5", "humidity": '', "ph": '', "dan": '', "lin": '', "jia": '', "photoUrl": data, "notes": "" }] headers = { "token": self.token } response = requests.post( self.data_url, headers=headers, json=params ) logger.info(f"Response: {data}") if response.json().get('code') == 0 : logger.info(f"Data save success: {response.text}") else: logger.warning(f"Data save failed: {response.text}") except Exception as e: logger.error(f"Data upload error: {str(e)}") def handle_detection(self, objs, img): with self.lock: current_time = time.time() # 状态机逻辑 if self.state == SystemState.NORMAL: for obj in objs: if obj.class_id == 19: logger.info("Special label 19 detected") self.state = SystemState.SPECIAL_HANDLING self.send_uart("stop") # 1秒后保存并上传 def delayed_save(): print("开始上传") data = self.save_and_upload(img, 19) print("上传成功") if data: self.save_data(data) self.send_uart("back") self.add_timer(1.0, delayed_save) break elif 1 <= obj.class_id <= 18: logger.info(f"Label {obj.class_id} detected") self.state = SystemState.OBJECT_DETECTED self.send_uart("stop") # 1秒后保存并上传 def delayed_save(): print("开始上传") data = self.save_and_upload(img, obj.class_id) print("上传成功") if data: self.save_data(data) self.add_timer(1.0, delayed_save) # 2秒后发送forward def delayed_forward(): self.send_uart("forward") self.state = SystemState.NORMAL self.add_timer(2.0, delayed_forward) break elif self.state == SystemState.SPECIAL_HANDLING: # 等待上传完成 pass # 在同步上传的情况下不需要等待标志位 elif self.state == SystemState.WAIT_FOR_LABEL1: for obj in objs: if obj.class_id == 1: logger.info("Label1 after special handling") self.send_uart("stop") self.add_timer(1.0, lambda: self.send_uart("forward")) self.state = SystemState.NORMAL break def add_timer(self, delay, callback): timer = threading.Timer(delay, callback) timer.start() self.timers.append(timer) def cleanup(self): for timer in self.timers: timer.cancel() logger.info("System cleanup completed") # 主控制实例 controller = OperationController() # 创建 Color 对象 red_color = image.Color(255, 0, 0) # 定义标准红色 # 主循环 try: while not app.need_exit(): try: img = cam.read() except Exception as e: logger.error(f"摄像头读取失败: {str(e)}") continue # 执行检测 try: objs = detector.detect(img, conf_th=0.6) except Exception as e: logger.error(f"目标检测失败: {str(e)}") disp.show(img) continue # 处理结果 if len(objs) > 0: controller.handle_detection(objs, img) # 显示画面 for obj in objs: # 绘制检测框(使用位置参数) img.draw_rect( obj.x, obj.y, obj.w, obj.h, red_color, # 颜色参数位置 2 # 线宽参数位置 ) # 绘制标签文本 img.draw_string( obj.x, obj.y, f"{detector.labels[obj.class_id]} {obj.score:.2f}", scale=0.5, color=red_color ) disp.show(img) except KeyboardInterrupt: logger.info("用户中断") except Exception as e: logger.critical(f"致命错误: {str(e)}") finally: controller.cleanup() logger.info("系统关闭") 代码2:from maix import camera, display, image, nn, app, time, touchscreen def is_in_button(x, y, btn_pos): return x > btn_pos[0] and x < btn_pos[0] + btn_pos[2] and y > btn_pos[1] and y < btn_pos[1] + btn_pos[3] def main(disp): model = "/root/models/pp_ocr.mud" ocr = nn.PP_OCR(model) cam = camera.Camera(ocr.input_width(), ocr.input_height(), ocr.input_format()) ts = touchscreen.TouchScreen() img_back = image.load("/maixapp/share/icon/ret.png") back_rect = [0, 0, 32, 32] back_rect_disp = image.resize_map_pos(cam.width(), cam.height(), disp.width(), disp.height(), image.Fit.FIT_CONTAIN, back_rect[0], back_rect[1], back_rect[2], back_rect[3]) image.load_font("ppocr", "/maixapp/share/font/ppocr_keys_v1.ttf", size = 20) image.set_default_font("ppocr") while not app.need_exit(): img = cam.read() objs = ocr.detect(img) for obj in objs: points = obj.box.to_list() img.draw_keypoints(points, image.COLOR_RED, 4, -1, 1) img.draw_string(obj.box.x4, obj.box.y4, obj.char_str(), image.COLOR_RED) img.draw_image(0, 0, img_back) disp.show(img) x, y, pressed = ts.read() if is_in_button(x, y, back_rect_disp): app.set_exit_flag(True) if __name__ == '__main__': screen = display.Display() try: main(screen) except Exception: import traceback e = traceback.format_exc() print(e) img = image.Image(screen.width(), screen.height()) img.draw_string(2, 2, e, image.COLOR_WHITE, font="hershey_complex_small", scale=0.6) screen.show(img) while not app.need_exit(): time.sleep(0.2)
最新发布
07-12
<think>我们有两个代码片段,代码1是基于YOLOv5的目标检测,代码2是基于PP_OCR的文本识别。 任务要求:修改代码1,将其中使用YOLOv5模型识别物体的逻辑替换为使用OCR模型(如代码2所示)的识别逻辑。 具体规则: 1. 当OCR识别到文本为"01"、"02"、...、"07"时,执行原代码1中`elif 1 <= obj.class_id <= 18:`对应的逻辑(即处理普通标签的逻辑)。 2. 当OCR识别到文本为"08"时,执行原代码1中`if obj.class_id == 19`的逻辑(即处理特殊标签19的逻辑)。 注意:原代码1中,YOLOv5检测返回的是物体检测结果(objs,每个obj有class_id等属性),而OCR检测返回的是文本识别结果(objs,每个obj有char_str()方法返回识别到的字符串)。 因此,我们需要做以下修改: - 将模型从YOLOv5改为PP_OCR,并按照代码2的方式初始化OCR模型。 - 修改主循环中的检测部分,使用OCR模型进行识别。 - 在得到OCR识别结果后,遍历每一个识别到的文本对象,判断其字符串是否为"01"到"08"(注意:OCR识别到的字符串可能是数字字符串,但也要注意可能有误识别,所以需要判断是否在指定集合中)。 - 根据识别到的字符串,触发相应的逻辑(相当于原代码中的class_id判断)。 但是,原代码1中,每个检测到的对象(obj)都有一个class_id,而OCR识别到的每个文本对象有一个字符串。我们需要将字符串映射到原逻辑中的class_id范围: 字符串"01"到"07"对应原逻辑中的class_id范围1到18(注意:原逻辑中1到18是多个标签,但这里我们实际上只需要知道是普通标签即可,因为原逻辑中对于1到18的处理是一样的,都是按照普通标签处理,而具体是哪个数字并不影响处理流程,因为原逻辑中上传时使用的是obj.class_id。但现在我们使用OCR,识别到的字符串需要转换为一个数字,然后模拟成class_id吗?) 然而,原代码1中,上传图片时保存的文件名和上传数据时使用的标签是class_id(即数字)。现在,我们使用OCR识别到的字符串(如"01")实际上对应一个数字标签(比如1),但注意原代码1中标签1到18的处理方式相同,所以我们可以这样处理: 将OCR识别到的字符串(去掉前导零)转换为整数,然后这个整数就作为模拟的class_id。但是注意,原代码中标签19对应特殊处理,而我们现在要求当识别到"08"时执行原class_id==19的逻辑。 因此,我们可以设计: - 如果识别到的字符串是"01"到"07",则将其转换为整数(如int("01")=1),然后这个整数在1到7之间,符合原代码中1<=class_id<=18的条件,所以会触发普通标签的处理。 - 如果识别到的字符串是"08",我们将其视为特殊标签,相当于原class_id=19。 但是,原代码1中,普通标签的范围是1到18,而我们现在只有1到7(对应字符串01-07)和08(对应特殊标签19)。所以,我们可以在handle_detection函数中,将OCR识别到的字符串映射为一个模拟的class_id: 如果是"01"到"07",则class_id = int(text) # 得到1到7 如果是"08",则class_id = 19 这样,我们就可以复用原handle_detection函数中的状态机逻辑,因为该函数是根据class_id的值进行判断的。 步骤: 1. 替换模型加载部分:将YOLOv5模型加载改为OCR模型加载。 2. 修改主循环中的检测部分:使用OCR模型进行检测,然后对检测到的每个文本对象进行映射,生成一个类似原YOLOv5检测结果的对象(我们用一个简单的类来模拟,只需要有class_id属性即可,同时为了绘制,我们还需要保留文本的位置信息)。 3. 修改绘制部分:原代码绘制的是矩形框和类别标签,现在OCR返回的是关键点(四个点)和文本内容,我们可以用绘制关键点的方式(或者绘制多边形)来绘制文本框,并绘制识别到的字符串。 但是,原代码1中,检测结果对象(obj)具有x, y, w, h属性,而OCR返回的是四个点的坐标(obj.box,包含四个点)。为了兼容原绘制代码(draw_rect需要矩形框),我们可以计算四个点的最小外接矩形(x, y, w, h)来模拟。 然而,原handle_detection函数中,除了使用class_id,并没有使用其他属性(如位置信息)。所以我们可以只关注class_id,但绘制时需要使用位置信息。 因此,我们需要创建一个新的对象类型来模拟原YOLOv5的检测结果,这个对象需要有以下属性: class_id: 映射后的整数(1-7或19) x, y, w, h: 由OCR返回的四个点计算出的最小外接矩形 score: 置信度(OCR返回的obj.score) 另外,原代码1中有一个labels属性(detector.labels)用于获取类别名称,但现在我们不需要了,因为绘制时我们直接绘制OCR识别到的字符串(obj.char_str())即可。但为了不报错,我们可以给detector对象添加一个labels属性,或者修改绘制部分。 考虑到时间,我们修改绘制部分:不再使用detector.labels,而是直接使用OCR识别到的字符串(即原obj.char_str())。 具体修改计划: 1. 模型加载部分: 将原来的detector改为加载OCR模型,并调整相机分辨率(使用OCR模型要求的输入分辨率)。 2. 修改OperationController类中的handle_detection函数:它现在接收的是OCR识别结果(经过映射后的对象列表)和图像。 3. 主循环中: - 读取图像 - 使用OCR模型检测 - 将每个OCR结果映射为模拟的检测对象(具有class_id, x, y, w, h, score, 以及text属性用于绘制) - 调用controller.handle_detection(模拟的对象列表, img) - 绘制:遍历模拟的对象列表,绘制矩形框(使用计算出的矩形)和文本(使用OCR识别到的原始字符串,而不是映射后的class_id) 4. 注意:原代码1中,当没有检测到物体时,不进行处理。现在,当没有OCR结果时,同样不处理。 5. 由于OCR模型可能识别到非数字的文本,我们需要过滤掉非目标文本(即不是01-08的文本)。 6. 模拟检测对象类定义(简单起见,使用namedtuple或自定义类)。 考虑到代码复杂度,我们创建一个简单的类来模拟: class SimulatedObj: def __init__(self, class_id, x, y, w, h, score, text): self.class_id = class_id self.x = x self.y = y self.w = w self.h = h self.score = score self.text = text # 原始字符串,用于绘制 如何从OCR的一个结果obj(来自ocr.detect)计算矩形框? OCR返回的obj有一个box属性,它是一个Box对象(根据代码2,它有to_list方法,返回四个点的列表,每个点是一个(x,y))。我们可以通过四个点计算最小外接矩形(即能包围这四个点的最小矩形)。 计算最小外接矩形的方法: x_min = min(x1, x2, x3, x4) y_min = min(y1, y2, y3, y4) x_max = max(x1, x2, x3, x4) y_max = max(y1, y2, y3, y4) 则矩形框的左上角坐标为(x_min, y_min),宽度为x_max-x_min,高度为y_max-y_min。 7. 映射规则: 对于OCR识别到的每个文本obj,先获取其字符串text = obj.char_str()。 如果text在["01","02","03","04","05","06","07"]中,则class_id = int(text) 如果text == "08",则class_id = 19 其他文本则忽略(不加入模拟对象列表)。 8. 注意:原代码1中,YOLOv5的检测结果有多个对象时,会遍历处理,但状态机可能只处理第一个符合条件的对象。现在,我们同样只处理第一个符合条件的OCR结果(因为状态机设计如此,避免多次触发)。但是,我们仍然需要绘制所有识别到的目标。 因此,在构建模拟对象列表时,我们不过滤,只是将非目标文本忽略(不加入列表)。然后,在handle_detection中,我们遍历模拟对象列表,只要遇到一个符合条件的就触发状态转换(并break)。 9. 相机分辨率调整:在代码2中,创建相机时使用了ocr.input_width()和ocr.input_height(),我们也需要这样设置。 10. 修改日志和错误处理。 由于代码较长,我们只修改关键部分,并尽量保留原结构。 开始修改代码1:</think>我将修改代码1,将YOLOv5模型识别逻辑替换为OCR模型识别逻辑,并按照要求处理识别结果。以下是修改后的代码: ```python import logging from maix import camera, display, image, nn, app, uart, time import requests import json import os import threading from datetime import datetime # 日志配置 logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ logging.FileHandler("/root/operation.log"), logging.StreamHandler() ] ) logger = logging.getLogger("MAIX_PRO") # 状态定义 class SystemState: NORMAL = 0 # 正常检测模式 OBJECT_DETECTED = 1 # 物体检测处理中 SPECIAL_HANDLING = 2# 特殊处理模式(标签08) WAIT_FOR_LABEL1 = 3 # 等待标签1状态 # OCR模型加载 try: ocr = nn.PP_OCR(model="/root/models/pp_ocr.mud") logger.info("PP_OCR model loaded") except Exception as e: logger.critical(f"OCR model load failed: {str(e)}") exit(1) # 保存目录 SAVE_DIR = "/boot/Pictures/" os.makedirs(SAVE_DIR, exist_ok=True) # 硬件初始化(使用OCR模型要求的分辨率) try: cam = camera.Camera(ocr.input_width(), ocr.input_height(), ocr.input_format()) logger.debug(f"Camera resolution: {cam.width()}x{cam.height()}") except RuntimeError as e: logger.critical(f"Camera init failed: {str(e)}") exit(1) disp = display.Display() # UART初始化 device = "/dev/ttyS0" serial0 = uart.UART(device, 38400) logger.info("UART initialized") # 登录获取token login_url = "http://111.230.114.23/api/user/login" headers_login = {'Content-Type': 'application/json'} login_data = {"userAccount": "lanyating", "userPassword": 12345678} json_data = json.dumps(login_data) try: login_response = requests.post(login_url, data=json_data, headers=headers_login) response_json = login_response.json() token = response_json.get('data') if token: logger.info(f"Login successful, token obtained: {token[:10]}...") # 只显示部分token以保护隐私 else: logger.error(f"Login failed: No token returned in response") exit(1) except Exception as e: logger.critical(f"Login failed: {str(e)}") exit(1) def send_uart(data): try: serial0.write_str(data + "\n") logger.info(f"UART sent: {data}") except Exception as e: logger.error(f"UART send failed: {str(e)}") class OperationController: def __init__(self): self.state = SystemState.NORMAL self.current_label = None self.last_detect_time = 0 self.upload_complete = False self.lock = threading.Lock() self.timers = [] # 初始发送forward self.send_uart("forward") # 初始化 photo_url 和 data_url self.photo_url = "http://111.230.114.23/api/file/upload" self.data_url = "http://111.230.114.23/api/data/add" # 确保 token 在整个类中可用 self.token = token def send_uart(self, command): try: serial0.write_str(command + "\n") logger.info(f"UART sent: {command}") except Exception as e: logger.error(f"UART send failed: {str(e)}") def save_and_upload(self, img, label): try: # 生成文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{SAVE_DIR}{label}_{timestamp}.jpg" # 保存图片 if img.save(filename, quality=90): logger.info(f"Image saved: {filename}") # 同步上传 with open(filename, 'rb') as file: files = { 'file': ('image.jpg', file, 'image/jpeg') } params = { 'biz': 'plant_picture', } headers = { "token": self.token } logger.info(f"Uploading {filename} with label {label}, Token: {self.token[:10]}...") response = requests.post( self.photo_url, files=files, headers=headers, params=params ) if response.json().get('code') == 0 : logger.info(f"Upload success: {filename}, Response: {response.text}") return response.json().get('data') else: logger.warning(f"Upload failed: {response.text}") else: logger.error("Image save failed") except Exception as e: logger.error(f"Capture failed: {str(e)}") return None def save_data(self, data): try: params = [{ "deviceName": 1, "plantId": 1, "growthStage": "flowering", "healthStage": "healthy", "height": "5", "crownWidth": "5", "humidity": '', "ph": '', "dan": '', "lin": '', "jia": '', "photoUrl": data, "notes": "" }] headers = { "token": self.token } response = requests.post( self.data_url, headers=headers, json=params ) logger.info(f"Response: {data}") if response.json().get('code') == 0 : logger.info(f"Data save success: {response.text}") else: logger.warning(f"Data save failed: {response.text}") except Exception as e: logger.error(f"Data upload error: {str(e)}") def handle_detection(self, objs, img): with self.lock: current_time = time.time() # 状态机逻辑 if self.state == SystemState.NORMAL: for obj in objs: text = obj.char_str().strip() # 处理特殊标签08 if text == "08": logger.info("Special label 08 detected") self.state = SystemState.SPECIAL_HANDLING self.send_uart("stop") # 1秒后保存并上传 def delayed_save(): print("开始上传") data = self.save_and_upload(img, 8) # 使用8代表08 print("上传成功") if data: self.save_data(data) self.send_uart("back") self.add_timer(1.0, delayed_save) break # 处理普通标签01-07 elif text in ["01", "02", "03", "04", "05", "06", "07"]: label_id = int(text) # 转换为数字 logger.info(f"Label {text} detected") self.state = SystemState.OBJECT_DETECTED self.send_uart("stop") # 1秒后保存并上传 def delayed_save(): print(f"开始上传标签{label_id}") data = self.save_and_upload(img, label_id) print("上传成功") if data: self.save_data(data) self.add_timer(1.0, delayed_save) # 2秒后发送forward def delayed_forward(): self.send_uart("forward") self.state = SystemState.NORMAL self.add_timer(2.0, delayed_forward) break elif self.state == SystemState.SPECIAL_HANDLING: # 等待上传完成 pass # 在同步上传的情况下不需要等待标志位 elif self.state == SystemState.WAIT_FOR_LABEL1: for obj in objs: if obj.char_str().strip() == "01": logger.info("Label01 after special handling") self.send_uart("stop") self.add_timer(1.0, lambda: self.send_uart("forward")) self.state = SystemState.NORMAL break def add_timer(self, delay, callback): timer = threading.Timer(delay, callback) timer.start() self.timers.append(timer) def cleanup(self): for timer in self.timers: timer.cancel() logger.info("System cleanup completed") # 主控制实例 controller = OperationController() # 创建 Color 对象 red_color = image.Color(255, 0, 0) # 定义标准红色 # 主循环 try: while not app.need_exit(): try: img = cam.read() except Exception as e: logger.error(f"摄像头读取失败: {str(e)}") continue # 执行OCR检测 try: ocr_objs = ocr.detect(img) except Exception as e: logger.error(f"OCR识别失败: {str(e)}") disp.show(img) continue # 处理结果 if len(ocr_objs) > 0: controller.handle_detection(ocr_objs, img) # 显示画面 - 绘制OCR结果 for obj in ocr_objs: # 绘制文本框(四个关键点) points = obj.box.to_list() img.draw_keypoints(points, image.COLOR_RED, 4, -1, 1) # 绘制识别文本 text = obj.char_str() img.draw_string( obj.box.x4, # 使用右下角坐标 obj.box.y4, text, scale=1.0, color=red_color ) disp.show(img) except KeyboardInterrupt: logger.info("用户中断") except Exception as e: logger.critical(f"致命错误: {str(e)}") finally: controller.cleanup() logger.info("系统关闭") ``` ### 修改说明: 1. **模型替换**: - 将YOLOv5模型替换为PP_OCR模型 - 调整相机分辨率以匹配OCR模型输入要求 2. **识别逻辑修改**: - 当OCR识别到"01"-"07"时,执行原普通标签逻辑(`1 <= obj.class_id <= 18`) - 当OCR识别到"08"时,执行原特殊标签逻辑(`obj.class_id == 19`) - 添加文本清理(`strip()`)处理可能的空格问题 3. **结果处理**: - 将文本标签转换为数字ID(01→1,08→8) - 保留OCR原始文本用于显示 - 修改文件名生成使用数字ID而非文本 4. **绘制逻辑**: - 使用`draw_keypoints`绘制OCR文本框(四个点) - 在文本框右下角显示识别文本 - 移除原YOLOv5的矩形框绘制 5. **状态机调整**: - 修改状态描述(SPECIAL_HANDLING对应08) - 更新日志信息反映OCR识别结果 6. **错误处理**: - 更新模型加载错误信息 - 修改OCR识别失败的错误日志 ### 关键修改点: 1. OCR结果处理: ```python for obj in objs: text = obj.char_str().strip() if text == "08": # 特殊处理 # 执行特殊标签逻辑 elif text in ["01", "02", "03", "04", "05", "06", "07"]: # 普通处理 label_id = int(text) # 转换为数字ID # 执行普通标签逻辑 ``` 2. 绘制OCR结果: ```python # 绘制文本框 points = obj.box.to_list() img.draw_keypoints(points, image.COLOR_RED, 4, -1, 1) # 绘制识别文本 img.draw_string(obj.box.x4, obj.box.y4, text, scale=1.0, color=red_color) ``` 3. 文件名生成: ```python # 使用数字ID而非文本标签 filename = f"{SAVE_DIR}{label}_{timestamp}.jpg" ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.Lanson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值