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()
相关推荐
Code_Solitude1 分钟前
C语言:指针题型总结
c语言·开发语言
曹牧1 分钟前
Java:`tools.jar`
java·开发语言·jar
我命由我1234514 分钟前
Python 开发 - Python 项目部署(Windows 与 Linux)
java·linux·windows·后端·python·java-ee·python3.11
小白旗15 分钟前
PHP 严重漏洞风险提醒:如何排查运行环境并做好安全更新
开发语言·php
丑梨数据18 分钟前
法律合同知识库怎么搭:从图片压缩到私有化 RAG 的工程笔记
开发语言·笔记·c#
小羊没烦恼!19 分钟前
TypedSql:在 C# 类型系统上实现一个 SQL 查询引擎
java·linux·运维·服务器·开发语言·数据库·mysql
FLJwu22 分钟前
运动相机采购选型避坑实战|第二篇
c语言·开发语言·数码相机·运动相机·oem 量产
Zella折耳根30 分钟前
实验2进阶版数据看板的设计与实现
python
诚丞成35 分钟前
Kavita真正好用的不是“远程阅读”:而是把NAS目录重新组织成可扫描的Library
开发语言·网络
databook38 分钟前
让 LLM 输出不再失控:用 Pydantic 校验大模型输出
人工智能·python·llm