简体   繁体   English

OpenCV cv :: Mat to std :: ifstream for base64 encoding

[英]OpenCV cv::Mat to std::ifstream for base64 encoding

To be honest I'm suprised nobody has run into this thus far. 说实话,我很惊讶到目前为止没有人遇到过这种情况。 I'm loading a picture from OpenCV into cv::Mat , which I want to base64 encode before I send it over a socket. 我正在将一张来自OpenCV的图片加载到cv :: Mat中 ,在我通过套接字发送之前,我想要base64编码。

For base64 I am using libb64 as it is native to Debian/Ubuntu, and easy to use and very fast. 对于base64,我使用的是libb64,因为它是Debian / Ubuntu原生的,易于使用且速度非常快。 The encoding function takes as a parameter an std::ifstream , and outputs an std::ofstream . 编码函数将std :: ifstream作为参数,并输出std :: ofstream

#include <opencv2/opencv.hpp>
#include <b64/encode.h>
#include <fstream>

using namespace cv;
Mat image;
image = imread( "picture.jpg", CV_LOAD_IMAGE_COLOR );

if ( image.data )
{
    std::ifstream instream( ???, std::ios_base::in | std::ios_base::binary);
    std::ofstream outstream;        

    // Convert Matrix to ifstream
    // ...

    base64::encoder E;
    E.encode( instream, outstream );

    // Now put it in a string, and send it over a socket...
}

I don't really know how to populate the instream from the cv::Mat. 我真的不知道如何填充cv :: Mat的内流。 Googling around, I found that I can iterate a cv::Mat, by columns and rows, and get each (pixel I am assuming) RGB values: 谷歌搜索,我发现我可以通过列和行迭代cv :: Mat,并得到每个(我假设的像素)RGB值:

for ( int j = 0; j < image.rows; j++ )
{
    for ( int i = 0; i < image.cols; i++ )
    {
        unsigned char b = input [ image.step * j + i ] ;
        unsigned char g = input [ image.step * j + i + 1 ];
        unsigned char r = input [ image.step * j + i + 2 ];
    }
}

Is this the right way of going on about it? 这是继续下去的正确方法吗? Is there some more elegant way? 有更优雅的方式吗?

In order to be able to send an image via HTTP, you also need to encode its width, height and type. 为了能够通过HTTP发送图像,您还需要对其宽度,高度和类型进行编码。 You need to serialize the Mat into a stream and encode that stream with libb64. 您需要将Mat序列化为流并使用libb64对该流进行编码。 On the other side you need to decode that stream and deserialize the image to retrieve it. 另一方面,您需要解码该流并反序列化图像以检索它。

I implemented a small test program that does this serialization and deserialization using std::stringstream as a buffer. 我实现了一个小的测试程序,它使用std::stringstream作为缓冲区进行序列化和反序列化。 I chose it because it extends both std::istream and std::ostream which libb64 uses. 我选择它是因为它扩展了libb64使用的std::istreamstd::ostream

The serialize function serializes a cv::Mat into a std::stringstream . serialize函数将cv::Mat序列化为std::stringstream In it, I write the image width, height, type, size of the buffer and the buffer itself. 在其中,我写了图像的宽度,高度,类型,缓冲区的大小和缓冲区本身。

The deserialize function does the reverse. deserialize函数反过来。 It reads the width, height, type, size of the buffer and the buffer. 它读取缓冲区的宽度,高度,类型,大小和缓冲区。 It's not as efficient as it could be because it needs to allocate a temporary buffer to read the data from the stringstream. 它没有那么高效,因为它需要分配一个临时缓冲区来从字符串流中读取数据。 Also, it needs to clone the image so that it does not rely on the temporary buffer and it will handle its own memory allocation. 此外,它需要克隆图像,以便它不依赖于临时缓冲区,它将处理自己的内存分配。 I'm sure that with some tinkering it can be made more efficient. 我敢肯定,通过一些修补,它可以提高效率。

The main function loads an image, serializes it, encodes it using libb64, then decodes it, deserializes it and displays it in a window. main函数加载图像,对其进行序列化,使用libb64对其进行编码,然后对其进行解码,对其进行反序列化并在窗口中显示。 This should simulate what you are trying to do . 这应该模拟你想要做的事情。

// Serialize a cv::Mat to a stringstream
stringstream serialize(Mat input)
{
    // We will need to also serialize the width, height, type and size of the matrix
    int width = input.cols;
    int height = input.rows;
    int type = input.type();
    size_t size = input.total() * input.elemSize();

    // Initialize a stringstream and write the data
    stringstream ss;
    ss.write((char*)(&width), sizeof(int));
    ss.write((char*)(&height), sizeof(int));
    ss.write((char*)(&type), sizeof(int));
    ss.write((char*)(&size), sizeof(size_t));

    // Write the whole image data
    ss.write((char*)input.data, size);

    return ss;
}

// Deserialize a Mat from a stringstream
Mat deserialize(stringstream& input)
{
    // The data we need to deserialize
    int width = 0;
    int height = 0;
    int type = 0;
    size_t size = 0;

    // Read the width, height, type and size of the buffer
    input.read((char*)(&width), sizeof(int));
    input.read((char*)(&height), sizeof(int));
    input.read((char*)(&type), sizeof(int));
    input.read((char*)(&size), sizeof(size_t));

    // Allocate a buffer for the pixels
    char* data = new char[size];
    // Read the pixels from the stringstream
    input.read(data, size);

    // Construct the image (clone it so that it won't need our buffer anymore)
    Mat m = Mat(height, width, type, data).clone();

    // Delete our buffer
    delete[]data;

    // Return the matrix
    return m;
}

void main()
{
    // Read a test image
    Mat input = imread("D:\\test\\test.jpg");

    // Serialize the input image to a stringstream
    stringstream serializedStream = serialize(input);

    // Base64 encode the stringstream
    base64::encoder E;
    stringstream encoded;
    E.encode(serializedStream, encoded);

    // Base64 decode the stringstream
    base64::decoder D;
    stringstream decoded;
    D.decode(encoded, decoded);

    // Deserialize the image from the decoded stringstream
    Mat deserialized = deserialize(decoded);

    // Show the retrieved image
    imshow("Retrieved image", deserialized);
    waitKey(0);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM