【u201】矩形覆盖

Time Limit: 1 second
Memory Limit: 128 MB

【问题描述】

有N个矩形,矩形的底边边长为1,且均在X轴上,高度给出,第i个矩形的高为h[i],例如h = [3, 2, 4, 2]的图形如下:
 你可以容易地发现,只需要3个矩形就能覆盖这个图形。 你的任务就是,输出最少需要几个矩形能覆盖这个图形。 


【输入格式】

第一行一个整数N。接下来1行包含N个正整数,为h[i]。

【输出格式】

输出一个整数表示最少需要几个矩形能覆盖这个图形。

【数据规模】

对于所有数据,N<=100000,h[i] <= 100。 对于部分数据,N<=10; 对于部分数据,N<=100; 对于部分数据,N<=1000; 对于部分数据,h[i] <= 10;

Sample Input1

10
2 3 2 4 2 1 3 4 3 2
【题解】

像是题目给的那张图


会发现前3列。3 2 4无论如何都没有办法用少于3个矩形覆盖。

而第4列2确可以省掉一个。

因为我们可以横着用一条长的把第2列和第3列都覆盖到

假想一下旁边还有一个高度为2的。也同样可以覆盖掉。

但如果是这样却不能省


虽然1和4高度都为2.

但是你中间有比这两个2低的1.

这下你可没办法弄一个长长的矩形同时覆盖它们俩了。

事实上如果后面出现2或者3都没有可能节省了,因为1比2和3都矮,都和这种情况相似(即虽然前面有一样高度的但是没办法省一个矩形)。

由此。我们可以用栈来解决这个问题。

如果栈为空就把当前这个高度入栈。

否则。如果当前元素比栈顶元素高。则入栈。

否则一直退栈。直到满足当前元素比栈顶元素高或恰好等于栈顶元素。

如果恰好等于则不要入栈(表示省了一个矩形)。

最后统计入过栈的元素的个数。就是答案了。

【代码】

#include <cstdio>

int n,stack[100009] = {0},top = 0,num = 0;

void input_data()
{
	scanf("%d",&n);
	for (int i = 1;i <= n;i++)
		{
			int x;
			scanf("%d",&x);
			while (stack[top] > x) //如果比栈顶元素矮则一直退栈 
				top--;
			if (stack[top]!=x) //如果和栈顶元素不一样(就是大于了) 
				{
					stack[++top] = x; //入栈 
					num++; //答案递增。 
				}
		}
}

void output_ans()
{
	printf("%d",num);	//最后输出入过栈的元素个数。 
}

int main()
{
	input_data();
	output_ans();
	return 0;	
}


转载于:https://www.cnblogs.com/AWCXV/p/7632334.html

