cxnet 发表于 7 天前

C++ Mat图片旋转拼接

在C++中,一张很大的图片直接加载会出错,而分块拼接成大图再做旋转也会出错,所以需要按图片的宽度和100像素高度的块分块加载图片后,将分块旋转75度后依次将分块无缝拼接到新的Mat画布(新的画布尺寸应该为原图旋转后的完整尺寸),拼接完成后对拼接图片做相应处理后再将处理后的图片分块旋转回正常角度(-75)并实现无缝拼接,请在我的代码基础上进行修正,另外,在处理大图时内存会很快占用完总内存,会导致崩溃,如何让程序保留一部份内存用于系统运行(也就是不管处理多大的文件都不让程序把内存占用完)。



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

cv::Mat rotateAndTile(const cv::Mat& src, double angle) {
        // 计算旋转矩阵和旋转后的图像大小
        cv::Point2f src_center(src.cols / 2.0, src.rows / 2.0);
        cv::Mat rot_mat = cv::getRotationMatrix2D(src_center, angle, 1.0);
        cv::Rect bbox = cv::RotatedRect(src_center, src.size(), angle).boundingRect();

        // 调整旋转矩阵以适应新图像的中心
        cv::Mat adjust_rot_mat = rot_mat;
        adjust_rot_mat.at<double>(0, 2) += (bbox.width / 2) - src_center.x;
        adjust_rot_mat.at<double>(1, 2) += (bbox.height / 2) - src_center.y;

        // 初始化目标图像
        cv::Mat dst(bbox.height, bbox.width, src.type(), cv::Scalar::all(255));

        // 旋转并填充到目标图像
        cv::warpAffine(src, dst, adjust_rot_mat, dst.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT);

        return dst;
}


int main() {


        cv::Mat image= cv::imread("bigpicture.tif",cv::IMREAD_UNCHANGED);

        if (image.empty()) {
                return 0;
        }

        cv::Mat dst;

        int block_width = image.cols;
        int block_height = 100;
        int angle = 75;

        cv::Mat result = cv::Mat::zeros(image.rows, image.cols, image.type());


        for (int y = 0; y < image.rows; y+= block_height) {
                for (int x = 0; x < image.cols; x+= block_width) {
                        cv::Rect roi(x, y, std::min(image.cols, image.cols - x), std::min(block_height, image.rows - y));
                        cv::Mat block = image(roi);
                        cv::Mat element = getStructuringElement(MORPH_RECT, Size(2, 2)); // 使用3x3的矩形结构元素

                        cv::Mat processedBlock;
                       
                        cv::dilate(block, processedBlock, element);

                        processedBlock.copyTo(result(roi));
                }
        }

        dst = rotateAndTile(result, angle);

        imwrite("output.tif", dst);

}

FishC 发表于 7 天前

#include <opencv2/opencv.hpp>

cv::Mat rotateAndTile(const cv::Mat& src, double angle) {
    // 计算旋转矩阵和旋转后的图像大小
    cv::Point2f src_center(src.cols / 2.0, src.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(src_center, angle, 1.0);
    cv::Rect bbox = cv::RotatedRect(src_center, src.size(), angle).boundingRect();

    // 调整旋转矩阵以适应新图像的中心
    cv::Mat adjust_rot_mat = rot_mat;
    adjust_rot_mat.at<double>(0, 2) += (bbox.width / 2) - src_center.x;
    adjust_rot_mat.at<double>(1, 2) += (bbox.height / 2) - src_center.y;

    // 初始化目标图像
    cv::Mat dst(bbox.height, bbox.width, src.type(), cv::Scalar::all(255));

    // 旋转并填充到目标图像
    cv::warpAffine(src, dst, adjust_rot_mat, dst.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT);

    return dst;
}

cv::Mat processLargeImage(const cv::Mat& image, int block_height, double angle) {
    int block_width = image.cols;   
    cv::Size resultSize = cv::RotatedRect(cv::Point2f(image.cols/2.0, image.rows/2.0), image.size(), angle).boundingRect().size();
    cv::Mat result = cv::Mat::zeros(resultSize, image.type());

    for (int y = 0; y < image.rows; y += block_height) {
      cv::Rect roi(0, y, image.cols, std::min(block_height, image.rows - y));
      cv::Mat block = image(roi);

      // 旋转块
      cv::Mat rotatedBlock = rotateAndTile(block, angle);

      // 将旋转后的块复制到结果图像。
      cv::Rect rotatedRoi(0, y, rotatedBlock.cols, rotatedBlock.rows);
      rotatedBlock.copyTo(result(rotatedRoi));
    }

    return result;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);

    if (image.empty()) {
      return 0;
    }

    int block_height = 100;
    double angle = 75;

    // 处理大图
    cv::Mat processed = processLargeImage(image, block_height, angle);

    // 保存处理后的图像
    cv::imwrite("output.tif", processed);
}

