图像剪切 OpenCV CPP

发布于 2025-01-09 05:36:29 字数 5371 浏览 0 评论 0 原文

我正在尝试使用 cv::warpAffine 执行图像转换,但有一个问题。

注意:我已经看到 questions/46998895 并且它也工作得很好,但没有插值,我想使用仿射矩阵。

所以我想剪切图像,结果放入更大的图像中,以获得整个剪切图像,如下所示:

photo1

现在,我的想法是如果值为负数,则对倒置图像执行剪切而不是计算新的具有负值的图像大小等,如 paperspace 博客上所示

当我们使用负剪切时,剪切方向是从右到左,而 x2 在负方向上不比 x1 更远。解决这个问题的一种方法可能是获得另一组角(这将满足约束,你能证明它吗?)。应用剪切变换,然后根据我们遵循的符号更改为另一组角。 好吧,我们可以这样做,但还有更好的方法。以下是如何使用剪切因子 -alpha 执行负剪切。

  1. 水平翻转图像和框。
  2. 应用剪切因子 alpha 的正剪切变换
  3. 再次水平翻转图像和框。

左边是我的结果,右边是预期结果:

results

因此,正如您所看到的,当 x 和 y 均为正值或一个为 0 另一个为负值时,它可以正常工作。当一个为正而另一个为负时,或者当它们都是负时,它总是为我的剪切函数提供相同的结果,请参见上图中的最后 4 行。同样,左边的表格是我的结果,右边的表格是mickaShear()结果(显然是正确的结果)。

我的问题是:我必须在剪切矩阵中做什么才能对负值执行正确的转换?是 cv::flip() 方法rel="nofollow noreferrer">paperSpace 实现这一转变的正确方法是什么?

在这里您可以找到可重现的代码以及我的函数与此答案中的函数之间的比较。 您将需要 OpenCV。我在 OpenCV 4.5.1 上工作

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

cv::Mat myShear(const cv::Mat & src, float sx, float sy) {
    cv::Mat tmp;
    cv::Mat dst;

    std::vector<cv::Point2f> extremePoints;
    extremePoints.emplace_back(cv::Point2f(0, 0));
    extremePoints.emplace_back(cv::Point2f((float)src.cols, 0));
    extremePoints.emplace_back(cv::Point2f((float)src.cols, (float)src.rows));
    extremePoints.emplace_back(cv::Point2f(0, (float)src.rows));
    for(auto & pt : extremePoints){
        pt = cv::Point2f(pt.x + pt.y * abs(sx), pt.y + pt.x * abs(sy));
    }
    cv::Rect offsets = cv::boundingRect(extremePoints);
    cv::Size new_size = offsets.size();
    float mat_values[] = {1.0f, abs(sx), 0.0f, abs(sy), 1.0f, 0.0f};
    cv::Mat shear_matrix = cv::Mat(2, 3, CV_32F, mat_values);
    dst = cv::Mat::zeros(new_size, src.type());

    /*
     *cv::flip(img, tmp, INT_FLIP_CODE) where INT_FLIP_CODE can be: 
     * 0   (Vertically) 
     * 1   (Horizontally)
     * -1  (Both)
     */
    if(sx < 0.0f and sy < 0.0f) {
        cv::flip(img, tmp, -1);
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
        cv::flip(dst, dst, -1);
    } else if(sx < 0.0f) {
        cv::flip(img, tmp, 1);
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
        cv::flip(dst, dst, 1);
    } else if(sy < 0.0f) {
        cv::flip(img, tmp, 0);
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
        cv::flip(dst, dst, 0);
    } else {
        tmp = src.clone();
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
    }
    return dst;
}

cv::Mat mickaShear(const cv::Mat & input, float Bx, float By)
{
    if (Bx*By == 1)
    {
        std::cerr << "error == 1" << std::endl;
    }
    if (input.type() != CV_8UC3) return {};

    std::vector<cv::Point2f> extremePoints;
    extremePoints.emplace_back(0, 0);
    extremePoints.emplace_back(input.cols, 0);
    extremePoints.emplace_back(input.cols, input.rows);
    extremePoints.emplace_back(0, input.rows);
    for (auto & pt : extremePoints)
    {
        pt = cv::Point2f(pt.x + pt.y*Bx, pt.y + pt.x*By);
    }
    cv::Rect offsets = cv::boundingRect(extremePoints);
    cv::Point2f offset = -offsets.tl();
    cv::Size resultSize = offsets.size();
    cv::Mat shearedImage = cv::Mat::zeros(resultSize, input.type());

    for (int j = 0; j < shearedImage.rows; ++j){
        for (int i = 0; i < shearedImage.cols; ++i){
            cv::Point2f pp((float)i, (float)j);
            pp = pp - offset; // go back to original coordinate system
            cv::Point2f p;
            p.x = int((-pp.y*Bx + pp.x) / (1 - By*Bx));
            p.y = int(pp.y - p.x*By);
            if ((p.x >= 0 && p.x < (float)input.cols) && (p.y >= 0 && p.y < (float)input.rows)){
                shearedImage.at<cv::Vec3b>(j, i) = input.at<cv::Vec3b>(p);
            }
        }
    }
    return shearedImage;
}

int main(int argc, char *argv[]){
    float x = -0.2f; //CHANGE SIGN TO TEST
    float y = 0.5f; //CHANGE SIGN TO TEST

    cv::Mat im = cv::imread("MODIFY BY JPG FILE PATH");
    cv::Mat result = im.clone();
    cv::Mat output = cv::Mat();
    for(auto & aug: augments){
        output = mickaShear(im, x, y);
        result = myShear(im);
    }
    cv::imshow("result", result);
    cv::imshow("output", output);
    auto k = cv::waitKey(0);
    if(k == (int)'q'){
        cv::destroyAllWindows();
        break;
    }
    cv::destroyAllWindows();
    return 0;
}

