引言
在图像处理过程中,提取感兴趣区域(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;
}
实现效果: