当前位置: 首页 > news >正文

python 截取矩形 缩放,旋转

python

import cv2 import numpy as np import time img = cv2.imread(r"C:\Users\ChanJing-01\Pictures\3_35_l.jpg") if img is None: raise ValueError("图片路径错误") clone = img.copy() cx, cy = img.shape[1] // 2, img.shape[0] // 2 w, h = 300, 200 angle = 0 dragging = None start_angle = 0 start_mouse_angle = 0 # ====================== # 工具函数 # ====================== def get_box_points(cx, cy, w, h, angle): rect = ((cx, cy), (w, h), angle) return cv2.boxPoints(rect).astype(np.float32) def order_points(pts): """ 固定顺序:左上、右上、右下、左下 """ pts = np.array(pts, dtype=np.float32) rect = np.zeros((4, 2), dtype=np.float32) s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] # 左上 rect[2] = pts[np.argmax(s)] # 右下 diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] # 右上 rect[3] = pts[np.argmax(diff)] # 左下 return rect def get_rotate_handle(cx, cy, w, h, angle): theta = np.radians(angle) rx = cx + (-np.sin(theta)) * (h / 2 + 40) ry = cy + ( np.cos(theta)) * (h / 2 + 40) return int(rx), int(ry) def warp_crop(): global cx, cy, w, h, angle rect = ((cx, cy), (w, h), angle) box = get_box_points(cx, cy, w, h, angle) box = order_points(box) # 自动真实宽高(避免翻转) widthA = np.linalg.norm(box[2] - box[3]) widthB = np.linalg.norm(box[1] - box[0]) maxW = int(max(widthA, widthB)) heightA = np.linalg.norm(box[1] - box[2]) heightB = np.linalg.norm(box[0] - box[3]) maxH = int(max(heightA, heightB)) dst = np.array([ [0, 0], [maxW, 0], [maxW, maxH], [0, maxH] ], dtype=np.float32) M = cv2.getPerspectiveTransform(box, dst) warped = cv2.warpPerspective(img, M, (maxW, maxH)) return warped def draw(): display = clone.copy() pts = get_box_points(cx, cy, w, h, angle) # 矩形 cv2.polylines(display, [pts.astype(np.int32)], True, (0,255,0), 2) # 角点 for p in pts: cv2.circle(display, tuple(p.astype(int)), 6, (0,0,255), -1) # 中心 cv2.circle(display, (int(cx), int(cy)), 5, (255,0,0), -1) # 旋转手柄 rx, ry = get_rotate_handle(cx, cy, w, h, angle) cv2.circle(display, (rx, ry), 6, (0,255,255), -1) cv2.line(display, (int(cx), int(cy)), (rx, ry), (0,255,255), 1) return display # ====================== # 鼠标事件 # ====================== def mouse(event, x, y, flags, param): global cx, cy, w, h, angle, dragging global start_angle, start_mouse_angle pts = get_box_points(cx, cy, w, h, angle) rx, ry = get_rotate_handle(cx, cy, w, h, angle) if event == cv2.EVENT_LBUTTONDOWN: # 角点 for i, p in enumerate(pts): if np.linalg.norm(np.array([x,y]) - p) < 10: dragging = i return # 中心 if np.linalg.norm(np.array([x,y]) - np.array([cx,cy])) < 10: dragging = 'center' return # 旋转 if np.linalg.norm(np.array([x,y]) - np.array([rx,ry])) < 12: dragging = 'rotate' start_angle = angle start_mouse_angle = np.degrees(np.arctan2(y - cy, x - cx)) return elif event == cv2.EVENT_MOUSEMOVE: if dragging == 'center': cx, cy = x, y elif dragging == 'rotate': current = np.degrees(np.arctan2(y - cy, x - cx)) angle = start_angle + (current - start_mouse_angle) elif isinstance(dragging, int): theta = np.radians(angle) dx = x - cx dy = y - cy local_x = dx * np.cos(theta) + dy * np.sin(theta) local_y = -dx * np.sin(theta) + dy * np.cos(theta) w = max(20, abs(local_x) * 2) h = max(20, abs(local_y) * 2) elif event == cv2.EVENT_LBUTTONUP: dragging = None # ====================== # 初始化 # ====================== cv2.namedWindow("ROI") cv2.setMouseCallback("ROI", mouse) # ====================== # 主循环 # ====================== while True: cv2.imshow("ROI", draw()) key = cv2.waitKey(1) & 0xFF # Enter预览 if key == 13: cv2.imshow("crop_preview", warp_crop()) # S保存 elif key == ord('s'): warped = warp_crop() filename = f"crop_{int(time.time())}.png" cv2.imwrite(filename, warped) print("已保存:", filename) # 重置 elif key == ord('r'): cx, cy = img.shape[1] // 2, img.shape[0] // 2 w, h = 300, 200 angle = 0 # 退出 elif key == 27: break cv2.destroyAllWindows()
http://www.jsqmd.com/news/651299/

相关文章:

  • RFdiffusion酶设计实战:从5an7.pdb到活性口袋生成的保姆级参数解析
  • 从合规审计到渗透测试:安全从业者如何用Lynis这一把‘瑞士军刀’?
  • 3小时从零到火箭专家:OpenRocket免费仿真软件完整指南
  • ESXi 7.x 升级 8.0 失败?两套官方合规解决方案完整教程
  • 优选靠谱企业:高纯气体管道工程安装厂家推荐与口碑对比分析 - 品牌推荐大师1
  • 在红米Note3上部署postmarketOS:从零开始的Linux手机系统移植实践
  • 往priority_queue里塞了100万个定时器,每次插入要走17层堆——时间轮用一次取模就解决了
  • 轻松掌握TranslucentTB:让Windows任务栏焕然一新的实用指南
  • STM32-结构体对齐与内存池实战优化
  • 从零构建本地AI推理引擎:llama-cpp-python实战指南
  • 【AI应用事务可靠性生死线】:97.3%的生成式系统因忽略这4类事务边界而崩溃
  • Transformer位置编码的平替方案:手把手实现Relative Position Representations
  • Rocky Linux 9.4 VMware磁盘扩容实战:从分区调整到文件系统扩展
  • 纸张矫正算法笔记
  • IDEA 的项目 jdk可以切换
  • 简单高效的终极解决方案:3个步骤用猫抓浏览器插件轻松获取网页视频音频资源
  • 使用VS2019将WinForm项目一键打包为exe安装包并配置桌面快捷方式
  • H3C SecPath ACG 1000系列 上网行为管理对接飞书 OAuth2.0 企业认证全流程落地实践
  • AI 热点资讯日报-2026年4月16日
  • 滴滴Tinyid实战:从MySQL到Oracle数据库迁移的完整避坑指南
  • 3个关键步骤:如何让OpenIPC在君正T31ZX平台稳定运行
  • 动态壁纸后台持续耗电的深层原因与优化方案
  • 告别Unchecked Cast警告:Java中Object到List安全转换的5种实战策略
  • 还在用iReport 5.6.0?手把手教你搞定JDK 1.8兼容与中文乱码(附完整Spring Boot集成代码)
  • 4月16日
  • 用NumPy的linalg模块搞定机器学习里的特征值分解:一个PCA降维的实战例子
  • 深入OpenNIC架构:如何利用Alveo FPGA上那两个‘用户Box’玩转自定义数据处理(250MHz vs 322MHz AXI-Stream详解)
  • AI搜索流量突围:成都GEO优化公司选型实用指南(2026版) - 品牌评测官
  • 用TotalSegmentator实现医学影像自动分割:117个解剖结构的一键式解决方案
  • 2025最权威的AI学术网站推荐榜单