C++ OpenCV:将向量转换< Point>为向量< Point2f>

snz8szmq  于 2023-01-05  发布在  其他
关注(0)|答案(2)|浏览(651)

我通过OpenCV获得了一个vector<vector<Point>>数据。由于某些原因(例如偏移/缩放),我需要将数据Point转换为Point2f。我该怎么做?
例如:

std::vector<std::vector<Point> > contours;
std::vector<std::Vec4i> hierarchy;
cv::findContours(edges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);

std::vector<std::vector<Point2f> > new_contour;

我需要new_contour,它是Point2fvectorvector。有没有简单的方法将它转换为浮点类型?然后我可以通过替换每个Point2f数据来做一些修改(例如偏移/缩放)。
我尝试使用push_back。但仍然得到错误时,建设...

mwyxok5s

mwyxok5s1#

可以使用2个嵌套循环:一个用于外部vector,一个用于内部。在下面的代码中,您可以替换intfloat之间的普通转换,并应用您需要的任何转换。
注意,对于输出轮廓,我使用std::vector::resize分配了外部向量,然后以类似的方式分配了循环中的所有内部vector
或者,您可以使用std::vector::reserve执行所有分配,并使用std::vector::push_back添加元素。

std::vector<std::vector<cv::Point>> contours;

// ... obtain the contours

std::vector<std::vector<cv::Point2f>> contours_float;
contours_float.resize(contours.size());  // allocate the outer vector
for (size_t i = 0; i < contours.size(); ++i)
{
    auto const & cur_contour = contours[i];
    auto & cur_contour_float = contours_float[i];
    cur_contour_float.resize(cur_contour.size()); // allocate the current inner vector
    for (size_t j = 0; j < cur_contour.size(); ++j)
    {
        auto const & cur_point = cur_contour[j];
        // Here you can apply any transformation you need:
        float x = static_cast<float>(cur_point.x);
        float y = static_cast<float>(cur_point.y);
        cur_contour_float[j] = cv::Point2f{ x,y };
    }
}

另一种实现方法是使用std::transform(在这里,我发现使用适当的std::vector构造函数分配向量很方便):

std::vector<std::vector<cv::Point>> contours;

// ... obtain the contours

std::vector<std::vector<cv::Point2f>> contours_float(contours.size());  // allocate the outer vector
std::transform(
        contours.begin(), 
        contours.end(), 
        contours_float.begin(), 
        [](auto const & cur_contour) -> auto
        {
            std::vector<cv::Point2f> cur_contour_float(cur_contour.size());  // allocate the current inner vector
            std::transform(
                    cur_contour.begin(), 
                    cur_contour.end(), 
                    cur_contour_float.begin(),
                    [](auto const & cur_point) -> auto
                    {
                        // Here you can apply any transformation you need:
                        float x = static_cast<float>(cur_point.x);
                        float y = static_cast<float>(cur_point.y);
                        return cv::Point2f{ x,y };
                    });
            return cur_contour_float;
        });

第2版实际上实现了您所需要的确切操作(从一种表示转换为另一种表示)。

whitzsjs

whitzsjs2#

我首先关注你真正的问题,也就是类型转换。假设我们有一些基本的类型别名,以使我们的工作更容易:

using pi_vec = std::vector<cv::Point2i>; // NB: Point is an alias for Point2i
using pf_vec = std::vector<cv::Point2f>;
using pi_vec_vec = std::vector<pi_vec>;
using pf_vec_vec = std::vector<pf_vec>;

为了解决这个问题,我们采用“分而治之”的原则:

  • 为了转换点的矢量的矢量,我们需要能够转换点的单个矢量
  • 为了转换点的矢量,我们需要能够转换单个点

转换单个点

这实际上是微不足道的,因为cv::Point_提供了一个强制转换操作符,允许隐式转换到不同数据类型的点,因此,转换是通过一个简单的赋值来完成的:

cv::Point2i p1 = { 1,2 };
cv::Point2f p2 = p1;