I'm trying to use cv::warpAffine to perform image transformations, but I have an issue.

Note: I already saw questions/46998895 and it works well too, but with no interpolation and I wanted to use affine matrixes.

So I want to shear the image and the result goes in a bigger image to have the entire sheared image like this:

photo1

Right now, my idea was to perform the shear on inverted image if the values are negatives rather than calculate the new image size with negative values etc as seen on paperspace blog.

When we use a negative shear, the direction of shear is right to left, while x2 is not further in the negative direction than x1. One way to solve this could be to get the other set of corners (that would satisfy the constraint, can you prove it?). Apply the shear transformation and then change to the other set of corners because of the notation we follow.
Well, we could do that, but there's a better method. Here's how how perform a negative shear with shearing factor -alpha.

  1. Flip the image and boxes horizontally.
  2. Apply the positive shear transformation with shearing factor alpha
  3. Flip the image and boxes horizontally again.

The left ones are my results, and the right ones are the expected results :

results

So as you can see it works well when x and y are both positive values or when one is 0 and the other is negative, its ok. When one is positive and the other is negative, or when they are both negative, it provides always the same result for my shear function, see the 4 last lines in the pic above. Again, the left forms are my results, the right ones are the mickaShear() results (and obviously the correct ones).

My question is: what do I have to do in my shear_matrix to perform the correct transformation with negative values? Is the cv::flip() method described by paperSpace the right way to achieve this transformation?

Here you can find the reproductible code with the comparison between my function and the function in this answer. You will need OpenCV. I work on OpenCV 4.5.1

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

cv::Mat myShear(const cv::Mat & src, float sx, float sy) {
    cv::Mat tmp;
    cv::Mat dst;

    std::vector<cv::Point2f> extremePoints;
    extremePoints.emplace_back(cv::Point2f(0, 0));
    extremePoints.emplace_back(cv::Point2f((float)src.cols, 0));
    extremePoints.emplace_back(cv::Point2f((float)src.cols, (float)src.rows));
    extremePoints.emplace_back(cv::Point2f(0, (float)src.rows));
    for(auto & pt : extremePoints){
        pt = cv::Point2f(pt.x + pt.y * abs(sx), pt.y + pt.x * abs(sy));
    }
    cv::Rect offsets = cv::boundingRect(extremePoints);
    cv::Size new_size = offsets.size();
    float mat_values[] = {1.0f, abs(sx), 0.0f, abs(sy), 1.0f, 0.0f};
    cv::Mat shear_matrix = cv::Mat(2, 3, CV_32F, mat_values);
    dst = cv::Mat::zeros(new_size, src.type());

    /*
     *cv::flip(img, tmp, INT_FLIP_CODE) where INT_FLIP_CODE can be: 
     * 0   (Vertically) 
     * 1   (Horizontally)
     * -1  (Both)
     */
    if(sx < 0.0f and sy < 0.0f) {
        cv::flip(img, tmp, -1);
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
        cv::flip(dst, dst, -1);
    } else if(sx < 0.0f) {
        cv::flip(img, tmp, 1);
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
        cv::flip(dst, dst, 1);
    } else if(sy < 0.0f) {
        cv::flip(img, tmp, 0);
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
        cv::flip(dst, dst, 0);
    } else {
        tmp = src.clone();
        cv::warpAffine(tmp, dst, shear_matrix, new_size, cv::INTER_LINEAR);
    }
    return dst;
}

cv::Mat mickaShear(const cv::Mat & input, float Bx, float By)
{
    if (Bx*By == 1)
    {
        std::cerr << "error == 1" << std::endl;
    }
    if (input.type() != CV_8UC3) return {};

    std::vector<cv::Point2f> extremePoints;
    extremePoints.emplace_back(0, 0);
    extremePoints.emplace_back(input.cols, 0);
    extremePoints.emplace_back(input.cols, input.rows);
    extremePoints.emplace_back(0, input.rows);
    for (auto & pt : extremePoints)
    {
        pt = cv::Point2f(pt.x + pt.y*Bx, pt.y + pt.x*By);
    }
    cv::Rect offsets = cv::boundingRect(extremePoints);
    cv::Point2f offset = -offsets.tl();
    cv::Size resultSize = offsets.size();
    cv::Mat shearedImage = cv::Mat::zeros(resultSize, input.type());

    for (int j = 0; j < shearedImage.rows; ++j){
        for (int i = 0; i < shearedImage.cols; ++i){
            cv::Point2f pp((float)i, (float)j);
            pp = pp - offset; // go back to original coordinate system
            cv::Point2f p;
            p.x = int((-pp.y*Bx + pp.x) / (1 - By*Bx));
            p.y = int(pp.y - p.x*By);
            if ((p.x >= 0 && p.x < (float)input.cols) && (p.y >= 0 && p.y < (float)input.rows)){
                shearedImage.at<cv::Vec3b>(j, i) = input.at<cv::Vec3b>(p);
            }
        }
    }
    return shearedImage;
}

int main(int argc, char *argv[]){
    float x = -0.2f; //CHANGE SIGN TO TEST
    float y = 0.5f; //CHANGE SIGN TO TEST

    cv::Mat im = cv::imread("MODIFY BY JPG FILE PATH");
    cv::Mat result = im.clone();
    cv::Mat output = cv::Mat();
    for(auto & aug: augments){
        output = mickaShear(im, x, y);
        result = myShear(im);
    }
    cv::imshow("result", result);
    cv::imshow("output", output);
    auto k = cv::waitKey(0);
    if(k == (int)'q'){
        cv::destroyAllWindows();
        break;
    }
    cv::destroyAllWindows();
    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文