- **对于内存占用建议**:尽量避免同时在内存中加载处理完整图片。在代码中已通过 `roi` 机制和按行处理分块。除此之外,你还可以:
- 使用 OpenCV 的 `cv::UMat` 这一类来代替 `cv::Mat`,其底层利用了 OpenCL,可以在 GPU 上执行操作。
- 避免做不必要的拷贝。
- 如有可能,考虑对最终的处理结果按块进行处理和保存,这样也可以降低内存的占用。

- **修正**:修正了 `adjustRotMat.at<double>` 中模板类型的问题以确保矩阵操作正确(调整了函数部分)。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 7 天前

FishC 发表于 2024-12-14 10:23
- **对于内存占用建议**:尽量避免同时在内存中加载处理完整图片。在代码中已通过 `roi` 机制和按行处 ...

始终还是会在        cv::warpAffine(src, dst, adjust_rot_mat, dst.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT);
这一行就会报错,内存占满崩溃

FishC 发表于 7 天前

cxnet 发表于 2024-12-14 10:32
始终还是会在        cv::warpAffine(src, dst, adjust_rot_m ...

要处理大图时避免内存占满崩溃的问题,可以逐行(或逐小块)进行旋转和拼接。以下是代码的改进版本,增加了对内存使用的控制:

#include <opencv2/opencv.hpp>

cv::Mat rotateAndTile(const cv::Mat& src, double angle) {
    cv::Point2f src_center(src.cols / 2.0, src.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(src_center, angle, 1.0);
    cv::Rect bbox = cv::RotatedRect(src_center, src.size(), angle).boundingRect();

    cv::Mat adjust_rot_mat = rot_mat;
    adjust_rot_mat.at<double>(0, 2) += (bbox.width / 2) - src_center.x;
    adjust_rot_mat.at<double>(1, 2) += (bbox.height / 2) - src_center.y;

    cv::Mat dst(bbox.height, bbox.width, src.type(), cv::Scalar::all(255));
    cv::warpAffine(src, dst, adjust_rot_mat, dst.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT);

    return dst;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      return 0;
    }

    int block_height = 100;
    double angle = 75;
    cv::Size full_size = cv::RotatedRect(cv::Point2f(image.cols/2.0, image.rows/2.0), image.size(), angle).boundingRect().size();
    cv::Mat result = cv::Mat::zeros(full_size, image.type());

    for (int y = 0; y < image.rows; y += block_height) {
      cv::Rect roi(0, y, image.cols, std::min(block_height, image.rows - y));
      cv::Mat block = image(roi);

      cv::Mat rotatedBlock = rotateAndTile(block, angle);

      cv::Rect targetRoi(0, y * (full_size.height / (double)image.rows), rotatedBlock.cols, rotatedBlock.rows);
      rotatedBlock.copyTo(result(targetRoi));
    }

    cv::imwrite("output.tif", result);
    return 0;
}

**几点建议:**
1. 确保只在内存中一小块一小块地操作图像。
2. 调整 `block_height` 或其他参数以确保 JVM 或其他内存使用限制合理。
3. `cv::BORDER_TRANSPARENT` 设置替换为 `cv::BORDER_CONSTANT` 以避免在无效数据上崩溃。
4. 验证操作系统的内存使用限制,以确保程序有足够的工作空间。

此方法仍然需要注意内存使用,特别是对于非常大的图像,考虑在已旋转部分立即写出,释放已用内存。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 7 天前

FishC 发表于 2024-12-14 10:32
要处理大图时避免内存占满崩溃的问题,可以逐行(或逐小块)进行旋转和拼接。以下是代码的改进版本,增 ...

还是在这一行报错了,因为我的图像尺寸确实有些大,宽度120000像素,高度750000像素左右大的图片
    cv::warpAffine(src, dst, adjust_rot_mat, dst.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT);

FishC 发表于 7 天前

cxnet 发表于 2024-12-14 11:06
还是在这一行报错了,因为我的图像尺寸确实有些大,宽 ...

处理如此大的图片需要特殊的内存管理策略。以下是一种通过逐块处理并避免一次性占用过多内存的方法:

#include <opencv2/opencv.hpp>

cv::Mat rotateBlock(const cv::Mat& block, double angle) {
    cv::Point2f center(block.cols / 2.0, block.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);

    cv::Rect bbox = cv::RotatedRect(center, block.size(), angle).boundingRect();
    cv::Mat adjusted_rot_mat = rot_mat.clone();
    adjusted_rot_mat.at<double>(0, 2) += (bbox.width / 2) - center.x;
    adjusted_rot_mat.at<double>(1, 2) += (bbox.height / 2) - center.y;

    cv::Mat rotatedBlock;
    cv::warpAffine(block, rotatedBlock, adjusted_rot_mat, bbox.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0));

    return rotatedBlock;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      return 0;
    }

    int block_height = 100;
    double angle = 75;

    cv::Size finalSize = cv::RotatedRect(cv::Point2f(image.cols / 2.0, image.rows / 2.0), image.size(), angle).boundingRect().size();
    cv::Mat result(finalSize, image.type());

    for (int y = 0; y < image.rows; y += block_height) {
      cv::Rect roi(0, y, image.cols, std::min(block_height, image.rows - y));
      cv::Mat block = image(roi);

      cv::Mat rotatedBlock = rotateBlock(block, angle);

      cv::Rect targetRoi(0, y * double(finalSize.height) / image.rows, rotatedBlock.cols, rotatedBlock.rows);

      // 处理每一块旋转后的图像
      rotatedBlock.copyTo(result(targetRoi));
    }

    cv::imwrite("output.tif", result);
    return 0;
}

