Opencv图像到阵列问题

时间:2017-12-29 12:14:22

标签: c++ arrays opencv

我在使用opencv尝试将图像转换为数组时遇到问题。转换工作但我似乎在结果数组中有不正确的尺寸:

#include <opencv2/opencv.hpp>

int main()
{
    auto img = cv::imread("test.jpg", CV_LOAD_IMAGE_COLOR);
    std::cout << "img cols: " << img.cols << " img rows: "
        << img.rows << " channels: " << img.channels() << std::endl;

    std::vector<float> array2;
    if (img.isContinuous()) {
        array2.assign((float*)img.ptr(0), (float*)(img.ptr(img.rows - 1)) + img.cols);
        std::cout << array2.size() << "\n";
    }

    return 0;
}

第一个打印行的输出结果为:

img cols:416 img rows:416 channels:3

这是正确的,但是在将数据分配给数组后,尺寸为:518336,它们应为519168(416 * 416 * 3)。

是否有人可能会建议究竟是什么导致结果数组小于预期?

2 个答案:

答案 0 :(得分:1)

您的代码存在一些问题:

  • 首先,cv::imread("test.jpg", CV_LOAD_IMAGE_COLOR);将(成功时)返回cv::Mat数据类型为CV_8UC3,但您将float访问这些元素秒。这意味着您将读取的值将是垃圾,并且您最终还将读取超过像素缓冲区的末尾。

    如果你想要浮动,那么你需要在复制行为之前或期间进行一些转换/转换。

  • 第二个问题在于你计算&#34; end&#34;指针,您似乎忘记了您正在处理多频道cv::Mat。在CV_8UC3矩阵的情况下,每个像素由3个字节表示,因此每行有cols*channels个字节。 (这就是为什么你要用2 * 416个元素做空的原因)

  • 不是真正的问题,而是一个限制 - 您的代码仅适用于连续Mat

我会采用一种不同的方法,并利用OpenCV提供的功能。

选项1

使用cv::Mat::copyTo,因为OutputArray可以包裹std::vector<T>。但是,要使其工作,源Mat需要有1个通道和1个行。我们可以使用cv::Mat::reshape有效地实现这一点,但Mat需要是连续的,因此限制仍然存在。

std::vector<uchar> to_array_v1(cv::Mat3b const& img)
{
    std::vector<uchar> a;
    if (img.isContinuous()) {
        img.reshape(1, 1).copyTo(a);
    }
    return a;
}

选项2

使用我们可以使用MatIteratorscv::Mat::begin获取的cv::Mat::end。迭代器即使在非连续Mat上也能正常工作,但是我们需要它们迭代字节,因此我们需要将reshape矩阵转换为单个通道1。由于我们不会更改行数,因此重塑也适用于非连续Mat

std::vector<uchar> to_array_v2(cv::Mat3b const& img)
{
    cv::Mat1b tmp(img.reshape(1));
    return std::vector<uchar>(tmp.begin(), tmp.end());
}

选项3

Silencer建议的方法,使用记录较差的cv::Mat::datastartcv::Mat::dataend成员。 cv::Mat::locateROI的文档更清楚地说明了这些成员变量的含义:

  

但是,每个子矩阵都包含有助于重建原始矩阵大小和原始矩阵中提取的子矩阵位置的信息(由datastartdataend字段表示)。

这意味着这种方法有两个局限:它需要一个连续的矩阵,并且它不能正确地为子矩阵工作,即使它是连续的。 (具体来说,对于连续的子矩阵,它将返回&#34; parent&#34;矩阵的整个缓冲区)

std::vector<uchar> to_array_v3(cv::Mat3b const& img)
{
    std::vector<uchar> a;
    if (img.isContinuous() && !img.isSubmatrix()) {
        a.assign(img.datastart, img.dataend);
    }
    return a;
}

测试代码

#include <opencv2/opencv.hpp>

#include <iostream>
#include <numeric>
#include <vector>


// Paste implementations from the answer here


cv::Mat3b test_image()
{
    cv::Mat1b m(4, 4);
    std::iota(m.begin(), m.end(), 0);

    cv::Mat3b img;
    cv::merge(std::vector<cv::Mat1b>{ m * 3, m * 3 + 1, m * 3 + 2 }, img);
    return img;
}

void print(cv::Mat3b const& img)
{
    std::cout << "Continuous: " << (img.isContinuous() ? "yes" : "no") << '\n';
    std::cout << "Submatrix: " << (img.isSubmatrix() ? "yes" : "no") << '\n';
    std::cout << img << "\n";
}

void print(std::vector<uchar> const& a)
{
    if (a.empty()) {
        std::cout << "empty";
    } else {
        for (auto n : a) {
            std::cout << int(n) << ' ';
        }
    }
    std::cout << "\n";
}

void test(cv::Mat3b const& img)
{
    print(img);
    print(to_array_v1(img));
    print(to_array_v2(img));
    print(to_array_v3(img));
}

int main()
{
    cv::Mat3b img(test_image());
    test(img);

    cv::Mat3b img2(img(cv::Rect(0, 0, 3, 3)));
    test(img2);

    cv::Mat3b img3(img(cv::Rect(1, 1, 3, 1)));
    test(img3);

    return 0;
}

运行此程序将产生以下输出:

Continuous: yes
Submatrix: no
[  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11;
  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23;
  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35;
  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47]
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 
Continuous: no
Submatrix: yes
[  0,   1,   2,   3,   4,   5,   6,   7,   8;
  12,  13,  14,  15,  16,  17,  18,  19,  20;
  24,  25,  26,  27,  28,  29,  30,  31,  32]
empty
0 1 2 3 4 5 6 7 8 12 13 14 15 16 17 18 19 20 24 25 26 27 28 29 30 31 32 
empty
Continuous: yes
Submatrix: yes
[ 15,  16,  17,  18,  19,  20,  21,  22,  23]
15 16 17 18 19 20 21 22 23 
15 16 17 18 19 20 21 22 23 
empty

答案 1 :(得分:0)

Mat img = imread("test.png");
std::vector<uchar> arr;

// convert Mat of CV_8UC3 to std::vector<uchar> if continuous
if(img.isContinuous()){
    arr.assign(img.datastart, img.dataend);
}