Ask Your Question
0

efficiently fill cv::Mat

asked 2019-09-24 15:23:32 -0600

Dear users,

I'm currently investigating the use of the cv::Mat class as a storage basis for a software dedicated to sequences of images.Basically, I have a binary file containing some raw data that I need to access, process and finally display. Up to now, I was using a simple std::vector but I'm currently thinking of replacing this vector by a cv::Mat object to benefit from all the great tools openCV provides. I'm currently facing a performance issue regarding to time needed to read a file and fill the cv::Mat object.

Here are the method I have written to fill the cv::Mat object :

void ImageReader::readData(cv::Mat &mat)
{
    std::ifstream in(m_file, std::ios::in | std::ios::binary);
    in.seekg(10);
    in.read((char*)(mat.data), 2949120*sizeof(std::uint16_t));
    in.close();
}

And here is the one the std::vector

void ImageReader::readDataStd(std::vector<std::uint16_t> &vec)
{
    std::ifstream in(m_file, std::ios::in | std::ios::binary);
    in.seekg(10);
    in.read((char*)(vec.data()), 2949120*sizeof(std::uint16_t));
    in.close();
}

The file I read basically contains one small header then followed by the data (1920 * 1536 pixels, each pixel is a uint16 in this example). Although the pieces of code are basically the same, I do not get the same performance on my machine : the method that fills the cv::Mat is almost twice slower than the one filling the std::vector (8ms versus 4.2ms).

Is this result logical? Do I make something wrong? Why do I get such a difference? Is there any tips that can help me to reduce the filling time?

I'm new to C++, so please be kind if the answer is obvious ;-)

Best regards,

Vincent

edit retag flag offensive close merge delete

1 answer

Sort by ยป oldest newest most voted
0

answered 2019-10-01 14:33:36 -0600

Here is my version of your functions and test code. I get very similar results for the Mat and vector reading using fstream. I do get faster times for the Mat reading using stdio functions fseek, fread

output:

ReadFileMatStdio time = 0.00337 s

ReadFileMatStream time = 0.00546 s

ReadFileVector time = 0.00556 s

string WindowName = "OutputWindow";


void WriteImage(
    const Size& size,
    const int pixelDepthBits,
    const int headerSizeBytes,
    const string& filename
) {
    int pixelType = CV_MAKETYPE(pixelDepthBits/8, 1);
    Mat img(size, pixelType, Scalar(0));
    // draw something to see
    arrowedLine(img, Point(size.width / 4, size.height / 4), Point(size.width / 4 * 3, size.height / 4 * 3), Scalar(0xffff), 5);

    vector<char> header(headerSizeBytes, char(0xFF));

    FILE* fp = fopen(filename.c_str(), "wb");
    fwrite(header.data(), sizeof(char), headerSizeBytes, fp);
    fwrite(img.data, sizeof(char), pixelDepthBits / 8 * img.size().area(), fp);
    fclose(fp);

    //namedWindow(WindowName);
    //imshow(WindowName, img);
}

void ReadFileMatStdio(
    Mat& result,
    const Size& size,
    const int pixelDepthBits,
    const int headerSizeBytes,
    const string& filename
) {
    FILE* fp = fopen(filename.c_str(), "rb");
    fseek(fp, headerSizeBytes, 0);
    fread(result.data, sizeof(char), pixelDepthBits / 8 * result.size().area(), fp);
    fclose(fp);
}

void ReadFileMatStream(
    Mat& result,
    const Size& size,
    const int pixelDepthBits,
    const int headerSizeBytes,
    const string& filename
) {
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    in.seekg(headerSizeBytes);
    in.read((char*)(result.data), size.area() * pixelDepthBits / 8);
    in.close();
}

void ReadFileVector(
    std::vector<std::uint16_t>& vec,
    const Size& size,
    const int pixelDepthBits,
    const int headerSizeBytes,
    const string& filename
    ) {
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    in.seekg(headerSizeBytes);
    in.read((char*)(vec.data()), size.area() * pixelDepthBits/8);
    in.close();
}

int main() {

    Size size(1920, 1536);
    const int pixelDepthBits = 16;
    const int headerSizeBytes = 10;
    const string& filename = "test.bin";

    int pixelType = CV_MAKETYPE(pixelDepthBits / 8, 1);
    Mat img(size, pixelType);

    int64 beg, end;
    double timeSec;

    //WriteImage(size, pixelDepthBits, headerSizeBytes, filename);


    beg = getTickCount();
    ReadFileMatStdio(img, size, pixelDepthBits, headerSizeBytes, filename);
    end = getTickCount();
    timeSec = (end - beg) / getTickFrequency();
    printf("ReadFileMatStdio time = %.5f s\n\n", timeSec);
    /* */

    namedWindow(WindowName);
    imshow(WindowName, img);
    while (waitKey(0) < 0) {
    }

    beg = getTickCount();
    ReadFileMatStream(img, size, pixelDepthBits, headerSizeBytes, filename);
    end = getTickCount();
    timeSec = (end - beg) / getTickFrequency();
    printf("ReadFileMatStream time = %.5f s\n\n", timeSec);

    namedWindow(WindowName);
    imshow(WindowName, img);
    while (waitKey(0) < 0) {
    }

    vector<uint16_t> vec(size.area(), 0);
    beg = getTickCount();
    ReadFileVector(vec, size, pixelDepthBits, headerSizeBytes, filename);
    end = getTickCount();
    timeSec = (end - beg) / getTickFrequency();
    printf("ReadFileVector time = %.5f s\n\n", timeSec);
    img = Mat(size, pixelType, vec.data());
    /* */

    namedWindow(WindowName);
    imshow(WindowName, img);
    while (waitKey(0) < 0) {
    }


    return 0;
edit flag offensive delete link more

Comments

That is strange. You get the same results using cv::Mat or std::vector. This is not what I got, but your result is may be what we should expect. Or it may be machine dependent....

Many thanks for your feedback and I will definitely use the C style approach for fast reading of the file.

Vincent

lesauxvi gravatar imagelesauxvi ( 2019-10-01 15:17:01 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2019-09-24 15:23:32 -0600

Seen: 1,131 times

Last updated: Oct 01 '19