### 说明:
1. **逐块旋转**:图像被分成更小的块处理,降低单次内存占用。
2. **避免大块显式存储**:处理完一块即存至`result`,减少对临时或中间大图像的长期占用。
3. **更好地控制内存**:处理完每小块立即合成到结果,以释放在块中使用的内存。
4. **使用ROI**:使用 `Rect` 和 `ROI` 精确裁剪与布局每个块以控制位置与产出尺寸。
5. **尽量降低分辨率或者分块存储处理**:在空间紧张时,尝试分步保存中间产出的片段。

对于如此超大的图像,操作系统的内存限制是需要留意的,应当尽量采用更高效的方式处理。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 6 天前

FishC 发表于 2024-12-14 11:07
处理如此大的图片需要特殊的内存管理策略。以下是一种通过逐块处理并避免一次性占用过多内存的方法:
...

这样分块旋转后拼接出来的图是不对的,只是把旋转后原图内容进行无缝拼接,并不是把旋转后整个Mat矩阵进行拼接,最后拼接出来的图案必须是和原图一样的内容,只是整体旋转了角度

FishC 发表于 6 天前

cxnet 发表于 2024-12-15 11:43
这样分块旋转后拼接出来的图是不对的,只是把旋转后原 ...

为了确保在旋转后还能正确地拼接大图,可以调整处理逻辑。我们需要先对每个块进行旋转,然后正确地将这些块拼接到一个旋转后的整体图中。以下是一个改进版本:

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

