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()
相关推荐
mabing99322 分钟前
Qt QMessageBox、QDialogButtonBox中英文翻译动态切换
开发语言·qt
BerryS3N23 分钟前
Java 后端转型大模型:Demo 能跑不等于能上线
java·人工智能·python·java后端·spring ai·langchain4j·大模型转型
Sagittarius_A*24 分钟前
【RCELABS】Level 17~18 —— PHP命令执行函数与环境变量注入
开发语言·安全·web安全·靶场·php·rce
冻柠檬飞冰走茶25 分钟前
PTA基础编程题目集 7-19 支票面额(C语言实现)
c语言·开发语言·数据结构·算法
制造数据与AI践行者老蒋28 分钟前
智联工坊实战:从“金鱼记忆”到“记住了”:给制造Agent装上记忆芯片的完整指南
人工智能·python·langchain·制造
某林21230 分钟前
ros从底层硬件到 Web 端部署
python·机器人·硬件架构·ros2
破碎的南瓜37 分钟前
靶场中使用到的php函数以及每种漏洞的防御方式
开发语言·php
谁看我谁 狼家二丫1 小时前
局域网文件共享实战:从“账户被禁用”到成功互传文件
开发语言·php
FBI HackerHarry浩1 小时前
AI大模型开发V2第四阶段机器学习概述
人工智能·python·机器学习
axinawang1 小时前
第22课 石头剪刀布
python