import time, os, sys, math
from media.sensor import *
from media.display import *
from media.media import *
# 配置参数
picture_width = 400
picture_height = 240
sensor_id = 2
# 显示模式选择:可以是 "VIRT"、"LCD" 或 "HDMI"
DISPLAY_MODE = "LCD"
# 根据模式设置显示宽高
if DISPLAY_MODE == "VIRT":
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 480
elif DISPLAY_MODE == "HDMI":
DISPLAY_WIDTH = 1920
DISPLAY_HEIGHT = 1080
else:
raise ValueError("未知的 DISPLAY_MODE,请选择 'VIRT', 'LCD' 或 'HDMI'")
# 矩形检测参数
MIN_RECT_AREA = 5000 # 最小矩形面积阈值 (像素)
MAX_RECT_AREA = 80000 # 最大矩形面积阈值
ASPECT_RATIO_RANGE = 0.6 # 宽高比误差范围
MORPHOLOGY_KERNEL = 1 # 形态学操作核大小
BINARY_THRESHOLD = 127 # 二值化固定阈值
# 颜色定义
SCREEN_CENTER_COLOR = (0, 0, 255) # 画面中心点颜色(蓝色)
RECT_CENTER_COLOR = (255, 0, 0) # 矩形中心点颜色(红色)
CONNECT_LINE_COLOR = (128, 0, 128) # 连线颜色(紫色)
CROSS_COLOR = (255, 255, 0) # 十字线颜色(黄色)
def calculate_angle(p1, p2, p3):
"""计算三点形成的角度"""
dx1 = p1[0] - p2[0]
dy1 = p1[1] - p2[1]
dx2 = p3[0] - p2[0]
dy2 = p3[1] - p2[1]
dot_product = dx1 * dx2 + dy1 * dy2
cross_product = dx1 * dy2 - dy1 * dx2
angle = math.atan2(cross_product, dot_product) * 180 / math.pi
return abs(angle)
def preprocess_image(img):
"""图像预处理:灰度化、高斯模糊、固定阈值二值化、形态学操作"""
# 转换为灰度图
gray = img.to_grayscale(copy=True)
# 高斯模糊降噪
gray.gaussian(1)
# 使用固定阈值二值化
gray.binary([(BINARY_THRESHOLD, 255)], invert=False)
# 形态学操作:先腐蚀后膨胀,消除小噪点并连接边缘
gray.erode(MORPHOLOGY_KERNEL)
gray.dilate(MORPHOLOGY_KERNEL)
return gray
def detect_rectangles(img, min_area=MIN_RECT_AREA):
"""检测图像中的矩形"""
# 预处理图像
processed_img = preprocess_image(img)
# 查找矩形
rects = processed_img.find_rects(threshold=min_area)
return rects, processed_img
def evaluate_rectangles(rects, img_width, img_height):
"""评估并筛选最佳矩形"""
best_rect = None
max_score = 0
for rect in rects:
# 获取矩形参数
x, y, w, h = rect.rect()
area = w * h
corners = rect.corners()
# 过滤面积不符合要求的矩形
if not (MIN_RECT_AREA < area < MAX_RECT_AREA):
continue
# 计算矩形中心位置得分 (越靠近中心越好)
center_x = x + w/2
center_y = y + h/2
dist_x = abs(center_x - img_width/2) / (img_width/2)
dist_y = abs(center_y - img_height/2) / (img_height/2)
center_score = 1.0 - (dist_x + dist_y)/2
# 计算宽高比得分 (越接近1:1越好)
aspect_ratio = w / h if h != 0 else float('inf')
aspect_score = max(0, 1.0 - abs(aspect_ratio - 1.0) * ASPECT_RATIO_RANGE)
# 计算角度得分 (四个角越接近90度越好)
angle_score = 1.0
for i in range(4):
p1 = corners[i]
p2 = corners[(i+1)%4]
p3 = corners[(i+2)%4]
angle = calculate_angle(p1, p2, p3)
angle_deviation = min(abs(angle - 90), abs(angle - 270))
angle_score *= max(0, 1.0 - angle_deviation / 45.0) # 角度偏离90度越大,得分越低
# 总面积得分
area_score = min(area / MAX_RECT_AREA, 1.0)
# 综合得分 (调整权重)
score = (center_score * 0.4 + # 中心位置权重
aspect_score * 0.3 + # 宽高比权重
area_score * 0.2 + # 面积权重
angle_score * 0.1) # 角度权重
# 更新最佳矩形
if score > max_score:
max_score = score
best_rect = rect
return best_rect, max_score
def draw_rectangle_info(img, rect, score):
"""在图像上绘制矩形信息、画面中心点及连线"""
if img is None:
return None
# 计算画面中心点
screen_center_x = int(picture_width / 2)
screen_center_y = int(picture_height / 2)
# 绘制画面中心点 (蓝色)
img.draw_circle(screen_center_x, screen_center_y, 8, color=SCREEN_CENTER_COLOR, thickness=-1)
cross_size = 10
img.draw_line(screen_center_x - cross_size, screen_center_y,
screen_center_x + cross_size, screen_center_y,
color=CROSS_COLOR, thickness=2)
img.draw_line(screen_center_x, screen_center_y - cross_size,
screen_center_x, screen_center_y + cross_size,
color=CROSS_COLOR, thickness=2)
if not rect:
try:
# 显示无矩形提示
img.draw_string_advanced(5, 5, "未检测到矩形",
color=color_to_rgb565((255, 0, 0)),
scale=1.5, mono_space=False, transparent=False)
except:
img.draw_string(5, 5, "未检测到矩形", color=(255, 0, 0), scale=1.5)
return img
corners = rect.corners()
x, y, w, h = rect.rect()
# 计算矩形中心点坐标
rect_center_x = int((corners[0][0] + corners[2][0]) / 2)
rect_center_y = int((corners[0][1] + corners[2][1]) / 2)
# 计算中心点偏移
offset_x = rect_center_x - screen_center_x
offset_y = rect_center_y - screen_center_y
# 绘制画面中心到矩形中心的连线 (紫色)
img.draw_line(screen_center_x, screen_center_y,
rect_center_x, rect_center_y,
color=CONNECT_LINE_COLOR, thickness=2)
# 绘制矩形框 (绿色)
for i in range(4):
x1, y1 = corners[i]
x2, y2 = corners[(i+1)%4]
img.draw_line(x1, y1, x2, y2, color=(0, 255, 0), thickness=5)
# 绘制矩形中心点 (红色)
img.draw_circle(rect_center_x, rect_center_y, 10, color=RECT_CENTER_COLOR, thickness=-1)
img.draw_line(rect_center_x - cross_size, rect_center_y,
rect_center_x + cross_size, rect_center_y,
color=CROSS_COLOR, thickness=3)
img.draw_line(rect_center_x, rect_center_y - cross_size,
rect_center_x, rect_center_y + cross_size,
color=CROSS_COLOR, thickness=3)
# 显示矩形信息
try:
img.draw_string_advanced(5, 5, f"目标矩形 | 得分: {score:.2f}",
color=color_to_rgb565((0, 255, 0)),
scale=1.5, mono_space=False, transparent=False)
img.draw_string_advanced(rect_center_x + 10, rect_center_y - 20,
f"中心: ({rect_center_x},{rect_center_y})",
color=color_to_rgb565((255, 255, 0)),
scale=1.2, mono_space=False, transparent=False)
img.draw_string_advanced(rect_center_x + 10, rect_center_y,
f"偏移: ({offset_x},{offset_y})",
color=color_to_rgb565((255, 255, 0)),
scale=1.2, mono_space=False, transparent=False)
img.draw_string_advanced(5, 30,
f"画面中心: ({screen_center_x},{screen_center_y})",
color=color_to_rgb565(SCREEN_CENTER_COLOR),
scale=1.2, mono_space=False, transparent=False)
except:
img.draw_string(5, 5, f"目标矩形 | 得分: {score:.2f}", color=(0, 255, 0), scale=1.5)
img.draw_string(rect_center_x + 10, rect_center_y - 20,
f"中心: ({rect_center_x},{rect_center_y})",
color=(255, 255, 0), scale=1.2)
img.draw_string(rect_center_x + 10, rect_center_y,
f"偏移: ({offset_x},{offset_y})",
color=(255, 255, 0), scale=1.2)
img.draw_string(5, 30,
f"画面中心: ({screen_center_x},{screen_center_y})",
color=SCREEN_CENTER_COLOR, scale=1.2)
return img
def color_to_rgb565(color):
"""将RGB三元组转换为RGB565格式"""
r, g, b = color
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
def main():
"""主程序:初始化系统并运行矩形检测循环"""
global sensor
try:
# 初始化摄像头
sensor = Sensor(id=sensor_id)
sensor.reset()
sensor.set_framesize(width=picture_width, height=picture_height, chn=CAM_CHN_ID_0)
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
# 初始化显示器
if DISPLAY_MODE == "VIRT":
Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
elif DISPLAY_MODE == "LCD":
Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
elif DISPLAY_MODE == "HDMI":
Display.init(Display.LT9611, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
# 初始化媒体管理器
MediaManager.init()
sensor.run()
print("矩形检测系统已启动,按 Ctrl+C 退出")
# 主循环
while True:
os.exitpoint()
start_time = time.ticks_ms()
# 捕获图像
img = sensor.snapshot(chn=CAM_CHN_ID_0)
# 检测矩形
rects, processed_img = detect_rectangles(img)
# 评估矩形
best_rect, score = evaluate_rectangles(rects, picture_width, picture_height)
# 在原图上绘制矩形信息、画面中心点及连线
result_img = draw_rectangle_info(img, best_rect, score)
# 计算处理时间
process_time = time.ticks_ms() - start_time
# 显示处理时间
if result_img:
try:
result_img.draw_string_advanced(5, DISPLAY_HEIGHT - 25,
f"处理时间: {process_time}ms",
color=color_to_rgb565((255, 255, 255)),
scale=1.2, mono_space=False, transparent=False)
except:
result_img.draw_string(5, DISPLAY_HEIGHT - 25,
f"处理时间: {process_time}ms",
color=(255, 255, 255), scale=1.2)
# 显示图像
Display.show_image(result_img,
x=int((DISPLAY_WIDTH - picture_width) / 2),
y=int((DISPLAY_HEIGHT - picture_height) / 2))
# 打印检测结果
if best_rect:
x, y, w, h = best_rect.rect()
print(f"检测到矩形 | 中心: ({int(x+w/2)},{int(y+h/2)}) | 宽高: {w}x{h} | 得分: {score:.2f} | 偏移: ({int(x+w/2)-int(picture_width/2)},{int(y+h/2)-int(picture_height/2)})")
else:
print("未检测到符合条件的矩形")
except KeyboardInterrupt:
print("用户停止程序")
except BaseException as e:
print(f"异常: {e}")
finally:
# 清理资源
if isinstance(sensor, Sensor):
sensor.stop()
Display.deinit()
os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
time.sleep_ms(100)
MediaManager.deinit()
if __name__ == "__main__":
main()
这是k230识别矩形的代码,根据这个代码,在其中加上k230和32串口通信的·代码,并写出整体完整代码,要能够脱机运行