`重现步骤`
<!-- 该问题的重现步骤是什么?请使用1、2、3描述 -->
# ============================================================
# MicroPython 灰度图矩形检测测试代码(使用 cv_lite 扩展模块)
# Grayscale Rectangle Detection Test using cv_lite extension
# ============================================================
import time, os, sys, gc
from machine import Pin
from media.sensor import * # 摄像头接口 / Camera interface
from media.display import * # 显示接口 / Display interface
from media.media import * # 媒体资源管理器 / Media manager
import _thread
import cv_lite # cv_lite扩展模块 / cv_lite extension (C bindings)
import ulab.numpy as np # MicroPython NumPy类库
import math
from machine import UART
from machine import FPIOA
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)
fpioa.set_function(52, FPIOA.GPIO52)
fpioa.set_function(36, FPIOA.GPIO36)
uart = UART(UART.UART2, baudrate=38400, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)
# -------------------------------
# 图像尺寸设置 / Image resolution
# -------------------------------
image_shape = [320, 320] # 高 x 宽 / Height x Width
def calculate_crop(sensor_width, sensor_height, target_width, target_height):
"""
Calculate center crop rectangle from sensor resolution to match target resolution
with preserved aspect ratio.
Returns:
(crop_x, crop_y, crop_width, crop_height)
"""
scale = min(sensor_width // target_width, sensor_height // target_height)
crop_width = int(target_width * scale)
crop_height = int(target_height * scale)
crop_x = (sensor_width - crop_width) // 2
crop_y = (sensor_height - crop_height) // 2
return (crop_x, crop_y, crop_width, crop_height)
# -------------------------------
# 初始化摄像头(灰度图模式) / Initialize camera (grayscale mode)
# -------------------------------
sensor = Sensor(id=2, fps = 90)
sensor.reset()
sensor_width = sensor.width(None)
sensor_height = sensor.height(None)
sensor.set_framesize(width=image_shape[1], height=image_shape[0], crop = calculate_crop(sensor_width,sensor_height,image_shape[1],image_shape[0]))
sensor.set_pixformat(Sensor.GRAYSCALE) # 灰度图格式 / Grayscale format
# -------------------------------
# 初始化显示器(IDE虚拟输出) / Initialize display (IDE virtual output)
# -------------------------------
Display.init(Display.VIRT, width=image_shape[1], height=image_shape[0],
to_ide=True, quality=50)
# -------------------------------
# 初始化媒体系统 / Initialize media system
# -------------------------------
MediaManager.init()
sensor.run()
# -------------------------------
# 可选增益设置(亮度/对比度调节)/ Optional sensor gain setting
# -------------------------------
gain = k_sensor_gain()
gain.gain[0] = 20
sensor.again(gain)
# -------------------------------
# 启动帧率计时 / Start FPS timer
# -------------------------------
clock = time.clock()
# -------------------------------
# 矩形检测可调参数 / Adjustable rectangle detection parameters
# -------------------------------
canny_thresh1 = 50 # Canny 边缘检测低阈值 / Canny low threshold
canny_thresh2 = 150 # Canny 边缘检测高阈值 / Canny high threshold
approx_epsilon = 0.06 # 多边形拟合精度比例(越小拟合越精确)/ Polygon approximation accuracy
area_min_ratio = 0.01 # 最小面积比例(相对于图像总面积)/ Min area ratio
max_angle_cos = 0.3 # 最大角度余弦(越小越接近矩形)/ Max cosine of angle between edges
gaussian_blur_size = 5 # 高斯模糊核尺寸(奇数)/ Gaussian blur kernel size
threshold = [180, 255] # 二值化阈值范围(亮区域)/ Threshold range for binarization
min_area = 1 # 最小目标面积 / Minimum area to keep
kernel_size = 1 # 腐蚀核大小(可用于降噪)/ Erosion kernel size
clock = time.clock() # 帧率计时器
def intersection(p1, p2, p3, p4):
"""
计算线段 (p1-p2) 与 (p3-p4) 的交点
返回 (ix, iy),若平行则返回 None
"""
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
x4, y4 = p4
denom = (x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4)
if abs(denom) < 1e-6: # 平行
return None
t = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4)) / denom
ix = int(x1 + t*(x2 - x1))
iy = int(y1 + t*(y2 - y1))
return (ix, iy)
# 在文件顶部添加数学计算函数
def calculate_ellipse_points(center_x, center_y, radius_cm, rect_corners, num_points=8):
"""
根据矩形透视变形判断是画正圆还是椭圆
center_x, center_y: 圆心坐标
radius_cm: 实际半径(cm)
rect_corners: 矩形的四个角点 [(x1,y1), (x2,y2), (x3,y3), (x4,y4)]
num_points: 点数
"""
# 估算像素到厘米的转换比例(以矩形长边为基准)
d1 = math.sqrt((rect_corners[0][0]-rect_corners[2][0])**2 +
(rect_corners[0][1]-rect_corners[2][1])**2)
d2 = math.sqrt((rect_corners[1][0]-rect_corners[3][0])**2 +
(rect_corners[1][1]-rect_corners[3][1])**2)
diag_avg = (d1 + d2) / 2
# A4纸对角线长度 ≈ 36.4 cm
a4_diag_cm = math.sqrt(17.4**2 + 26**2)
cm_to_pixel = diag_avg / a4_diag_cm
radius_px = radius_cm * cm_to_pixel
# 计算矩形长宽比
width = math.sqrt((rect_corners[0][0]-rect_corners[1][0])**2 +
(rect_corners[0][1]-rect_corners[1][1])**2)
height = math.sqrt((rect_corners[1][0]-rect_corners[2][0])**2 +
(rect_corners[1][1]-rect_corners[2][1])**2)
long_edge = max(width, height)
short_edge = min(width, height)
aspect = long_edge / short_edge
# 如果长宽比接近1(正视),画正圆;否则画椭圆
if aspect < 1.6: # 阈值可调
# 正圆
points = []
for i in range(num_points):
theta = 2 * math.pi * i / num_points
x = int(center_x + radius_px * math.cos(theta))
y = int(center_y + radius_px * math.sin(theta))
points.append((x, y))
else:
# 椭圆
dx = rect_corners[1][0] - rect_corners[0][0]
dy = rect_corners[1][1] - rect_corners[0][1]
angle = math.atan2(dy, dx)
# 用矩形边长估算椭圆长短轴
a = radius_px * (long_edge / short_edge)
b = radius_px * (short_edge / long_edge)
points = []
for i in range(num_points):
theta = 2 * math.pi * i / num_points
x = a * math.cos(theta)
y = b * math.sin(theta)
x_rot = x * math.cos(angle) - y * math.sin(angle)
y_rot = x * math.sin(angle) + y * math.cos(angle)
points.append((int(center_x + x_rot), int(center_y + y_rot)))
#print("aspect ratio:", aspect)
return points
task_flag = 2
receivemess = None
# -------------------------------
# 主循环 / Main loop
# -------------------------------
while True:
clock.tick()
img = sensor.snapshot()
max_area = 0
r = None
blob_max_area = 0
b = None
if task_flag == 0:
while receivemess == None:
img = sensor.snapshot() #####
receivemess = uart.read()
Display.show_image(img)
time.sleep_ms(1)
# uart.write(receivemess)
if receivemess == b'\x31':
task_flag=1
elif receivemess == b'\x32':
task_flag=2
elif receivemess == b'\x33':
task_flag=3
elif receivemess == b'\x34':
task_flag=4
elif receivemess == b'\x35':
task_flag=5
elif receivemess == b'\x36':
task_flag=6
elif receivemess == b'\x37':
task_flag=7
elif receivemess == b'\x38':
task_flag=8
elif receivemess == b'\x39':
task_flag=9
elif receivemess == b'\x40':
task_flag=10
else :
task_flag=0
print(f"{receivemess}")
receivemess = None
elif task_flag == 1:
# 拍摄一帧图像 / Capture a frame
img_np = img.to_numpy_ref()
# 调用底层矩形检测函数
# 返回格式:[[x0, y0, w0, h0, c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4,x, c4.y], [x1, y1, w1, h1,c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4,x, c4.y], ...]
rects = cv_lite.grayscale_find_rectangles_with_corners(
image_shape, img_np,
canny_thresh1, canny_thresh2,
approx_epsilon,
area_min_ratio,
max_angle_cos,
gaussian_blur_size
)
# 遍历检测到的矩形并绘制矩形框和角点
for i in range(len(rects)):
r_1 = rects[i]
#img.draw_rectangle(r[0],r[1], r[2], r[3], color=(255, 255, 255), thickness=2)
#img.draw_cross(r[4],r[5],color=(255,255,255),size=5,thickness=2)
#img.draw_cross(r[6],r[7],color=(255,255,255),size=5,thickness=2)
#img.draw_cross(r[8],r[9],color=(255,255,255),size=5,thickness=2)
#img.draw_cross(r[10],r[11],color=(255,255,255),size=5,thickness=2)
area = r_1[2] * r_1[3]
if area > max_area:
max_area = area
r = r_1
if r != None:
# 提取角点(已按 cv_lite 返回顺序)
pts = [
(r[4], r[5]), # 角点1
(r[6], r[7]), # 角点2
(r[8], r[9]), # 角点3
(r[10], r[11]) # 角点4
]
# 计算几何中心
cx = sum(p[0] for p in pts) / 4.0
cy = sum(p[1] for p in pts) / 4.0
# 极角排序(顺时针)
import math
def angle_key(pt):
return math.atan2(pt[1] - cy, pt[0] - cx)
pts_sorted = sorted(pts, key=angle_key)
# 提取排序后的角点
p0, p1, p2, p3 = pts_sorted
# 计算对角线交点
ix, iy = intersection(p0, p2, p1, p3)
#################圆形#################
# 计算椭圆上的8个点
ellipse_points = calculate_ellipse_points(ix, iy, 6, [p0, p1, p2, p3], num_points=20)
# 绘制椭圆上的点
for i, (px, py) in enumerate(ellipse_points):
img.draw_circle(px, py, 3, color=(255, 0, 0), thickness=2)
# 绘制连接线
next_idx = (i + 1) % len(ellipse_points)
img.draw_line(px, py, ellipse_points[next_idx][0], ellipse_points[next_idx][1],
color=(255, 0, 255), thickness=1)
# 画边
img.draw_line(*p0, *p1, color=(0,255,0), thickness=2)
img.draw_line(*p1, *p2, color=(0,255,0), thickness=2)
img.draw_line(*p2, *p3, color=(0,255,0), thickness=2)
img.draw_line(*p3, *p0, color=(0,255,0), thickness=2)
# 画对角线
img.draw_line(*p0, *p2, color=(0,255,255), thickness=2)
img.draw_line(*p1, *p3, color=(0,255,255), thickness=2)
# 画交点
img.draw_cross(ix, iy, size=10, thickness=2)
center_points = [ix,iy]
value_rect_1 = int((center_points[0]//100)%10 +(center_points[0]//10)%10 +center_points[0]%10 + (center_points[1]//100)%10 + (center_points[1]//10)%10 + center_points[1]%10)
message_x = f'{center_points[0]:03d}'
message_y = f'{center_points[1]:03d}'
message_rect_1 = "{}{}{}\r\n".format(value_rect_1, message_x, message_y)
# 发送椭圆点数据到UART
# 格式:每个点"x,y"用逗号分隔,以"E"结尾
ellipse_data = "E"
for px, py in ellipse_points:
ellipse_data += f"{px:03d},{py:03d},"
ellipse_data = ellipse_data.rstrip(',') + "\r\n"
#uart.write(ellipse_data)
#uart.write(message_rect_1)
#print(ellipse_data)
#print(message_rect_1)
# 画外接矩形(可选)
#img.draw_rectangle(r[0], r[1], r[2], r[3], color=(1, 147, 230), thickness=3)
# 显示图像 / Show image
Display.show_image(img)
elif task_flag == 2:
img_np = img.to_numpy_ref() # 获取图像数据引用 / Get ndarray reference
# 执行灰度图二值连通域检测 / Perform blob detection on thresholded grayscale image
blob = cv_lite.grayscale_find_blobs(
image_shape, img_np,
threshold[0], threshold[1],
min_area, kernel_size
)
if len(blob) == 4: # 只有一个 blob
x, y, w, h = blob
img.draw_rectangle(x, y, w, h, color=(0, 0, 0), thickness=2)
bcx = blob[0] + blob[2]
bcy = blob[1] + blob[3]
print(blob)
center_blob_points = [bcx,bcy]
value_blob_1 = int((center_blob_points[0]//100)%10 +(center_blob_points[0]//10)%10 + center_blob_points[0]%10 + (center_blob_points[1]//100)%10 + (center_blob_points[1]//10)%10 + center_blob_points[1]%10)
message_blob_x = f'{center_blob_points[0]:03d}'
message_blob_y = f'{center_blob_points[1]:03d}'
message_blob_1 = "{}{}{}b\r\n".format(value_blob_1, message_blob_x, message_blob_y)
rects = cv_lite.grayscale_find_rectangles_with_corners(
image_shape, img_np,
canny_thresh1, canny_thresh2,
approx_epsilon,
area_min_ratio,
max_angle_cos,
gaussian_blur_size
)
# 遍历检测到的矩形并绘制矩形框和角点
if rects:
for r_1 in rects:
area = r_1[2] * r_1[3]
if area > max_area:
max_area = area
r = r_1
if r != None:
# 提取角点(已按 cv_lite 返回顺序)
pts = [
(r[4], r[5]), # 角点1
(r[6], r[7]), # 角点2
(r[8], r[9]), # 角点3
(r[10], r[11]) # 角点4
]
# 计算几何中心
cx = sum(p[0] for p in pts) / 4.0
cy = sum(p[1] for p in pts) / 4.0
# 极角排序(顺时针)
import math
def angle_key(pt):
return math.atan2(pt[1] - cy, pt[0] - cx)
pts_sorted = sorted(pts, key=angle_key)
# 提取排序后的角点
p0, p1, p2, p3 = pts_sorted
# 计算对角线交点
ix, iy = intersection(p0, p2, p1, p3)
#################圆形#################
# 计算椭圆上的8个点
ellipse_points = calculate_ellipse_points(ix, iy, 6, [p0, p1, p2, p3], num_points=20)
# 绘制椭圆上的点
for i, (px, py) in enumerate(ellipse_points):
img.draw_circle(px, py, 3, color=(255, 0, 0), thickness=2)
# 绘制连接线
next_idx = (i + 1) % len(ellipse_points)
img.draw_line(px, py, ellipse_points[next_idx][0], ellipse_points[next_idx][1],
color=(255, 0, 255), thickness=1)
# 画边
img.draw_line(*p0, *p1, color=(0,255,0), thickness=2)
img.draw_line(*p1, *p2, color=(0,255,0), thickness=2)
img.draw_line(*p2, *p3, color=(0,255,0), thickness=2)
img.draw_line(*p3, *p0, color=(0,255,0), thickness=2)
# 画对角线
img.draw_line(*p0, *p2, color=(0,255,255), thickness=2)
img.draw_line(*p1, *p3, color=(0,255,255), thickness=2)
# 画交点
img.draw_cross(ix, iy, size=10, thickness=2)
#img.draw_cross(bcx, bcy, size=10, thickness=2)
center_points = [ix,iy]
value_rect_1 = int((center_points[0]//100)%10 +(center_points[0]//10)%10 +center_points[0]%10 + (center_points[1]//100)%10 + (center_points[1]//10)%10 + center_points[1]%10)
message_x = f'{center_points[0]:03d}'
message_y = f'{center_points[1]:03d}'
message_rect_1 = "{}{}{}a\r\n".format(value_rect_1, message_x, message_y)
# uart.write(message_rect_1)
# uart.write(message_blob_1)
print(message_rect_1)
#print(message_blob_1)
Display.show_image(img)
# 垃圾回收 & 输出帧率和检测矩形数量 / Garbage collect and print FPS & rectangles count
gc.collect()
print("fps:", clock.fps())
# -------------------------------
# 程序退出与资源释放 / Cleanup on exit
# -------------------------------
sensor.stop()
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
MediaManager.deinit()
`期待结果和实际结果`
<!-- 你期待的结果是什么?实际看到的结果是什么? -->
能够在一个任务里同时进行
`软硬件版本信息`
<!-- 硬件版本信息?软件版本信息? -->
`错误日志`
<!-- 是否有任何错误信息或日志?请附上相关内容。 -->
find sensor gc2093_csi2, type 24, output 1920x1080@60
vb common pool count 4
sensor(0), mode 0, buffer_num 4, buffer_size 0
到这直接卡死
`尝试解决过程`
<!-- 你为解决这个问题尝试过哪些方法?请提供更多详细信息。 -->
分开运行都没问题
`补充材料`
<!-- 请提供其他相关信息(如代码片段、配置文件、截图等)。 -->