cv::Mat rotateBlock(const cv::Mat& block, double angle) {
    cv::Point2f center(block.cols / 2.0, block.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);
    cv::Rect bbox = cv::RotatedRect(center, block.size(), angle).boundingRect();
    cv::Mat adjusted_rot_mat = rot_mat.clone();
    adjusted_rot_mat.at<double>(0, 2) += (bbox.width / 2) - center.x;
    adjusted_rot_mat.at<double>(1, 2) += (bbox.height / 2) - center.y;
    cv::Mat rotatedBlock;
    cv::warpAffine(block, rotatedBlock, adjusted_rot_mat, bbox.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0));
    return rotatedBlock;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      std::cerr << "Image not loaded!" << std::endl;
      return 0;
    }

    int block_height = 100;
    double angle = 75;

    // 计算旋转后图像的总大小
    cv::Size finalSize = cv::RotatedRect(cv::Point2f(image.cols / 2.0, image.rows / 2.0), image.size(), angle).boundingRect().size();
    cv::Mat rotationResult(finalSize, image.type(), cv::Scalar::all(0)); // 初始化为零

    for (int y = 0; y < image.rows; y += block_height) {
      int currentBlockHeight = std::min(block_height, image.rows - y);
      cv::Rect roi(0, y, image.cols, currentBlockHeight);
      cv::Mat block = image(roi);

      // 旋转块
      cv::Mat rotatedBlock = rotateBlock(block, angle);

      cv::Point2f blockCenter((float)block.cols / 2, (float)y + currentBlockHeight / 2);
      cv::Rect rotatedBlockRect = cv::RotatedRect(blockCenter, block.size(), angle).boundingRect();

      cv::Rect targetRoi(rotatedBlockRect.x, rotatedBlockRect.y, rotatedBlock.cols, rotatedBlock.rows);
      rotatedBlock.copyTo(rotationResult(targetRoi));
    }

    cv::imwrite("output.tif", rotationResult);
    return 0;
}

**说明:**
1. **旋转中心计算**:旋转每个小块时,计算它在整体图中的旋转中心位置。
2. **旋转矩阵**:分块旋转后重新计算新的相对位置并用 `warpAffine` 将其放置在目标位置。
3. **拼接位置**:将每个旋转后的小块放到相对于整个旋转图的正确位置。

这种策略将确保图件在拼接整体后的图案与原图一致,只是旋转了角度。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 6 天前

FishC 发表于 2024-12-15 11:56
为了确保在旋转后还能正确地拼接大图,可以调整处理逻辑。我们需要先对每个块进行旋转,然后正确地将这 ...

报错mat.inl.hpp
    return Mat(*this, roi);

FishC 发表于 6 天前

cxnet 发表于 2024-12-15 12:18
报错mat.inl.hpp
    return Mat(*this, roi);

出现 `Mat(*this, roi)` 错误通常是由于尝试从一个空或不合法的矩阵创建子矩阵(ROI)的时候引发的。该问题可能发生在访问外部区域时造成的,有可能是对 `cv::Rect` 创建时不合法的数值操作引发。

考虑到大图处理和操作系统内存限制,你可以按以下方式确保逻辑和内存安全:

#include <opencv2/opencv.hpp>

cv::Mat rotateBlock(const cv::Mat& block, double angle) {
    cv::Point2f center(block.cols / 2.0, block.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);
    cv::Rect bbox = cv::RotatedRect(center, block.size(), angle).boundingRect();
    cv::Mat adjusted_rot_mat = rot_mat.clone();
    adjusted_rot_mat.at<double>(0, 2) += (bbox.width / 2) - center.x;
    adjusted_rot_mat.at<double>(1, 2) += (bbox.height / 2) - center.y;

    cv::Mat rotatedBlock;
    cv::warpAffine(block, rotatedBlock, adjusted_rot_mat, bbox.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0));
    return rotatedBlock;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      std::cerr << "Error: Could not load image." << std::endl;
      return 1;
    }

    int block_height = 100;
    double angle = 75;

    cv::Size finalSize = cv::RotatedRect(cv::Point2f(image.cols / 2.0, image.rows / 2.0), image.size(), angle).boundingRect().size();
    cv::Mat result(finalSize, image.type(), cv::Scalar(0));

    for (int y = 0; y < image.rows; y += block_height) {
      cv::Rect roi(0, y, image.cols, std::min(block_height, image.rows - y));
      cv::Mat block = image(roi);

      cv::Mat rotatedBlock = rotateBlock(block, angle);

      int newY = static_cast<int>(y * static_cast<double>(finalSize.height) / image.rows);
      cv::Rect targetRoi(0, newY, rotatedBlock.cols, rotatedBlock.rows);

      if (newY + rotatedBlock.rows <= finalSize.height) {
            rotatedBlock.copyTo(result(targetRoi));
      } else {
            cv::Mat validRegion = result(cv::Rect(0, newY, rotatedBlock.cols, finalSize.height - newY));
            rotatedBlock(cv::Rect(0, 0, rotatedBlock.cols, validRegion.rows)).copyTo(validRegion);
      }
    }

    cv::imwrite("output.tif", result);
    return 0;
}

