修改sort_area_size参数的注意事项

调整sort_area_size参数
本文介绍了如何通过不同的SQL命令来调整Oracle数据库中的sort_area_size参数。这些方法包括仅对当前会话生效、对后续登录的账号生效以及在数据库重启后生效的不同设置方式。
  1. 可以使用“alter session set sort_area_size=3000000”使得新值只对当前连接生效
  2. 可以使用“alter sysetm set sort_area_size=3000000 deferred”使得新值对随后登录的账号生效,而对当前连接不生效
  3. 可以使用“alter system set sort_area_size=3000000 scope=spfile”使得新值在数据库重新启动后生效
  4. 这里的数值不能包含K、M、G作单位,多数错误基本都是在数值中包含了这些单位引起的

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/37724/viewspace-152528/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/37724/viewspace-152528/

import re import os from pptx import Presentation from pptx.util import Inches, Pt from pptx.enum.text import PP_ALIGN from PIL import Image import math def natural_sort_key(s): """ 自然排序键函数,确保数字按数值大小排序 例如: ['10', '2'] 会排序为 ['2', '10'] """ return [int(text) if text.isdigit() else text.lower() for text in re.split(r'(\d+)', s)] def create_ppt_from_folders(folder_path, output_ppt_path): """ 将图片文件夹整合到PPT,每个文件夹一张幻灯片,图片占据上部3/5区域 确保按文件夹序号顺序排列 """ # 创建PPT对象 prs = Presentation() # 设置幻灯片尺寸(16:9) prs.slide_width = Inches(13.33) # 宽度 prs.slide_height = Inches(7.5) # 高度 # 获取所有文件夹(按自然顺序排序) folders = [f for f in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, f))] folders.sort(key=natural_sort_key) # 使用自然排序 if not folders: print("未找到任何图片文件夹") return print(f"共发现 {len(folders)} 个文件夹,将按序号顺序创建幻灯片") # 遍历每个文件夹(已按正确顺序) for folder in folders: folder_full_path = os.path.join(folder_path, folder) # 创建新幻灯片 slide = prs.slides.add_slide(prs.slide_layouts[6]) # 添加标题(文件夹名称) title = slide.shapes.add_textbox(Inches(0.5), Inches(0.2), Inches(12), Inches(0.5)) title_frame = title.text_frame title_para = title_frame.add_paragraph() title_para.text = f"图片集: {folder}" title_para.font.bold = True title_para.font.size = Pt(24) title_para.alignment = PP_ALIGN.CENTER # 获取文件夹中的所有图片(按文件名自然排序) image_files = [f for f in os.listdir(folder_full_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))] image_files.sort(key=natural_sort_key) # 图片也使用自然排序 if not image_files: print(f"文件夹 '{folder}' 中没有找到图片") continue # 计算图片区域尺寸(占据幻灯片上部3/5) slide_height = prs.slide_height top_area_height = slide_height * 3 / 5 # 上部3/5区域高度 bottom_area_height = slide_height * 2 / 5 # 下部2/5区域高度 # 根据图片数量确定布局 img_count = len(image_files) max_per_row = 3 # 每行最多图片数 # 计算行数和列数 rows = min(3, (img_count + max_per_row - 1) // max_per_row) # 最多3行 cols = min(max_per_row, img_count) if img_count < max_per_row else max_per_row # 计算单个图片尺寸 img_width = Inches(3) if cols <= 1 else Inches(10) / cols img_height = top_area_height / rows - Inches(0.2) # 留出间距 # 添加所有图片到幻灯片 print(f"在幻灯片 '{folder}' 中添加 {img_count} 张图片...") for i, img_file in enumerate(image_files): img_path = os.path.join(folder_full_path, img_file) # 获取图片原始尺寸(用于保持宽高比) with Image.open(img_path) as img: orig_width, orig_height = img.size aspect_ratio = orig_width / orig_height # 计算图片位置 row = i // cols col = i % cols # 计算位置坐标 left = Inches(1) + col * (img_width + Inches(0.2)) top = Inches(1) + row * img_height # 调整尺寸保持宽高比 if img_height * aspect_ratio <= img_width: # 高度受限 width = img_height * aspect_ratio height = img_height left += (img_width - width) / 2 # 水平居中 else: # 宽度受限 width = img_width height = img_width / aspect_ratio top += (img_height - height) / 2 # 垂直居中 # 添加图片到幻灯片 try: slide.shapes.add_picture(img_path, left, top, width, height) except Exception as e: print(f"添加图片失败: {img_path} - {str(e)}") # 在下部区域添加说明文字 textbox = slide.shapes.add_textbox(Inches(1), top_area_height + Inches(0.5), Inches(10), bottom_area_height - Inches(1)) tf = textbox.text_frame tf.word_wrap = True p = tf.add_paragraph() p.text = f"文件夹 '{folder}' 包含 {img_count} 张图片" p.font.size = Pt(18) p.alignment = PP_ALIGN.CENTER # 保存PPT prs.save(output_ppt_path) print(f"\nPPT创建成功!共 {len(folders)} 张幻灯片,按文件夹序号顺序排列") if __name__ == "__main__": # 配置路径 image_folders_path = "E:/pythonproject313/转换/pic" # 图片文件夹根目录 output_ppt = "E:/pythonproject313/转换/图片展示.pptx" # 输出PPT文件路径 create_ppt_from_folders(image_folders_path, output_ppt) 帮我删除幻灯片中下方的文字
08-13
import time, os, sys import math import cv_lite # 导入cv_lite扩展模块 import ulab.numpy as np # 导入numpy库 from media.sensor import * from media.display import * from media.media import * from machine import UART, FPIOA # --------------------------- 硬件初始化 --------------------------- # 串口初始化 fpioa = FPIOA() fpioa.set_function(3, FPIOA.UART1_TXD) fpioa.set_function(4, FPIOA.UART1_RXD) uart = UART(UART.UART1, 115200) # 屏幕分辨率设置 lcd_width = 800 lcd_height = 480 # 摄像头初始化(注意:保留RGB模式用于色块检测,后续转为灰度图用于矩形检测) sensor = Sensor(width=1280, height=960) sensor.reset() sensor.set_framesize(width=320, height=240) # 降低分辨率提高帧率 sensor.set_pixformat(Sensor.RGB565) # 保留彩色用于紫色色块检测 # 显示初始化 Display.init(Display.ST7701, width=lcd_width, height=lcd_height, to_ide=True) MediaManager.init() sensor.run() # --------------------------- 配置参数 --------------------------- # 矩形检测核心参数(基于cv_lite) canny_thresh1 = 50 # Canny边缘检测低阈值 canny_thresh2 = 150 # Canny边缘检测高阈值 approx_epsilon = 0.04 # 多边形拟合精度(越小越精确) area_min_ratio = 0.005 # 最小面积比例(相对于图像总面积) max_angle_cos = 0.3 # 角度余弦阈值(越小越接近矩形) gaussian_blur_size = 3 # 高斯模糊核尺寸(奇数) # 原有筛选参数 MIN_AREA = 100 # 最小面积阈值 MAX_AREA = 100000 # 最大面积阈值 MIN_ASPECT_RATIO = 0.3 # 最小宽高比 MAX_ASPECT_RATIO = 3.0 # 最大宽高比 # 虚拟坐标与圆形参数 BASE_RADIUS = 45 # 基础半径(虚拟坐标单位) POINTS_PER_CIRCLE = 24 # 圆形采样点数量 PURPLE_THRESHOLD = (20, 60, 15, 70, -70, -20) # 紫色色块阈值 # 基础矩形参数(固定方向,不再自动切换) RECT_WIDTH = 210 # 固定矩形宽度 RECT_HEIGHT = 95 # 固定矩形高度 # 移除自动切换方向的逻辑,始终使用固定宽高的虚拟矩形 # --------------------------- 工具函数 --------------------------- def calculate_distance(p1, p2): return math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])** 2) def calculate_center(points): if not points: return (0, 0) sum_x = sum(p[0] for p in points) sum_y = sum(p[1] for p in points) return (sum_x / len(points), sum_y / len(points)) def is_valid_rect(corners): edges = [calculate_distance(corners[i], corners[(i+1)%4]) for i in range(4)] # 对边比例校验 ratio1 = edges[0] / max(edges[2], 0.1) ratio2 = edges[1] / max(edges[3], 0.1) valid_ratio = 0.5 < ratio1 < 1.5 and 0.5 < ratio2 < 1.5 # 面积校验 area = 0 for i in range(4): x1, y1 = corners[i] x2, y2 = corners[(i+1) % 4] area += (x1 * y2 - x2 * y1) area = abs(area) / 2 valid_area = MIN_AREA < area < MAX_AREA # 宽高比校验 width = max(p[0] for p in corners) - min(p[0] for p in corners) height = max(p[1] for p in corners) - min(p[1] for p in corners) aspect_ratio = width / max(height, 0.1) valid_aspect = MIN_ASPECT_RATIO < aspect_ratio < MAX_ASPECT_RATIO return valid_ratio and valid_area and valid_aspect def detect_purple_blobs(img): return img.find_blobs( [PURPLE_THRESHOLD], pixels_threshold=100, area_threshold=100, merge=True ) def send_circle_points(points): if not points: return count = len(points) msg = f"$$C,{count}," for x, y in points: msg += f"{x},{y}," msg = msg.rstrip(',') + "##" uart.write(msg) # print(f"发送圆形点: {msg}") def get_perspective_matrix(src_pts, dst_pts): """计算透视变换矩阵""" A = [] B = [] for i in range(4): x, y = src_pts[i] u, v = dst_pts[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.append(u) B.append(v) # 高斯消元求解矩阵 n = 8 for i in range(n): max_row = i for j in range(i, len(A)): if abs(A[j][i]) > abs(A[max_row][i]): max_row = j A[i], A[max_row] = A[max_row], A[i] B[i], B[max_row] = B[max_row], B[i] pivot = A[i][i] if abs(pivot) < 1e-8: return None for j in range(i, n): A[i][j] /= pivot B[i] /= pivot for j in range(len(A)): if j != i and A[j][i] != 0: factor = A[j][i] for k in range(i, n): A[j][k] -= factor * A[i][k] B[j] -= factor * B[i] return [ [B[0], B[1], B[2]], [B[3], B[4], B[5]], [B[6], B[7], 1.0] ] def transform_points(points, matrix): """应用透视变换将虚拟坐标映射到原始图像坐标""" transformed = [] for (x, y) in points: x_hom = x * matrix[0][0] + y * matrix[0][1] + matrix[0][2] y_hom = x * matrix[1][0] + y * matrix[1][1] + matrix[1][2] w_hom = x * matrix[2][0] + y * matrix[2][1] + matrix[2][2] if abs(w_hom) > 1e-8: transformed.append((x_hom / w_hom, y_hom / w_hom)) return transformed def sort_corners(corners): """将矩形角点按左上、右上、右下、左下顺序排序""" center = calculate_center(corners) sorted_corners = sorted(corners, key=lambda p: math.atan2(p[1]-center[1], p[0]-center[0])) # 调整顺序为左上、右上、右下、左下 if len(sorted_corners) == 4: left_top = min(sorted_corners, key=lambda p: p[0]+p[1]) index = sorted_corners.index(left_top) sorted_corners = sorted_corners[index:] + sorted_corners[:index] return sorted_corners def get_rectangle_orientation(corners): """计算矩形的主方向角(水平边与x轴的夹角)""" if len(corners) != 4: return 0 # 计算上边和右边的向量 top_edge = (corners[1][0] - corners[0][0], corners[1][1] - corners[0][1]) right_edge = (corners[2][0] - corners[1][0], corners[2][1] - corners[1][1]) # 选择较长的边作为主方向 if calculate_distance(corners[0], corners[1]) > calculate_distance(corners[1], corners[2]): main_edge = top_edge else: main_edge = right_edge # 计算主方向角(弧度) angle = math.atan2(main_edge[1], main_edge[0]) return angle # --------------------------- 主循环 --------------------------- clock = time.clock() image_shape = [sensor.height(), sensor.width()] # [高, 宽] 用于cv_lite while True: clock.tick() img = sensor.snapshot() # 1. 检测紫色色块(保留原有功能) purple_blobs = detect_purple_blobs(img) for blob in purple_blobs: img.draw_rectangle(blob[0:4], color=(255, 0, 255), thickness=1) img.draw_cross(blob.cx(), blob.cy(), color=(255, 0, 255), thickness=1) # 2. 矩形检测(使用cv_lite替换原有实现) # 2.1 将RGB图像转为灰度图(用于矩形检测) gray_img = img.to_grayscale() img_np = gray_img.to_numpy_ref() # 转为numpy数组供cv_lite使用 # 2.2 调用cv_lite矩形检测函数(带角点) rects = cv_lite.grayscale_find_rectangles_with_corners( image_shape, # 图像尺寸 [高, 宽] img_np, # 灰度图数据 canny_thresh1, # Canny低阈值 canny_thresh2, # Canny高阈值 approx_epsilon, # 多边形拟合精度 area_min_ratio, # 最小面积比例 max_angle_cos, # 角度余弦阈值 gaussian_blur_size # 高斯模糊尺寸 ) # 3. 筛选最小矩形(保留原有逻辑) min_area = float('inf') smallest_rect = None smallest_rect_corners = None # 存储最小矩形的角点 for rect in rects: # rect格式: [x, y, w, h, c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4.x, c4.y] x, y, w, h = rect[0], rect[1], rect[2], rect[3] # 提取四个角点 corners = [ (rect[4], rect[5]), # 角点1 (rect[6], rect[7]), # 角点2 (rect[8], rect[9]), # 角点3 (rect[10], rect[11]) # 角点4 ] # 验证矩形有效性 if is_valid_rect(corners): # 计算面积 area = w * h # 直接使用矩形宽高计算面积(更高效) # 更新最小矩形 if area < min_area: min_area = area smallest_rect = (x, y, w, h) smallest_rect_corners = corners # 4. 处理最小矩形(修改后:固定虚拟矩形方向) if smallest_rect and smallest_rect_corners: x, y, w, h = smallest_rect corners = smallest_rect_corners # 对矩形角点进行排序 sorted_corners = sort_corners(corners) # 绘制矩形边框和角点 for i in range(4): x1, y1 = sorted_corners[i] x2, y2 = sorted_corners[(i+1) % 4] img.draw_line(x1, y1, x2, y2, color=(255, 0, 0), thickness=2) for p in sorted_corners: img.draw_circle(p[0], p[1], 5, color=(0, 255, 0), thickness=2) # 计算并绘制矩形中心点 rect_center = calculate_center(sorted_corners) rect_center_int = (int(round(rect_center[0])), int(round(rect_center[1]))) img.draw_circle(rect_center_int[0], rect_center_int[1], 4, color=(0, 255, 255), thickness=2) # 计算矩形主方向角(仅用于参考,不再影响虚拟矩形方向) angle = get_rectangle_orientation(sorted_corners) # 【核心修改】移除自动切换方向逻辑,固定使用预设的虚拟矩形尺寸和方向 # 固定虚拟矩形(不再根据实际宽高比切换) virtual_rect = [ (0, 0), (RECT_WIDTH, 0), (RECT_WIDTH, RECT_HEIGHT), (0, RECT_HEIGHT) ] # 【核心修改】固定圆形半径参数(不再根据实际宽高比调整) radius_x = BASE_RADIUS radius_y = BASE_RADIUS # 【核心修改】固定虚拟中心(基于固定的宽高) virtual_center = (RECT_WIDTH / 2, RECT_HEIGHT / 2) # 在虚拟矩形中生成椭圆点集(映射后为正圆) virtual_circle_points = [] for i in range(POINTS_PER_CIRCLE): angle_rad = 2 * math.pi * i / POINTS_PER_CIRCLE x_virt = virtual_center[0] + radius_x * math.cos(angle_rad) y_virt = virtual_center[1] + radius_y * math.sin(angle_rad) virtual_circle_points.append((x_virt, y_virt)) # 计算透视变换矩阵并映射坐标 matrix = get_perspective_matrix(virtual_rect, sorted_corners) if matrix: mapped_points = transform_points(virtual_circle_points, matrix) int_points = [(int(round(x)), int(round(y))) for x, y in mapped_points] # 绘制圆形 for (px, py) in int_points: img.draw_circle(px, py, 2, color=(255, 0, 255), thickness=2) # 绘制圆心 mapped_center = transform_points([virtual_center], matrix) if mapped_center: cx, cy = map(int, map(round, mapped_center[0])) img.draw_circle(cx, cy, 3, color=(0, 0, 255), thickness=1) # 发送坐标 send_circle_points(int_points) # 5. 显示与性能统计 fps = clock.fps() img.draw_string_advanced(10, 10, 20, f"FPS: {fps:.1f}", color=(255, 255, 255)) # 显示FPS # 显示图像 Display.show_image(img, x=round((lcd_width-sensor.width())/2), y=round((lcd_height-sensor.height())/2)) print(f"FPS: {fps:.1f}") # 打印FPS 给这个矩形识别加一个面积限制,排除面积过小的矩形
08-03
调用修改后的方法,涉及检测花屏的方法如下: def detect_screen_flicker(self, threshold=70, abnormal_ratio=0.1): """ 自动检测文件夹中的MP4视频是否存在花屏 检测到花屏返回False,未检测到返回True 检测完成后自动删除flicker文件夹下的所有文件 :param threshold: 帧间像素差值阈值 :param abnormal_ratio: 异常像素占比阈值 :return: (检测结果, 花屏帧比例) """ current_file = Path(__file__).resolve() PROJECT_ROOT = current_file.parent.parent.parent RESOURCES_DIR = os.path.join(PROJECT_ROOT, "resource", "Appshow", "flicker") # 查找所有MP4格式视频 video_files = [f for f in glob.glob(os.path.join(RESOURCES_DIR, "*")) if os.path.isfile(f) and f.lower().endswith(('.mp4'))] # 无视频文件处理 if not video_files: print(f"错误:文件夹 {RESOURCES_DIR} 中未找到MP4视频文件") self._clean_flicker_dir(RESOURCES_DIR) # 删除文件 return False, -1 # 多个视频文件处理 if len(video_files) > 1: print(f"警告:发现多个MP4文件,将检测第一个文件: {os.path.basename(video_files[0])}") video_path = video_files[0] cap = cv2.VideoCapture(video_path) if not cap.isOpened(): print(f"错误:无法打开视频文件: {video_path}") self._clean_flicker_dir(RESOURCES_DIR) # 删除文件 return False, -1 prev_frame = None ref_frame = None flicker_frame_count = 0 total_frame_count = 0 consecutive_flicker = 0 is_initial_normal = False # 初始状态标记 while cap.isOpened(): ret, frame = cap.read() if not ret: break # 预处理 gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray_frame = cv2.GaussianBlur(gray_frame, (7, 7), 5) # 加强降噪 total_frame_count += 1 # 初始化参考帧 if ref_frame is None: ref_frame = gray_frame.copy() prev_frame = gray_frame.copy() continue # 动态基准帧更新机制(解决全花屏问题) if not is_initial_normal: frame_diff_prev = cv2.absdiff(prev_frame, gray_frame) init_change_ratio = np.sum(frame_diff_prev > threshold) / frame_diff_prev.size if total_frame_count < 10 and init_change_ratio < 0.005: # 前10帧几乎无变化 ref_frame = gray_frame.copy() # 重置基准帧 prev_frame = gray_frame.copy() is_initial_normal = True continue # 计算帧间差异 frame_diff_prev = cv2.absdiff(prev_frame, gray_frame) frame_diff_ref = cv2.absdiff(ref_frame, gray_frame) frame_area = frame_diff_prev.size # 检测条件(三重对比+绝对检测) cond1 = np.sum(frame_diff_prev > threshold) / frame_area > abnormal_ratio cond2 = np.sum(frame_diff_ref > threshold) / frame_area > abnormal_ratio cond3 = self._check_local_abnormal(frame_diff_ref, threshold) cond4 = self._check_absolute_abnormality(gray_frame) # 绝对异常检测 # 花屏判定(连续3帧确认) if cond1 or cond2 or cond3 or cond4: consecutive_flicker += 1 if consecutive_flicker >= 5: flicker_frame_count += 1 else: consecutive_flicker = 0 # 每100帧更新基准帧(防止长期偏移) if total_frame_count % 100 == 0: ref_frame = gray_frame.copy() prev_frame = gray_frame.copy() cap.release() # 结果计算与判定 flicker_ratio = flicker_frame_count / (total_frame_count - 1) if total_frame_count > 1 else 0 is_normal = flicker_ratio <= 0.1 # 花屏帧≤10%视为正常 print("\n" + "=" * 50) print(f"视频分析完成: {os.path.basename(video_path)}") print(f"检测结果: {'视频正常' if is_normal else '检测到花屏'}") print(f"花屏帧比例: {flicker_ratio:.2%}") print(f"总帧数: {total_frame_count} | 花屏帧数: {flicker_frame_count}") print("=" * 50) # 清理文件(实际运行时取消注释) # self._clean_flicker_dir(RESOURCES_DIR) return is_normal, flicker_ratio def _check_local_abnormal(self, frame_diff, base_threshold, grid_size=8, local_threshold=0.35): """ 局部区域异常检测(网格检测) :param frame_diff: 帧差异矩阵 :param base_threshold: 像素差异阈值 :param grid_size: 网格划分尺寸(默认8x8) :param local_threshold: 局部异常比例阈值 """ h, w = frame_diff.shape grid_h, grid_w = max(1, h // grid_size), max(1, w // grid_size) # 防止除零 for i in range(grid_size): for j in range(grid_size): y_start = i * grid_h y_end = min((i + 1) * grid_h, h) x_start = j * grid_w x_end = min((j + 1) * grid_w, w) grid = frame_diff[y_start:y_end, x_start:x_end] grid_area = grid.shape[0] * grid.shape[1] if grid_area == 0: continue grid_abnormal = np.sum(grid > base_threshold) grid_ratio = grid_abnormal / grid_area if grid_ratio > local_threshold: return True return False def _check_absolute_abnormality(self, gray_frame, edge_threshold=0.15, color_deviation=45): """ 绝对异常检测(基于纹理和颜色特征) :param gray_frame: 灰度帧 :param edge_threshold: 边缘密度阈值(低于此值视为异常) :param color_deviation: 颜色标准差阈值(低于此值视为异常) """ # 边缘密度检测 edges = cv2.Canny(gray_frame, 100, 200) edge_density = np.sum(edges > 0) / edges.size if edge_density < edge_threshold: return True # 颜色分布检测 hist = cv2.calcHist([gray_frame], [0], None, [256], [0, 256]) hist_sorted = np.sort(hist, axis=None)[::-1] # 降序排列 # 计算主颜色占比和标准差 main_color_ratio = hist_sorted[:3].sum() / hist.sum() color_std = np.std(gray_frame) # 主色聚集且色差低判为异常 if main_color_ratio > 0.7 and color_std < color_deviation: return True return False 检测一个正常的视频,仍然误检,花屏帧数居高不下,请帮我修改: 视频分析完成: test1.mp4 检测结果: 检测到花屏 花屏帧比例: 99.50% 总帧数: 999 | 花屏帧数: 993
最新发布
10-15
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 import gc 图像参数 IMAGE_WIDTH = 352 IMAGE_HEIGHT = 240 SENSOR_ID = 2 sensor = None THRESHOLD = (77, 206) # 二值化阈值 显示模式设置 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) 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) # 处理图像并检测角点 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)) 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(“程序退出”) 在上述代码的基础上,添加以下功能:识别的矩形中有红色的圆环,识别圆环并大体确定矩形的感兴趣区域ROI,同时标定圆环,让中心识别更加精准
08-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值