OpenCV图像处理——按最小外接矩形剪切图像

引言

在图像处理过程中,提取感兴趣区域(ROI)并在其上进行处理后,往往需要将处理后的结果映射回原图像。这一步通常涉及以下几个步骤:

找到最小外接矩形:使用 cv::boundingRect 或 cv::minAreaRect 提取感兴趣区域的最小外接矩形。
从原图中提取 ROI:根据矩形坐标从原图中剪切出 ROI 进行处理。
在 ROI 上进行处理:对提取出的 ROI 进行特定的图像处理操作。
将处理后的 ROI 映射回原图:将处理后的结果重新放置回原图的相应位置。

鼠标选取区域

可以使用OpenCV和C++来通过鼠标绘制多边形,并确定闭合点。功能通过捕捉鼠标事件来实现。绘制多边形时,当点击的点接近第一个点时,可以自动将多边形闭合。
在绘制多边形并将其闭合后,通过OpenCV的 minAreaRect 函数来计算该多边形的最小外接矩形。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

vector<Point> points; // 存储鼠标点击的点
bool drawing = false; // 是否正在绘制
const int CLOSE_DISTANCE = 10; // 闭合多边形的最小距离

// 判断当前点是否接近第一个点
bool isCloseToFirstPoint(Point p) {
    if (points.empty()) return false;
    return norm(p - points[0]) < CLOSE_DISTANCE;
}

// 鼠标回调函数
void onMouse(int event, int x, int y, int, void* param) {
    Mat& image = *(Mat*)param;

    if (event == EVENT_LBUTTONDOWN) {
        Point p(x, y);
        if (isCloseToFirstPoint(p) && points.size() > 2) {
            // 如果点接近第一个点并且有至少三个点,则闭合多边形
            points.push_back(points[0]);
            polylines(image, points, true, Scalar(0, 255, 0), 2); // 闭合并绘制多边形

            // 计算最小外接矩形
            RotatedRect minRect = minAreaRect(points);

            // 获取矩形的4个顶点
            Point2f rect_points[4];
            minRect.points(rect_points);

            // 绘制最小外接矩形
            for (int i = 0; i < 4; i++) {
                line(image, rect_points[i], rect_points[(i + 1) % 4], Scalar(255, 0, 0), 2);
            }

            imshow("Image", image); // 显示最终结果
            drawing = false;
        } else {
            // 否则继续添加点
            drawing = true;
            points.push_back(p);
        }
    } 
    else if (event == EVENT_MOUSEMOVE && drawing) {
        Mat tempImage = image.clone(); // 创建临时图像用于显示
        if (!points.empty()) {
            polylines(tempImage, points, false, Scalar(255, 0, 0), 2); // 画多边形
            line(tempImage, points.back(), Point(x, y), Scalar(255, 0, 0), 2); // 绘制最后一条线
        }
        imshow("Image", tempImage);
    }
    else if (event == EVENT_RBUTTONDOWN && !points.empty()) {
        // 右键按下,重置并清除所有点
        points.clear();
        image = Mat::zeros(image.size(), image.type()); // 重置图像
        imshow("Image", image);
        drawing = false;
    }
}

int main() {
    // 创建空白图像
    Mat image = Mat::zeros(Size(800, 600), CV_8UC3);
    
    // 设置鼠标回调
    namedWindow("Image", WINDOW_AUTOSIZE);
    setMouseCallback("Image", onMouse, &image);

    // 显示图像并等待退出
    imshow("Image", image);
    waitKey(0);
    return 0;
}

在这里插入图片描述

按最小外接矩形剪切图像

bool is_rotated_rect(const cv::RotatedRect& rect)
{
	// 检查中心点是否在合理范围内
	if (rect.center.x == 0 && rect.center.y == 0)
	{
		return false;
	}
	// 检查宽度和高度是否为正值
	if (rect.size.width <= 0 || rect.size.height <= 0)
	{
		return false;
	}
	// 如果以上条件都满足,可以认为矩形是有效的
	return true;
}

bool cut_rotate_roi(cv::Mat& cv_src, cv::RotatedRect& minRect, cv::Mat& cv_roi)
{
	if (is_rotated_rect(minRect))
	{
		cv::Mat M = cv::getRotationMatrix2D(minRect.center, minRect.angle, 1.0);
		cv::Mat rotated;
		// 应用仿射变换 warpAffine,使用三次插值(INTER_CUBIC)图像旋转。旋转结果存储在 rotated 中。
		cv::warpAffine(cv_src, rotated, M, cv_src.size(), cv::INTER_CUBIC);

		// 裁剪旋转矩形区域
		cv::getRectSubPix(rotated, minRect.size, minRect.center, cv_roi);

		return true;
	}

	return false;
}

实现效果:
在这里插入图片描述

### 使用 Python 和 OpenCV 实现裁剪图片中对象的最小外包矩形 为了实现这一功能,可以按照如下方法操作: #### 获取轮廓并计算最小外接矩形 首先需要获取图像中的轮廓信息。这通常涉及到二值化处理、边缘检测等预处理步骤。一旦获得了轮廓,则可以通过 `cv2.minAreaRect()` 来获得最小外接矩形。 ```python import numpy as np import cv2 # 假设 'image' 是已经读取好的灰度图或彩色图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 如果是彩色图则转成灰度图 ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) rect = cv2.minAreaRect(contours[0]) # 对第一个轮廓求解最小外接矩形 ``` #### 绘制与验证最小外接矩形 接着将上述得到的最小外接矩形转换为四个角点的形式,并可以在原图上画出来以便确认位置是否正确。 ```python box = cv2.boxPoints(rect) box = np.intp(box) output_image = image.copy() cv2.drawContours(output_image, [box], 0, (0, 255, 0), 2) cv2.imshow('Output Image with Bounding Box', output_image) cv2.waitKey(0) cv2.destroyAllWindows() ``` #### 完成交叉变换以准备裁剪 由于 `minAreaRect` 返回的角度可能不是标准直角坐标系下的角度,因此需要用仿射变换来调整视角到水平垂直方向后再做裁剪。 ```python width = int(rect[1][0]) height = int(rect[1][1]) src_pts = box.astype("float32") dst_pts = np.array([[0, height-1], [0, 0], [width-1, 0], [width-1, height-1]], dtype="float32") M = cv2.getPerspectiveTransform(src_pts, dst_pts) warped = cv2.warpPerspective(image, M, (width, height)) ``` 这样就完成了基于最小外接矩形的对象裁剪过程[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值