### 说明:
1. **检查边缘条件**:确保在执行 `copyTo` 时合理限制矩阵的边界。
2. **预检查和裁剪操作**:通过检查 `targetRoi` 来确保合法性。
3. **详细错误输出**:在 `image.empty()` 时给出详细原因提示。

注意尝试小步骤验证逐步减小复杂度,确保对块的细致管理。若对 OpenCV 类型和参数(如 `Rect`)不熟悉,检查 API 文档以了解布尔操作及矩阵处理的细节。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 6 天前

FishC 发表于 2024-12-15 12:18
出现 `Mat(*this, roi)` 错误通常是由于尝试从一个空或不合法的矩阵创建子矩阵(ROI)的时候引发的。该 ...

拼接出来的图像和原图不一样,拼接出来后成了分块成阶梯状排列了,需要拼接后和原图一样,只是将原图整体旋转了角度

FishC 发表于 6 天前

cxnet 发表于 2024-12-15 12:36
拼接出来的图像和原图不一样,拼接出来后成了分块成阶 ...

理解到你需要的是将整张大图旋转之后,依然保持其内容和位置的正确,只是旋转角度不同。通过块状操作,再将旋转后的块拼接到新的画布上时,需要精准地计算出旋转后的坐标来进行无缝拼接。以下是一个改进的办法:

#include <opencv2/opencv.hpp>

cv::Mat rotateImage(const cv::Mat& src, double angle, cv::Size& newSize) {
    cv::Point2f center(src.cols / 2.0, src.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);

    // 计算旋转后的图像边界尺寸
    cv::Rect bbox = cv::RotatedRect(center, src.size(), angle).boundingRect();
    newSize = bbox.size();

    cv::Mat rotated;
    cv::warpAffine(src, rotated, rot_mat, bbox.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0));
    return rotated;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      std::cerr << "Failed to load image!" << std::endl;
      return -1;
    }

    int block_height = 100;
    double angle = 75.0;

    cv::Size newSize;
    cv::Mat fullRotated = rotateImage(image, angle, newSize);

    // 原图块处理
    for (int y = 0; y < image.rows; y += block_height) {
      int height = std::min(block_height, image.rows - y);
      cv::Rect roi(0, y, image.cols, height);
      cv::Mat block = image(roi);

      // 计算旋转后位置
      cv::Mat rotatedBlock = rotateImage(block, angle, newSize);

      // 根据旋转的整体位置,计算当前块在新图像中的位置
      cv::Rect targetRoi(0, round(y * static_cast<double>(newSize.height) / image.rows),
                           rotatedBlock.cols, rotatedBlock.rows);

      // 将块放置到完整旋转图上
      rotatedBlock.copyTo(fullRotated(targetRoi));
    }

    cv::imwrite("output.tif", fullRotated);
    return 0;
}

### 重要改善:
1. **整体旋转计算**:直接生成旋转后的整图为整体进行处理,并使用块填充到此大图中以便更加精确。
2. **旋转后的对齐**:确保每个块在新的旋转图像中的位置和尺寸被精准地计算,以避免不对齐或者错位。

### 注意事项:
- 应确保目标区域的尺寸不会超出旋转后的图像范围。
- 这种方法是采用旋转整幅图像的逻辑,将变化后的坐标精确映射到目标画布中。

如处理非常大的图,需考虑内存及处理性能瓶颈(硬件资源如内存、GPU可加速)。有必要时,数据分批次开启或采取纹理方案以减小压力。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 6 天前