import time, os, math from media.sensor import * from media.display import * from media.media import * from machine import FPIOA, PWM, Pin from machine import UART from machine import TOUCH import gc # 图像参数 IMAGE_WIDTH = 352 IMAGE_HEIGHT = 240 SENSOR_ID = 2 sensor = None THRESHOLD = (69, 252) # 初始灰度阈值 # 显示模式设置 DISPLAY_MODE = "LCD" # 可选 "VIRT", "LCD", "HDMI" # 根据显示模式设置分辨率 if DISPLAY_MODE == "VIRT": DISPLAY_WIDTH, DISPLAY_HEIGHT = 1920, 1080 elif DISPLAY_MODE == "LCD": DISPLAY_WIDTH, DISPLAY_HEIGHT = 800, 480 elif DISPLAY_MODE == "HDMI": DISPLAY_WIDTH, DISPLAY_HEIGHT = 1920, 1080 else: raise ValueError("无效的显示模式") # 角点检测阈值 CORNERS_THRESHOLD = 5000 # 透视变换目标尺寸 PERSPECTIVE_SIZE = 200 # 变换后的矩形大小 # 矩形过滤参数 MIN_AREA_RATIO = 0.01 # 最小面积占图像比例 MAX_AREA_RATIO = 0.8 # 最大面积占图像比例 ASPECT_RATIO_MIN = 0.5 # 最小宽比 ASPECT_RATIO_MAX = 2.0 # 最大宽比 COS_THRESHOLD = 0.3 # 角度余弦阈值(绝对值) # 初始化UART2 fpioa = FPIOA() fpioa.set_function(11, FPIOA.UART2_TXD) # UART2发送 fpioa.set_function(12, FPIOA.UART2_RXD) # UART2接收 uart2 = UART(UART.UART2, 115200, 8, 0, 1, timeout=1000) # 用于坐标通信 # 初始化按键 fpioa.set_function(53, FPIOA.GPIO53) button = Pin(53, Pin.IN, Pin.PULL_DOWN) # 使用下拉电阻 debounce_delay = 20 # 毫秒 last_press_time = 0 # 上次按键按下的时间,单位为毫秒 button_last_state = 0 # 上次按键状态 timeTouch = 0 tp = TOUCH(0) # 按钮文本 textL = ["back", "GL-", "GH-", "set"] textR = ["Gray", "GL+", "GH+",] isLabFlag = False # 默认使用灰度模式 isInvertFlag = False # 是否反转 # 虚拟按钮范围 buttonsL = [ (0, 5, 100, 100), # 按钮"back" (0, 65, 100, 100), # 按钮"GL-" (0, 125, 100, 100), # 按钮"GH-" (0, 200, 100, 100) # 按钮"set" ] buttonsR = [ (250, 5, 100, 100), # 按钮"Gray" (250, 65, 100, 100), # 按钮"GL+" (250, 125, 100, 100), # 按钮"GH+" ] buttonsS = [ (300, 5, 100, 50) # 步进按钮 ] # 灰度范围初始化 GL, GH = 0, 255 # 灰度范围0~255 step = 1 # 脱机阈值步进 Gray_thresholds = [(int(GL), int(GH))] # 灰度阈值列表 # 状态标志 flag = False # 页面标志,True为阈值设置 scan_mode = False # 扫描模式标志 last_scan_send_time = 0 # 上次发送扫描信号的时间 # 脱机调整阈值界面设计 def changeScreen(img): img_1 = img.copy() screen = img.copy() # 绘制白色背景、阈值画面范围及原始画面 screen.draw_rectangle(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, (255, 255, 255), fill=True) screen.draw_rectangle(200, 170, 400, 240, (0, 0, 0), 2) # 显示原始图像(全屏) screen.draw_image(img_1, 0, 0, x_size=DISPLAY_WIDTH, y_size=DISPLAY_HEIGHT) # 获取画面 # 灰度模式处理 GrayImg = img_1.to_grayscale().binary(Gray_thresholds) if isInvertFlag: GrayImg = GrayImg.invert() # 显示灰度阈值图像(全屏) screen.draw_image(GrayImg, 0, 0, x_size=DISPLAY_WIDTH, y_size=DISPLAY_HEIGHT) screen.draw_string_advanced(300, 420, 30, f"Gray: [{GL} {GH}]", color=(0, 0, 0)) # 绘制虚拟按钮,编写文本 for i in range(8): screen.draw_rectangle(0, 5+i*60, 100, 50, (200, 201, 202), thickness=1, fill=True) screen.draw_string_advanced(30, 20+i*60, 20, textL[i], (0, 0, 0)) screen.draw_rectangle(700, 5+i*60, 100, 50, (200, 201, 202), thickness=1, fill=True) screen.draw_string_advanced(730, 20+i*60, 20, textR[i], (0, 0, 0)) screen.draw_rectangle(390, 5, 100, 50, (200, 201, 202), thickness=1, fill=True) screen.draw_string_advanced(400, 20, 20, f"step: {step}", (0, 0, 0)) # 显示处理后的图像 Display.show_image(screen, 0, 0) # 触摸屏按钮事件动作 def buttonAction(direction, index): global GL, GH, flag, step, isInvertFlag, textR, Gray_thresholds # 阈值列表重置 Gray_thresholds = [(int(GL), int(GH))] # 按钮处理 if direction == 'left': # 减操作 if index == 1: # GL- GL = max(GL - step, 0) elif index == 2: # GH- GH = max(GH - step, 0) elif index == 0: # back flag = False elif direction == 'right': # 加操作 if index == 1: # GL+ GL = min(GL + step, 255) elif index == 2: # GH+ GH = min(GH + step, 255) elif index == 7: # invert/Ninvert isInvertFlag = not isInvertFlag if isInvertFlag: textR[7] = 'invert' # 更改文本 else: textR[7] = 'Ninvert' elif direction == 'step': # 调整步进 step += 1 if step > 5: step = 1 # 触摸屏事件 def touchAction(): global timeTouch touchP = tp.read(5) # 每3次计为1次,减少sleep时间 if touchP: timeTouch += 1 if timeTouch >= 3: p = touchP timeTouch = 0 if p: x, y = p[0].x, p[0].y # 通过触摸坐标与按钮覆盖位置确定触摸按钮 if x < 200: for i, (rect_x, rect_y, rect_w, rect_h) in enumerate(buttonsL): if (rect_x <= x <= rect_x + rect_w) and (rect_y <= y <= rect_y + rect_h): print(f"操作: {textL[i]}") buttonAction('left', i) elif x > 600: for i, (rect_x, rect_y, rect_w, rect_h) in enumerate(buttonsR): if (rect_x <= x <= rect_x + rect_w) and (rect_y <= y <= rect_y + rect_h): print(f"操作: {textR[i]}") buttonAction('right', i) else: for i, (rect_x, rect_y, rect_w, rect_h) in enumerate(buttonsS): if (rect_x <= x <= rect_x + rect_w) and (rect_y <= y <= rect_y + rect_h): print(f"操作: step") buttonAction('step', 1) time.sleep(0.01) def send_coordinates(center): """通过串口发送坐标数据""" if center: cx, cy = center # 协议格式: $X[坐标x]Y[坐标y]\n data_str = f"$X{cx:04d}Y{cy:04d}\n" uart2.write(data_str.encode()) else: uart2.write(b"$X9999Y9999\n") # 无效坐标标志 def sort_corners(corners): """对矩形角点进行排序(左上、右上、右下、左下)""" if len(corners) != 4: return corners # 计算中心点 center_x = sum(c[0] for c in corners) / 4 center_y = sum(c[1] for c in corners) / 4 # 分类角点 top_left = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] < center_x and c[1] < center_y else float('inf')) top_right = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] > center_x and c[1] < center_y else float('inf')) bottom_right = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] > center_x and c[1] > center_y else float('inf')) bottom_left = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] < center_x and c[1] > center_y else float('inf')) return [top_left, top_right, bottom_right, bottom_left] def matrix_multiply(A, B): """3x3矩阵乘法""" result = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] for i in range(3): for j in range(3): for k in range(3): result[i][j] += A[i][k] * B[k][j] return result def matrix_vector_multiply(M, v): """矩阵向量乘法""" result = [0, 0, 0] for i in range(3): for j in range(3): result[i] += M[i][j] * v[j] return result def matrix_inverse(M): """3x3矩阵求逆""" det = (M[0][0] * (M[1][1]*M[2][2] - M[1][2]*M[2][1]) - M[0][1] * (M[1][0]*M[2][2] - M[1][2]*M[2][0]) + M[0][2] * (M[1][0]*M[2][1] - M[1][1]*M[2][0])) if abs(det) < 1e-10: # 行列式接近0,不可逆 return [[1, 0, 0], [0, 1, 0], [0, 0, 1]] inv_det = 1.0 / det inv = [ [(M[1][1]*M[2][2] - M[2][1]*M[1][2]) * inv_det, (M[0][2]*M[2][1] - M[0][1]*M[2][2]) * inv_det, (M[0][1]*M[1][2] - M[0][2]*M[1][1]) * inv_det], [(M[1][2]*M[2][0] - M[1][0]*M[2][2]) * inv_det, (M[0][0]*M[2][2] - M[0][2]*M[2][0]) * inv_det, (M[1][0]*M[0][2] - M[0][0]*M[1][2]) * inv_det], [(M[1][0]*M[2][1] - M[2][0]*M[1][1]) * inv_det, (M[2][0]*M[0][1] - M[0][0]*M[2][1]) * inv_det, (M[0][0]*M[1][1] - M[1][0]*M[0][1]) * inv_det] ] return inv def calculate_perspective_transform(src_points): """ 计算透视变换矩阵 src_points: 源图像中的四个点 [左上, 右上, 右下, 左下] 返回: 变换矩阵和逆变换矩阵 """ # 目标点 (校正后的正方形) dst_points = [ [0, 0], [PERSPECTIVE_SIZE, 0], [PERSPECTIVE_SIZE, PERSPECTIVE_SIZE], [0, PERSPECTIVE_SIZE] ] # 构建A矩阵 A = [] for i in range(4): x, y = src_points[i] u, v = dst_points[i] A.append([x, y, 1, 0, 0, 0, -u*x, -u*y]) A.append([0, 0, 0, x, y, 1, -v*x, -v*y]) # 构建b向量 b = [] for i in range(4): u, v = dst_points[i] b.append(u) b.append(v) # 解线性方程组 (使用最小二乘法简化) h = [0] * 8 for i in range(8): for j in range(8): # 计算A^T * A ata = 0 for k in range(8): ata += A[k][i] * A[k][j] # 计算A^T * b atb = 0 for k in range(8): atb += A[k][i] * b[k] # 简单求解 if abs(ata) > 1e-10: h[i] = atb / ata # 构造变换矩阵 H = [ [h[0], h[1], h[2]], [h[3], h[4], h[5]], [h[6], h[7], 1] ] # 计算逆矩阵 H_inv = matrix_inverse(H) return H, H_inv def apply_perspective_transform(point, H): """应用透视变换到单个点""" x, y = point src = [x, y, 1] dst = matrix_vector_multiply(H, src) if abs(dst[2]) > 1e-10: dst = [dst[0]/dst[2], dst[1]/dst[2]] return dst def calculate_center(corners): """计算矩形中心点(使用透视校正)""" if len(corners) < 4: return None # 计算几何中心(原始中心) raw_center_x = sum(c[0] for c in corners) / len(corners) raw_center_y = sum(c[1] for c in corners) / len(corners) # 计算透视变换 try: H, H_inv = calculate_perspective_transform(corners) # 将中心点映射到透视校正后的空间 corrected_center = apply_perspective_transform((raw_center_x, raw_center_y), H) # 将校正后的中心点映射回原始图像空间 final_center = apply_perspective_transform(corrected_center, H_inv) return (int(final_center[0]), int(final_center[1])) except Exception as e: # 计算失败时使用原始中心 return (int(raw_center_x), int(raw_center_y)) def is_valid_rectangle(corners, cos_threshold=COS_THRESHOLD): """ 检查是否为有效矩形(内角接近90度) corners: 排序后的四个角点 [左上, 右上, 右下, 左下] cos_threshold: 余弦值阈值(绝对值应小于此值) 返回: True/False """ if len(corners) != 4: return False # 计算四个内角的余弦值 for i in range(4): # 获取三个连续点: A-B-C A = corners[i] B = corners[(i + 1) % 4] C = corners[(i + 2) % 4] # 计算向量BA和BC BA = (A[0] - B[0], A[1] - B[1]) BC = (C[0] - B[0], C[1] - B[1]) # 计算点积和模长 dot_product = BA[0] * BC[0] + BA[1] * BC[1] mod_BA = math.sqrt(BA[0]**2 + BA[1]**2) mod_BC = math.sqrt(BC[0]**2 + BC[1]**2) # 避免除零错误 if mod_BA * mod_BC < 1e-5: return False # 计算余弦值 cos_value = dot_product / (mod_BA * mod_BC) # 检查是否接近90度 (|cos| < threshold) if abs(cos_value) > cos_threshold: return False return True def calculate_rect_properties(corners): """计算矩形的面积和宽比""" if len(corners) != 4: return 0, 0 # 计算边长 top = math.sqrt((corners[1][0] - corners[0][0])**2 + (corners[1][1] - corners[0][1])**2) right = math.sqrt((corners[2][0] - corners[1][0])**2 + (corners[2][1] - corners[1][1])**2) bottom = math.sqrt((corners[3][0] - corners[2][0])**2 + (corners[3][1] - corners[2][1])**2) left = math.sqrt((corners[0][0] - corners[3][0])**2 + (corners[0][1] - corners[3][1])**2) # 计算平宽度和高度 width = (top + bottom) / 2 height = (left + right) / 2 # 计算面积和宽比 area = width * height aspect_ratio = max(width, height) / min(width, height) if min(width, height) > 0 else 0 return area, aspect_ratio def draw_corner_info(img, corners, center, area, aspect_ratio): """在图像上绘制角点和中心信息""" if len(corners) != 4: return # 定义角点颜色和标签 corner_colors = [(0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0)] labels = ["TL", "TR", "BR", "BL"] # 绘制角点 for i, (x, y) in enumerate(corners): color = corner_colors[i] img.draw_circle(int(x), int(y), 8, color=color, thickness=2) img.draw_string(int(x) + 10, int(y) - 10, labels[i], color=color, scale=1) # 绘制矩形边框 for i in range(4): x1, y1 = corners[i] x2, y2 = corners[(i + 1) % 4] img.draw_line(int(x1), int(y1), int(x2), int(y2), color=(0, 255, 255), thickness=2) # 绘制对角线 img.draw_line(int(corners[0][0]), int(corners[0][1]), int(corners[2][0]), int(corners[2][1]), color=(255, 0, 0), thickness=1) img.draw_line(int(corners[1][0]), int(corners[1][1]), int(corners[3][0]), int(corners[3][1]), color=(255, 0, 0), thickness=1) # 绘制中心点 if center: cx, cy = center img.draw_cross(int(cx), int(cy), size=15, color=(255, 0, 255), thickness=2) img.draw_circle(int(cx), int(cy), 5, color=(255, 0, 255), thickness=-1) img.draw_string(int(cx) + 10, int(cy) - 10, "Center", color=(255, 0, 255), scale=1) # 显示矩形属性 img.draw_string(10, 30, f"Area: {area:.1f} px", color=(0, 255, 0), scale=1) img.draw_string(10, 50, f"Aspect: {aspect_ratio:.2f}", color=(0, 255, 0), scale=1) def process_frame(img): """处理图像帧,检测矩形角点和中心(带形状验证)""" img_gray = img.to_grayscale(copy=False) img_bin = img_gray.binary([(GL, GH)]) rects = img_bin.find_rects(threshold=CORNERS_THRESHOLD) # 计算图像总面积 total_image_area = img.width() * img.height() min_area = MIN_AREA_RATIO * total_image_area max_area = MAX_AREA_RATIO * total_image_area # 找出所有可能的矩形 valid_rects = [] for rect in rects: corners = rect.corners() if len(corners) == 4: sorted_corners = sort_corners(corners) # 计算矩形属性 area, aspect_ratio = calculate_rect_properties(sorted_corners) # 检查角度、面积和宽比 angle_valid = is_valid_rectangle(sorted_corners) area_valid = min_area <= area <= max_area aspect_valid = ASPECT_RATIO_MIN <= aspect_ratio <= ASPECT_RATIO_MAX if angle_valid and area_valid and aspect_valid: valid_rects.append((area, aspect_ratio, sorted_corners)) # 选择面积最大的有效矩形 corners = [] center = None area = 0 aspect_ratio = 0 if valid_rects: valid_rects.sort(key=lambda x: x[0], reverse=True) area, aspect_ratio, corners = valid_rects[0] center = calculate_center(corners) # 绘制结果 if center: draw_corner_info(img, corners, center, area, aspect_ratio) else: # 显示未检测到矩形的信息 img.draw_string(img.width()//2 - 50, img.height()//2, "No valid rectangle", color=(255, 0, 0), scale=1) # 资源清理 del img_gray, img_bin gc.collect() return corners, center try: print("初始化摄像头...") # 初始化摄像头 sensor = Sensor(id=SENSOR_ID) sensor.reset() # 设置与显示匹配的分辨率 sensor.set_framesize(width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT) sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0) # sensor.set_hmirror(1) # sensor.set_vflip(1) print("初始化显示器...") # 初始化显示器 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) print("初始化媒体管理器...") # 初始化媒体管理器 MediaManager.init() sensor.run() clock = time.clock() print("开始主循环...") while True: os.exitpoint() clock.tick() # 捕获图像 img = sensor.snapshot(chn=CAM_CHN_ID_0) # 处理按键事件 button_state = button.value() # 获取当前按键状态 current_time = time.ticks_ms() # 获取当前时间(单位:毫秒) # 检测按键从未按下(0)到按下(1)的变化(上升沿) if button_state == 1 and button_last_state == 0: # 检查按键是否在消抖时间外 if current_time - last_press_time > debounce_delay: if button.value() == 1: flag = not flag # 第二次按下按键时进入扫描模式 if not flag: scan_mode = True print("进入扫描模式") last_press_time = current_time # 更新上次按键状态 button_last_state = button_state # 仅在阈值设置界面显示阈值调整UI if flag: changeScreen(img) touchAction() else: # 正常模式下显示原始图像和处理结果 # 处理图像并检测角点 corners, center = process_frame(img) # 发送坐标 send_coordinates(center) time.sleep_ms(20) # 控制处理频率 # 显示FPS img.draw_string(10, 10, f"FPS: {clock.fps():.1f}", color=(255, 0, 0), scale=1) # 显示图像(全屏) img.compressed_for_ide() Display.show_image(img, 0, 0) os.exitpoint() # 可用的退出点 except KeyboardInterrupt: print("程序被用户中断") except Exception as e: print(f"发生错误: {e}") import sys sys.print_exception(e) finally: print("清理资源...") # 清理资源 if sensor: sensor.stop() Display.deinit() MediaManager.deinit() print("程序退出") 以上述代码前81行程序为参照,修改81行以下函数的参数等使其正常运行
最新发布
08-04
import time, os, math from media.sensor import * from media.display import * from media.media import * from machine import FPIOA, PWM, Pin from machine import UART from machine import TOUCH import gc # 图像参数 IMAGE_WIDTH = 352 IMAGE_HEIGHT = 240 SENSOR_ID = 2 sensor = None THRESHOLD = (69, 252) # 二值化阈值 # 显示模式设置 DISPLAY_MODE = "LCD" # 可选 "VIRT", "LCD", "HDMI" # 根据显示模式设置分辨率 if DISPLAY_MODE == "VIRT": DISPLAY_WIDTH, DISPLAY_HEIGHT = 1920, 1080 elif DISPLAY_MODE == "LCD": DISPLAY_WIDTH, DISPLAY_HEIGHT = 800, 480 elif DISPLAY_MODE == "HDMI": DISPLAY_WIDTH, DISPLAY_HEIGHT = 1920, 1080 else: raise ValueError("无效的显示模式") # 角点检测阈值 CORNERS_THRESHOLD = 5000 # 透视变换目标尺寸 PERSPECTIVE_SIZE = 200 # 变换后的矩形大小 # 矩形过滤参数 MIN_AREA_RATIO = 0.01 # 最小面积占图像比例 MAX_AREA_RATIO = 0.8 # 最大面积占图像比例 ASPECT_RATIO_MIN = 0.5 # 最小宽比 ASPECT_RATIO_MAX = 2.0 # 最大宽比 COS_THRESHOLD = 0.3 # 角度余弦阈值(绝对值) fpioa = FPIOA() fpioa.set_function(11, FPIOA.UART2_TXD) fpioa.set_function(12, FPIOA.UART2_RXD) uart = UART(UART.UART2, 115200, 8, 0, 1, timeout=1000) fpioa = FPIOA() fpioa.set_function(53,FPIOA.GPIO53) button = Pin(53, Pin.IN, Pin.PULL_DOWN) # 使用下拉电阻 debounce_delay = 20 # 毫秒 last_press_time = 0 # 上次按键按下的时间,单位为毫秒 timeTouch = 0 tp = TOUCH(0) #按钮文本 textL = ["back", "LL-", "LH-", "AL-", "AH-", "BL-", "BH-","set"] textR = ["LAB", "LL+", "LH+", "AL+", "AH+", "BL+", "BH+","Ninvert"] isLabFlag = True #是否是LAB isInvertFlag = False #是否反转 #虚拟按钮范围,用于触摸点判断 buttonsL = [ (0, 5, 100, 50), # 按钮"back" (0, 65, 100, 50), # 按钮"LL-" (0, 125, 100, 50), # 按钮"LH-" (0, 185, 100, 50), # 按钮"AL-" (0, 245, 100, 50), # 按钮"AH-" (0, 305, 100, 50), # 按钮"BL-" (0, 365, 100, 50), # 按钮"BH-" (0, 425, 100, 50) # 按钮"set" ] buttonsR = [ (700, 5, 100, 50), # 按钮"change" (700, 65, 100, 50), # 按钮"LL+"(与左侧"LL-"对称) (700, 125, 100, 50), # 按钮"LH+"(与左侧"LH-"对称) (700, 185, 100, 50), # 按钮"AL+"(与左侧"AL-"对称) (700, 245, 100, 50), # 按钮"AH+"(与左侧"AH-"对称) (700, 305, 100, 50), # 按钮"BL+"(与左侧"BL-"对称) (700, 365, 100, 50), # 按钮"BH+"(与左侧"BH-"对称) (700, 425, 100, 50) # 按钮" " ] buttonsS = [ (390, 5, 100, 50) ] # 标准LAB范围初始化 L_MIN, L_MAX = 0, 52 # L亮度范围0-100 A_MIN, A_MAX = 31, 90 # a范围-128~127 B_MIN, B_MAX = -38, 109 # b范围-128~127 GL, GH = 0, 255 #灰度范围0~255 step = 1 #脱机阈值步进 LAB_thresholds = [(0, 52, 31, 90, -38, 109)] #LAB阈值列表 Gray_thresholds = [(int(GL), int(GH))] #灰度阈值列表 sensor_id = 2 sensor = None flag = False #页面标志,True为阈值设置 #脱机调整阈值界面设计 def changeScreen(img): img_1 = img.copy() screen = img.copy() #绘制白色背景、阈值画面范围及原始画面 screen.draw_rectangle(0, 0, 800, 480, (255,255,255),fill=True) screen.draw_rectangle(200, 170, 400, 240, (0,0,0),2) screen.draw_image(img_1, 120, 5,x_size=266,y_size=160) #获取画面 LabImg = img_1.binary(LAB_thresholds) GrayImg = img_1.to_grayscale().binary(Gray_thresholds) #是否反转 if isInvertFlag: LabImg = LabImg.invert() GrayImg = GrayImg.invert() #是LAB或Gray,绘制相关阈值数值 if isLabFlag: screen.draw_image(LabImg,200, 180,x_size=400,y_size=240) # 无关键字参数 screen.draw_string_advanced(150, 420, 30, f"L: [{L_MIN} {L_MAX}]", color=(0, 0, 0)) screen.draw_string_advanced(300, 420, 30, f"A: [{A_MIN} {A_MAX}]", color=(0, 0, 0)) screen.draw_string_advanced(500, 420, 30, f"B: [{B_MIN} {B_MAX}]", color=(0, 0, 0)) else : screen.draw_image(GrayImg, 200, 180,x_size=400,y_size=240) screen.draw_string_advanced(150, 420, 30, f"Gray: [{GL} {GH}]", color=(0, 0, 0)) #绘制虚拟按钮,编写文本 for i in range (8): screen.draw_rectangle(0, 5+i*60, 100, 50, (200,201,202),thickness=1, fill=True) screen.draw_string_advanced(30,20+i*60, 20, textL[i], (0,0,0)) screen.draw_rectangle(700, 5+i*60, 100, 50, (200,201,202),thickness=1, fill=True) screen.draw_string_advanced(730,20+i*60, 20, textR[i], (0,0,0)) screen.draw_rectangle(390, 5, 100, 50, (200,201,202),thickness=1, fill=True) screen.draw_string_advanced(400,20, 20, f"step: {step}", (0,0,0)) Display.show_image(screen if flag else img) #触摸屏按钮事件动作 def buttonAction(direction, index): #global获取全局变量,可更改数据 global L_MAX, L_MIN, A_MAX, A_MIN, B_MAX, B_MIN,flag,step global isLabFlag, textR, GL, GH, LAB_thresholds, isInvertFlag, Gray_thresholds #阈值列表重置 LAB_thresholds = [(int(L_MIN), int(L_MAX), int(A_MIN), int(A_MAX), int(B_MIN), int(B_MAX))] Gray_thresholds = [(int(GL), int(GH))] #按钮处理 if direction == 'left': # 减操作 if index == 1: if isLabFlag: L_MIN = max(L_MIN - step, 0) #确保L_MIN >= 0 else : GL = max(GL - step, 0) elif index == 2: if isLabFlag: L_MAX = max(L_MAX - step, 0) else : GH = max(GH - step, 0) elif index == 3: A_MIN = max(A_MIN - step, -128) elif index == 4: A_MAX = max(A_MAX - step, -128) elif index == 5: B_MIN = max(B_MIN - step, -128) elif index == 6: B_MAX = max(B_MAX - step, -128) elif index == 0: flag = False elif direction == 'right': # 加操作 if index == 1: if isLabFlag: L_MIN = min(L_MIN + step, 100) #确保L_MIN <= 100 else : GL = min(GL + step, 255) elif index == 2: if isLabFlag: L_MAX = min(L_MAX + step, 100) else : GH = min(GH + step, 255) elif index == 3: A_MIN = min(A_MIN + step, 127) elif index == 4: A_MAX = min(A_MAX + step, 127) elif index == 5: B_MIN = min(B_MIN + step, 127) elif index == 6: B_MAX = min(B_MAX + step, 127) elif index == 7: isInvertFlag = not isInvertFlag if isInvertFlag: textR[7] = 'invert' #更改文本 else : textR[7] = 'Ninvert' elif index == 0: isLabFlag = not isLabFlag if isLabFlag: textR[0] = 'LAB' textL[1] = 'LL-' textR[1] = 'LL+' textL[2] = 'LH-' textR[2] = 'LH+' else : textR[0] = 'Gray' textL[1] = 'GL-' textR[1] = 'GL+' textL[2] = 'GH-' textR[2] = 'GH+' elif direction == 'step': #调整步进 step += 1 if step > 5: step = 1 #触摸屏事件 def touchAction(): global timeTouch touchP = tp.read(2) #每3次计为1次,减少sleep时间,确保摄像头画面流畅 if touchP: timeTouch+=1 if timeTouch >= 3: p = touchP timeTouch = 0 if p: x, y = p[0].x, p[0].y #通过触摸坐标与按钮覆盖位置确定触摸按钮 if x < 200: for i, (rect_x, rect_y, rect_w, rect_h, *_) in enumerate(buttonsL): if (rect_x <= x <= rect_x + rect_w) and (rect_y <= y <= rect_y + rect_h): print(f"操作: {textL[i]}") buttonAction('left', i) elif x > 600 : for i, (rect_x, rect_y, rect_w, rect_h, *_) in enumerate(buttonsR): if (rect_x <= x <= rect_x + rect_w) and (rect_y <= y <= rect_y + rect_h): print(f"操作: {textR[i]}") buttonAction('right', i) else : for i, (rect_x, rect_y, rect_w, rect_h, *_) in enumerate(buttonsS): if (rect_x <= x <= rect_x + rect_w) and (rect_y <= y <= rect_y + rect_h): print(f"操作: step") buttonAction('step', 1) time.sleep(0.01) def send_coordinates(center): """通过串口发送坐标数据""" if center: cx, cy = center # 协议格式: $X[坐标x]Y[坐标y]\n data_str = f"$X{cx:04d}Y{cy:04d}\n" uart.write(data_str.encode()) else: uart.write(b"$X9999Y9999\n") # 无效坐标标志 def sort_corners(corners): """对矩形角点进行排序(左上、右上、右下、左下)""" if len(corners) != 4: return corners # 计算中心点 center_x = sum(c[0] for c in corners) / 4 center_y = sum(c[1] for c in corners) / 4 # 分类角点 top_left = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] < center_x and c[1] < center_y else float('inf')) top_right = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] > center_x and c[1] < center_y else float('inf')) bottom_right = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] > center_x and c[1] > center_y else float('inf')) bottom_left = min(corners, key=lambda c: (c[0] - center_x)**2 + (c[1] - center_y)**2 if c[0] < center_x and c[1] > center_y else float('inf')) return [top_left, top_right, bottom_right, bottom_left] def matrix_multiply(A, B): """3x3矩阵乘法""" result = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] for i in range(3): for j in range(3): for k in range(3): result[i][j] += A[i][k] * B[k][j] return result def matrix_vector_multiply(M, v): """矩阵向量乘法""" result = [0, 0, 0] for i in range(3): for j in range(3): result[i] += M[i][j] * v[j] return result def matrix_inverse(M): """3x3矩阵求逆""" det = (M[0][0] * (M[1][1]*M[2][2] - M[1][2]*M[2][1]) - M[0][1] * (M[1][0]*M[2][2] - M[1][2]*M[2][0]) + M[0][2] * (M[1][0]*M[2][1] - M[1][1]*M[2][0])) if abs(det) < 1e-10: # 行列式接近0,不可逆 return [[1, 0, 0], [0, 1, 0], [0, 0, 1]] inv_det = 1.0 / det inv = [ [(M[1][1]*M[2][2] - M[2][1]*M[1][2]) * inv_det, (M[0][2]*M[2][1] - M[0][1]*M[2][2]) * inv_det, (M[0][1]*M[1][2] - M[0][2]*M[1][1]) * inv_det], [(M[1][2]*M[2][0] - M[1][0]*M[2][2]) * inv_det, (M[0][0]*M[2][2] - M[0][2]*M[2][0]) * inv_det, (M[1][0]*M[0][2] - M[0][0]*M[1][2]) * inv_det], [(M[1][0]*M[2][1] - M[2][0]*M[1][1]) * inv_det, (M[2][0]*M[0][1] - M[0][0]*M[2][1]) * inv_det, (M[0][0]*M[1][1] - M[1][0]*M[0][1]) * inv_det] ] return inv def calculate_perspective_transform(src_points): """ 计算透视变换矩阵 src_points: 源图像中的四个点 [左上, 右上, 右下, 左下] 返回: 变换矩阵和逆变换矩阵 """ # 目标点 (校正后的正方形) dst_points = [ [0, 0], [PERSPECTIVE_SIZE, 0], [PERSPECTIVE_SIZE, PERSPECTIVE_SIZE], [0, PERSPECTIVE_SIZE] ] # 构建A矩阵 A = [] for i in range(4): x, y = src_points[i] u, v = dst_points[i] A.append([x, y, 1, 0, 0, 0, -u*x, -u*y]) A.append([0, 0, 0, x, y, 1, -v*x, -v*y]) # 构建b向量 b = [] for i in range(4): u, v = dst_points[i] b.append(u) b.append(v) # 解线性方程组 (使用最小二乘法简化) h = [0] * 8 for i in range(8): for j in range(8): # 计算A^T * A ata = 0 for k in range(8): ata += A[k][i] * A[k][j] # 计算A^T * b atb = 0 for k in range(8): atb += A[k][i] * b[k] # 简单求解 if abs(ata) > 1e-10: h[i] = atb / ata # 构造变换矩阵 H = [ [h[0], h[1], h[2]], [h[3], h[4], h[5]], [h[6], h[7], 1] ] # 计算逆矩阵 H_inv = matrix_inverse(H) return H, H_inv def apply_perspective_transform(point, H): """应用透视变换到单个点""" x, y = point src = [x, y, 1] dst = matrix_vector_multiply(H, src) if abs(dst[2]) > 1e-10: dst = [dst[0]/dst[2], dst[1]/dst[2]] return dst def calculate_center(corners): """计算矩形中心点(使用透视校正)""" if len(corners) < 4: return None # 计算几何中心(原始中心) raw_center_x = sum(c[0] for c in corners) / len(corners) raw_center_y = sum(c[1] for c in corners) / len(corners) # 计算透视变换 try: H, H_inv = calculate_perspective_transform(corners) # 将中心点映射到透视校正后的空间 corrected_center = apply_perspective_transform((raw_center_x, raw_center_y), H) # 将校正后的中心点映射回原始图像空间 final_center = apply_perspective_transform(corrected_center, H_inv) return (int(final_center[0]), int(final_center[1])) except Exception as e: # 计算失败时使用原始中心 return (int(raw_center_x), int(raw_center_y)) def is_valid_rectangle(corners, cos_threshold=COS_THRESHOLD): """ 检查是否为有效矩形(内角接近90度) corners: 排序后的四个角点 [左上, 右上, 右下, 左下] cos_threshold: 余弦值阈值(绝对值应小于此值) 返回: True/False """ if len(corners) != 4: return False # 计算四个内角的余弦值 for i in range(4): # 获取三个连续点: A-B-C A = corners[i] B = corners[(i + 1) % 4] C = corners[(i + 2) % 4] # 计算向量BA和BC BA = (A[0] - B[0], A[1] - B[1]) BC = (C[0] - B[0], C[1] - B[1]) # 计算点积和模长 dot_product = BA[0] * BC[0] + BA[1] * BC[1] mod_BA = math.sqrt(BA[0]**2 + BA[1]**2) mod_BC = math.sqrt(BC[0]**2 + BC[1]**2) # 避免除零错误 if mod_BA * mod_BC < 1e-5: return False # 计算余弦值 cos_value = dot_product / (mod_BA * mod_BC) # 检查是否接近90度 (|cos| < threshold) if abs(cos_value) > cos_threshold: return False return True def calculate_rect_properties(corners): """计算矩形的面积和宽比""" if len(corners) != 4: return 0, 0 # 计算边长 top = math.sqrt((corners[1][0] - corners[0][0])**2 + (corners[1][1] - corners[0][1])**2) right = math.sqrt((corners[2][0] - corners[1][0])**2 + (corners[2][1] - corners[1][1])**2) bottom = math.sqrt((corners[3][0] - corners[2][0])**2 + (corners[3][1] - corners[2][1])**2) left = math.sqrt((corners[0][0] - corners[3][0])**2 + (corners[0][1] - corners[3][1])**2) # 计算平宽度和高度 width = (top + bottom) / 2 height = (left + right) / 2 # 计算面积和宽比 area = width * height aspect_ratio = max(width, height) / min(width, height) if min(width, height) > 0 else 0 return area, aspect_ratio def draw_corner_info(img, corners, center, area, aspect_ratio): """在图像上绘制角点和中心信息""" if len(corners) != 4: return # 定义角点颜色和标签 corner_colors = [(0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0)] labels = ["TL", "TR", "BR", "BL"] # 绘制角点 for i, (x, y) in enumerate(corners): color = corner_colors[i] img.draw_circle(int(x), int(y), 8, color=color, thickness=2) img.draw_string(int(x) + 10, int(y) - 10, labels[i], color=color, scale=1) # 绘制矩形边框 for i in range(4): x1, y1 = corners[i] x2, y2 = corners[(i + 1) % 4] img.draw_line(int(x1), int(y1), int(x2), int(y2), color=(0, 255, 255), thickness=2) # 绘制对角线 img.draw_line(int(corners[0][0]), int(corners[0][1]), int(corners[2][0]), int(corners[2][1]), color=(255, 0, 0), thickness=1) img.draw_line(int(corners[1][0]), int(corners[1][1]), int(corners[3][0]), int(corners[3][1]), color=(255, 0, 0), thickness=1) # 绘制中心点 if center: cx, cy = center img.draw_cross(int(cx), int(cy), size=15, color=(255, 0, 255), thickness=2) img.draw_circle(int(cx), int(cy), 5, color=(255, 0, 255), thickness=-1) img.draw_string(int(cx) + 10, int(cy) - 10, "Center", color=(255, 0, 255), scale=1) # 显示矩形属性 img.draw_string(10, 30, f"Area: {area:.1f} px", color=(0, 255, 0), scale=1) img.draw_string(10, 50, f"Aspect: {aspect_ratio:.2f}", color=(0, 255, 0), scale=1) def process_frame(img): """处理图像帧,检测矩形角点和中心(带形状验证)""" img_gray = img.to_grayscale(copy=False) img_bin = img_gray.binary([THRESHOLD]) rects = img_bin.find_rects(threshold=CORNERS_THRESHOLD) # 计算图像总面积 total_image_area = img.width() * img.height() min_area = MIN_AREA_RATIO * total_image_area max_area = MAX_AREA_RATIO * total_image_area # 找出所有可能的矩形 valid_rects = [] for rect in rects: corners = rect.corners() if len(corners) == 4: sorted_corners = sort_corners(corners) # 计算矩形属性 area, aspect_ratio = calculate_rect_properties(sorted_corners) # 检查角度、面积和宽比 angle_valid = is_valid_rectangle(sorted_corners) area_valid = min_area <= area <= max_area aspect_valid = ASPECT_RATIO_MIN <= aspect_ratio <= ASPECT_RATIO_MAX if angle_valid and area_valid and aspect_valid: valid_rects.append((area, aspect_ratio, sorted_corners)) # 选择面积最大的有效矩形 corners = [] center = None area = 0 aspect_ratio = 0 if valid_rects: valid_rects.sort(key=lambda x: x[0], reverse=True) area, aspect_ratio, corners = valid_rects[0] center = calculate_center(corners) # 绘制结果 if center: draw_corner_info(img, corners, center, area, aspect_ratio) else: # 显示未检测到矩形的信息 img.draw_string(img.width()//2 - 50, img.height()//2, "No valid rectangle", color=(255, 0, 0), scale=1) # 资源清理 del img_gray, img_bin gc.collect() return corners, center try: print("初始化摄像头...") # 初始化摄像头 sensor = Sensor(id=SENSOR_ID) sensor.reset() sensor.set_framesize(width=IMAGE_WIDTH ,height=IMAGE_HEIGHT) # 352x240 sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0) sensor.set_hmirror(1) sensor.set_vflip(1) print("初始化显示器...") # 初始化显示器 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) print("初始化媒体管理器...") # 初始化媒体管理器 MediaManager.init() sensor.run() clock = time.clock() print("开始主循环...") while True: os.exitpoint() clock.tick() # 捕获图像 img = sensor.snapshot(chn=CAM_CHN_ID_0) button_state = button.value() # 获取当前按键状态 current_time = time.ticks_ms() # 获取当前时间(单位:毫秒) # 检测按键从未按下(0)到按下(1)的变化(上升沿) if button_state == 1 and button_last_state == 0: # 检查按键是否在消抖时间外 if current_time - last_press_time > debounce_delay: if button.value() == 1: flag = not flag last_press_time = current_time # 更新按键按下时间 # 更新上次按键状态 button_last_state = button_state blobs = img.find_blobs(LAB_thresholds,invert=isInvertFlag,area_threshold = 2000) if blobs: # 遍历每个检测到的颜色块 for blob in blobs: # 绘制颜色块的外接矩形 # blob[0:4] 表示颜色块的矩形框 [x, y, w, h], img.draw_rectangle(blob[0:4]) # 在颜色块的中心绘制一个十字 # blob[5] 和 blob[6] 分别是颜色块的中心坐标 (cx, cy) img.draw_cross(blob[5], blob[6]) changeScreen(img) touchAction() # 处理图像并检测角点 corners, center = process_frame(img) # 发送坐标 send_coordinates(center) time.sleep_ms(20) # 控制发送频率 # 显示FPS img.draw_string(10, 10, f"FPS: {clock.fps():.1f}", color=(255, 0, 0), scale=1) # 显示图像 img.compressed_for_ide() Display.show_image(img, x=int((DISPLAY_WIDTH - img.width()) / 2), y=int((DISPLAY_HEIGHT - img.height()) / 2)) os.exitpoint() # 可用的退出点 except KeyboardInterrupt: print("程序被用户中断") except Exception as e: print(f"发生错误: {e}") import sys sys.print_exception(e) finally: print("清理资源...") # 清理资源 if sensor: sensor.stop() Display.deinit() MediaManager.deinit() print("程序退出") 上述代码运行时,屏幕模块上的画面会来回闪动,而显示画面也比较小,解决上述问题放大显示画面
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值