You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在环面环绕的图像中提取子图像?(OpenCV实现)

环面(Toroid)形式提取子图像的实现方案

嘿,先帮你捋清楚当前代码的问题,再给你搞定环面子图像提取的需求:

首先看你写的代码,有两个明显的问题:

  1. OpenCV里Rect的构造参数是**x, y, width, height**,你写的Rect(rows - 1, cols - 1, 51, 51)把行(对应y轴)和列(对应x轴)搞反了,应该是Rect(cols - 1, rows - 1, 51, 51)才对。
  2. 即使修正了坐标顺序,当你的ROI超出原图像边界时,直接用image(roi)截取会报错或者得到错误结果——因为普通的ROI截取不支持循环边界,而这正是你要的环面效果的核心:超出图像的部分从对侧补全。

OpenCV没有直接内置环面提取子图像的函数,但有两种简单的实现方式,推荐第一种更高效的:

方法一:用copyMakeBorder实现(推荐,简洁高效)

利用OpenCV的copyMakeBorder函数,它支持BORDER_WRAP这种边框填充模式,正好对应环面的循环效果——填充的部分直接取图像对侧的像素。步骤很简单:先给原图像做循环边界填充,再在填充后的图像上截取ROI。

代码如下:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char **argv) {
    Mat image = imread("image.jpg", IMREAD_GRAYSCALE);
    if (image.empty()) {
        cerr << "没法读取图像哦,检查下文件路径!" << endl;
        return -1;
    }

    // 定义你要提取的子图像尺寸
    const int roiWidth = 51;
    const int roiHeight = 51;
    // 修正后的ROI起始坐标(x是列方向,y是行方向)
    const int roiX = image.cols - 1;
    const int roiY = image.rows - 1;

    // 计算需要填充的边界大小:如果ROI超出原图像,就补对应的边界
    int top = max(0, -roiY);
    int bottom = max(0, roiY + roiHeight - image.rows);
    int left = max(0, -roiX);
    int right = max(0, roiX + roiWidth - image.cols);

    // 创建循环填充后的图像,BORDER_WRAP就是环面模式
    Mat wrappedImage;
    copyMakeBorder(image, wrappedImage, top, bottom, left, right, BORDER_WRAP);

    // 在填充后的图像上截取正确的ROI
    Rect roi(roiX + left, roiY + top, roiWidth, roiHeight);
    Mat subImage = wrappedImage(roi);

    namedWindow("Toroid Subimage", WINDOW_AUTOSIZE);
    imshow("Toroid Subimage", subImage);
    waitKey(0);
    return 0;
}

方法二:逐像素循环映射(适合理解原理)

如果想搞清楚底层逻辑,可以手动遍历子图像的每个像素,把坐标做循环映射(处理负数的情况,因为C++里负数取模结果是负数,要调整成正数):

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

// 自定义环面子图像提取函数
Mat getToroidSubimage(const Mat& src, int roiX, int roiY, int roiWidth, int roiHeight) {
    Mat subImage(roiHeight, roiWidth, src.type());
    const int srcCols = src.cols;
    const int srcRows = src.rows;

    for (int y = 0; y < roiHeight; ++y) {
        // 计算原图像的y坐标:循环映射,处理负数
        int srcY = (roiY + y) % srcRows;
        if (srcY < 0) srcY += srcRows;
        uchar* subRow = subImage.ptr<uchar>(y);
        const uchar* srcRow = src.ptr<uchar>(srcY);

        for (int x = 0; x < roiWidth; ++x) {
            // 计算原图像的x坐标:循环映射,处理负数
            int srcX = (roiX + x) % srcCols;
            if (srcX < 0) srcX += srcCols;
            subRow[x] = srcRow[srcX];
        }
    }
    return subImage;
}

int main(int argc, char **argv) {
    Mat image = imread("image.jpg", IMREAD_GRAYSCALE);
    if (image.empty()) {
        cerr << "没法读取图像哦,检查下文件路径!" << endl;
        return -1;
    }

    const int roiWidth = 51;
    const int roiHeight = 51;
    const int roiX = image.cols - 1;
    const int roiY = image.rows - 1;

    Mat subImage = getToroidSubimage(image, roiX, roiY, roiWidth, roiHeight);

    namedWindow("Toroid Subimage", WINDOW_AUTOSIZE);
    imshow("Toroid Subimage", subImage);
    waitKey(0);
    return 0;
}

额外注意点

  • 废弃的CV_LOAD_IMAGE_GRAYSCALE已经换成了推荐的IMREAD_GRAYSCALE,避免兼容性问题。
  • 一定要加图像读取的判断,防止空图像导致崩溃。
  • 记住OpenCV的坐标规则:cols是x方向的宽度,rows是y方向的高度,Rect的参数顺序是x, y, width, height,别再搞反啦~

内容的提问来源于stack exchange,提问作者user8469759

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:13:41