FishC 发表于 2024-12-15 12:43
理解到你需要的是将整张大图旋转之后,依然保持其内容和位置的正确,只是旋转角度不同。通过块状操作, ...

这次拼接后的图像好像被targetRoi矩形给裁切掉了左上部份,如果把block_height 数值加大,这个被裁切的区域就会在图像中间形成一个直角三角形空白区域,我估计是targetRoi这个矩形是因为没有和图像一样旋转角度的原因造成在旋转后的图像上裁切targetRoi这个大小区域的图案就包括非原图上的白色区像素

FishC 发表于 6 天前

cxnet 发表于 2024-12-15 14:01
这次拼接后的图像好像被targetRoi矩形给裁切掉了左上 ...

要避免旋转时的裁切问题,尤其是确保大图旋转后仍然保持内容完整,可以通过更细致地计算旋转后的图块位置,以及合理使用存储目标图块坐标来实现。

现有的代码方案里,旋转块的计算及放置位置的计算可能没有准确考虑到旋转引起的空白填充(即非原图像像素区域)。以下是一个可能的解决方案:

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

// 旋转整个图像并返回旋转后的矩阵大小
cv::Mat rotateImage(const cv::Mat& src, double angle, cv::Size& newSize) {
    cv::Point2f center(src.cols / 2.0, src.rows / 2.0);
    cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);

    // 计算旋转后的图像边界尺寸
    cv::Rect bbox = cv::RotatedRect(center, src.size(), angle).boundingRect();
    newSize = bbox.size();

    // 调整旋转矩阵以适应新的画布中心
    rot_mat.at<double>(0, 2) += (bbox.width / 2) - center.x;
    rot_mat.at<double>(1, 2) += (bbox.height / 2) - center.y;

    cv::Mat rotated;
    cv::warpAffine(src, rotated, rot_mat, bbox.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0));
    return rotated;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      std::cerr << "Failed to load image!" << std::endl;
      return 0;
    }

    int block_height = 100;
    double angle = 75.0;
    cv::Size finalSize;
    cv::Mat fullRotated = rotateImage(image, angle, finalSize);

    // 创建一个目标画布用于最终拼接旋转后的块
    cv::Mat result(finalSize, image.type(), cv::Scalar::all(0));

    for (int y = 0; y < image.rows; y += block_height) {
      cv::Rect roi(0, y, image.cols, std::min(block_height, image.rows - y));
      cv::Mat block = image(roi);

      cv::Size blockRotatedSize;
      cv::Mat rotatedBlock = rotateImage(block, angle, blockRotatedSize);

      // 计算该块在旋转后整体图像中的位置
      int offsetY = y * finalSize.height / image.rows;
      cv::Rect targetRoi(0, offsetY, blockRotatedSize.width, blockRotatedSize.height);

      // 确保不越界后进行拷贝
      if (targetRoi.x >= 0 && targetRoi.y >= 0 && targetRoi.x + targetRoi.width <= result.cols && targetRoi.y + targetRoi.height <= result.rows) {
            rotatedBlock.copyTo(result(targetRoi));
      }
    }

    cv::imwrite("output.tif", result);
    return 0;
}

### 关键点:
1. **旋转块**:必须为每个旋转的块计算它在全尺寸旋转画布内的正确位置。
2. **准确复制**:确保在复制时不越出目标画布的边界。
3. **无缝拼接**:采用目标画布储存所有块,需要精确考虑每一个块在旋转结果中的相对位置。
4. **调整画布中心**:旋转矩阵需经调整,以考虑旋转后偏移到新图的中心。

尝试小的图块 rotation 以验证整个过程的准确性及完整性,然后适应真实复杂情境进行实践。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 6 天前

FishC 发表于 2024-12-15 12:43
理解到你需要的是将整张大图旋转之后,依然保持其内容和位置的正确,只是旋转角度不同。通过块状操作, ...

拼接后的图像好像被targetRoi矩形给裁切掉了左上部份,如果把block_height 数值加大,这个被裁切的区域就会在图像中间形成一个直角三角形空白区域,我估计是targetRoi这个矩形是因为没有和图像一样旋转角度的原因造成在旋转后的图像上裁切targetRoi这个大小区域的图案就包括非原图上的白色区像素

