python 截取矩形 缩放,旋转

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()
相关推荐
Victory_20251 天前
c#定时器顺序控制写法
开发语言·c#·c#顺序控制+定时器
Cyber4K1 天前
【Python专项】Nginx访问日志分析时间范围处理示例
开发语言·python·nginx
中犇科技1 天前
郑州无代码APP开发公司哪家好呢?推荐
开发语言
周末也要写八哥1 天前
代码中的注释的重要性(二)
开发语言·python
XingshiXu1 天前
【NWAFU×KUL】不打扰,也能看懂一头牛:非接触式技术正在改变精准畜牧
人工智能·python·深度学习·目标检测·机器学习·计算机视觉·目标跟踪
kybs19911 天前
springboot租车系统--附源码68701
java·hadoop·spring boot·python·django·asp.net·php
wxin_VXbishe1 天前
springboot新能源车充电站管理系统小程序-计算机毕业设计源码29213
java·c++·spring boot·python·spring·django·php
好运的阿财1 天前
OpenClaw工具拆解之memory_search+memory_get
人工智能·python·ai编程·openclaw·openclaw工具
江南十四行1 天前
Python生成器与协程:从迭代器到异步编程的进阶之路
开发语言·python
Java后端的Ai之路1 天前
大模型数据飞轮核心技术一篇讲透:原理、架构、企业级案例与2026最全实践指南
人工智能·python·架构·数据飞轮