转换点的向量

由于点的隐式转换是可能的,这也同样简单--我们简单地构造新向量,使用迭代器对初始化它:

pi_vec v1 = { {1,2}, {3,4}, {5,6} };
pf_vec v2{ v1.begin(), v1.end() };

转换点矢量的矢量

我们在一个循环中完成上述操作。为了提高性能,我们可以在目标向量中使用reserve空间来避免重新分配。然后,我们使用emplace_back来构造点的向量,使用迭代器对来初始化它们。

pi_vec_vec vv1 = {
    { {1,2}, {3,4}, {5,6} }
    , { {7, 8}, {9,10} }
};

pf_vec_vec vv2;
vv2.reserve(vv1.size());
for (auto const& pts : vv1) {
    vv2.emplace_back(pts.begin(), pts.end());
}

转换期间的附加操作

@wohlstad的回答已经提供了一些可能的方法,然而,查看使用std::transform的第二段代码让我想知道是否有一种方法可以使它变得更简洁,也许可以利用更新的标准(如c++20)提供的特性,下面是我使用std::views::transform的方法。
首先,我们创建一个“范围适配器闭包”来 Package 我们的转换lambda函数,它应用了一些常量缩放和偏移:

auto const tf = std::views::transform(
    [](cv::Point2i const& pt) -> cv::Point2f
    {
        return cv::Point2f(pt) * 0.5 + cv::Point2f{ 1, -1 };
    });

接下来,我们创建一个整数点向量的输入向量的外部转换视图,lambda函数将使用之前创建的“范围适配器闭包”创建一个内部转换视图,并使用此视图构造一个浮点数向量:

auto const tf_view2 = std::views::transform(vv1
    , [&tf](pi_vec const& v) -> pf_vec
    {
        auto const tf_view = v | tf;
        return { tf_view.begin(), tf_view.end() };
    });

最后,我们将构造一个浮点向量的向量,使用这个视图的迭代器对:

pf_vec_vec vv3{ tf_view2.begin(), tf_view2.end()};

示例代码

#include <opencv2/opencv.hpp>

#include <algorithm>
#include <ranges>

template <typename T>
void dump_v(std::vector<T> const& v)
{
    for (auto const& e : v) {
        std::cout << e << ' ';
    }
    std::cout << '\n';
}

template <typename T>
void dump_vv(std::vector<std::vector<T>> const& vv)
{
    for (auto const& v : vv) {
        dump_v(v);
    }
    std::cout << '\n';
}

int main()
{
    using pi_vec = std::vector<cv::Point2i>;
    using pf_vec = std::vector<cv::Point2f>;
    using pi_vec_vec = std::vector<pi_vec>;
    using pf_vec_vec = std::vector<pf_vec>;

    pi_vec_vec vv1 = {
        { {1,2}, {3,4}, {5,6} }
        , { {7, 8}, {9,10} }
    };

    dump_vv(vv1);

    pf_vec_vec vv2;
    vv2.reserve(vv1.size());
    for (auto const& pts : vv1) {
        vv2.emplace_back(pts.begin(), pts.end());
    }

    dump_vv(vv2);

    auto const tf = std::views::transform(
        [](cv::Point2i const& pt) -> cv::Point2f
        {
            return cv::Point2f(pt) * 0.5 + cv::Point2f{ 1, -1 };
        });
    auto const tf_view2 = std::views::transform(vv1
        , [&tf](pi_vec const& v) -> pf_vec
        {
            auto const tf_view = v | tf;
            return { tf_view.begin(), tf_view.end() };
        });
    pf_vec_vec vv3{ tf_view2.begin(), tf_view2.end()};

    dump_vv(vv3);

    return 0;
}

示例输出

[1, 2] [3, 4] [5, 6]
[7, 8] [9, 10]

[1, 2] [3, 4] [5, 6]
[7, 8] [9, 10]

[1.5, 0] [2.5, 1] [3.5, 2]
[4.5, 3] [5.5, 4]

相关问题