所以在拼接之前先把targetRoi这个矩形也旋转相同角度后再将图案copyto到目标Mat上

FishC 发表于 6 天前

cxnet 发表于 2024-12-15 14:11
拼接后的图像好像被targetRoi矩形给裁切掉了左上部份 ...

为了处理旋转后拼接过程中出现的裁切问题,我们需要在拼接之前计算每个旋转块的精确位置。由于块在旋转后的位置发生变化,我们得计算每个旋转块在最终图像中的准确位置。以下是修改后的代码,确保在旋转和拼接过程中考虑到位置调整:

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

cv::Mat rotateBlock(const cv::Mat& block, double angle, const cv::Point2f& center) {
    cv::Mat rot_mat = cv::getRotationMatrix2D(center, angle, 1.0);
    cv::Rect bbox = cv::RotatedRect(center, block.size(), angle).boundingRect();

    // 调整旋转矩阵以适应新范围中心
    cv::Mat adjusted_rot_mat = rot_mat.clone();
    adjusted_rot_mat.at<double>(0, 2) += (bbox.width / 2) - center.x;
    adjusted_rot_mat.at<double>(1, 2) += (bbox.height / 2) - center.y;

    cv::Mat rotatedBlock;
    cv::warpAffine(block, rotatedBlock, adjusted_rot_mat, bbox.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0));

    return rotatedBlock;
}

int main() {
    cv::Mat image = cv::imread("bigpicture.tif", cv::IMREAD_UNCHANGED);
    if (image.empty()) {
      std::cerr << "Error reading image!" << std::endl;
      return -1;
    }

    int block_height = 100;
    double angle = 75.0;

    // 获取新的旋转后图像大小
    cv::Size finalSize;
    cv::Mat rotatedImage = rotateBlock(image, angle, cv::Point2f(image.cols / 2.0, image.rows / 2.0));
    finalSize = rotatedImage.size();

    cv::Mat result(finalSize, image.type(), cv::Scalar::all(0));

    for (int y = 0; y < image.rows; y += block_height) {
      cv::Rect roi(0, y, image.cols, std::min(block_height, image.rows - y));
      cv::Mat block = image(roi);

      cv::Mat rotatedBlock = rotateBlock(block, angle, cv::Point2f(block.cols / 2.0, block.rows / 2.0));

      // 计算旋转后图块在目标图像中的新位置
      cv::Point2f new_center = cv::Point2f(roi.x + roi.width / 2.0, roi.y + roi.height / 2.0);

      // 计算旋转后位置
      cv::Point2f rotated_center(cos(angle * CV_PI / 180.0) * (new_center.x - image.cols / 2.0) - sin(angle * CV_PI / 180.0) * (new_center.y - image.rows / 2.0),
                                  sin(angle * CV_PI / 180.0) * (new_center.x - image.cols / 2.0) + cos(angle * CV_PI / 180.0) * (new_center.y - image.rows / 2.0));

      cv::Rect targetRoi(cv::Point(rotated_center.x + finalSize.width / 2.0 - rotatedBlock.cols / 2.0,
                                     rotated_center.y + finalSize.height / 2.0 - rotatedBlock.rows / 2.0),
                           rotatedBlock.size());

      if (targetRoi.x >= 0 && targetRoi.y >= 0 && targetRoi.x + targetRoi.width <= result.cols && targetRoi.y + targetRoi.height <= result.rows) {
            rotatedBlock.copyTo(result(targetRoi));
      }
    }

    cv::imwrite("output.tif", result);

    return 0;
}

### 说明:
1. **旋转矩阵和旋转后的ROI计算**:我们在计算目标位置时,使用旋转矩阵将块的中心计算成旋转后的新位置。
2. **准确地复制到结果图像的区域**:确保旋转后的块被复制到正确的区域,避免任何剪裁不当。
3. **检查边界**:在进行 `copyTo()` 操作前,检查目标 `ROI` 是否在最终图像的界限内。

这个方法通过计算每个块旋转后的真实坐标并精确对齐,避免了由于旋转角度误差或块处理不当而导致的拼接问题。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: C++ Mat图片旋转拼接