Search code examples
c++opencvhdf5hdfql

HDFql Saving and Loading Images


I am trying to save images from OpenCV to HDF5 using HDFql. Here is a minimal example of what I am trying to achieve (assuming you have an image at /tmp/lena.jpg):

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <HDFql.hpp>

int main() {
    char script[1024];

    HDFql::execute("CREATE FILE /tmp/test.h5");
    HDFql::execute("USE FILE /tmp/test.h5");
    HDFql::execute("CREATE GROUP image");
    cv::Mat img = cv::imread("/tmp/lena.jpg", CV_8UC3);
    cv::Size size(img.size());
    std::cout << "Size = " << size.width << ", " << size.height;

    for(int i=0; i<100; i++)
    {
        // Show a few pixel values - cast is necessary, since values are unsigned char
        std::cout << i << ": " << (int)img.data[i] << std::endl;
    }

    sprintf(script, "CREATE CONTIGUOUS DATASET test/image AS UNSIGNED TINYINT(%d)", size.width*size.height*3);
    HDFql::execute(script);
    sprintf(script, "INSERT INTO test/image VALUES FROM MEMORY %d", HDFql::variableRegister(img.data));
    HDFql::execute("CLOSE FILE");

    HDFql::execute("USE FILE /tmp/test.h5");
    cv::Mat img_loaded = cv::Mat::zeros(cv::Size(size.width, size.height), CV_8UC3);
    HDFql::variableRegister(img_loaded.data);
    HDFql::execute("SELECT AS INT FROM image VALUES INTO MEMORY " + HDFql::variableGetNumber(img_loaded.data));
    HDFql::variableUnregister(img_loaded.data);
    HDFql::execute("CLOSE FILE");

    cv::imshow("loaded image", img_loaded);
    cv::waitKey();

    return 0;
}

This code basically creates a new HDF5 file, then opens an image using OpenCV and saves that image to the HDF5 file. Then it loads that same image from the HDF5 file and displays it.

If you don't have OpenCV, you can always just replace image.data with a uchar array, eg: uchar image_data[10] = {1,2,3,4,5,6,7,8,9,10};

Long story short, this doesn't work and under inspection of the HDF5 file, it already fails during the saving step. Any help on what I am doing wrong will be much appreciated!


Solution

  • This is based on the answer given by @SOG - thanks for that!

    #include <iostream.h>
    #include <HDFql.hpp>
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    int main (int argc, const char * argv[]) {
        char script[1024];
    
        //Create HDF5 file and group "test"
        HDFql::execute("CREATE TRUNCATE FILE /tmp/test.h5");
        HDFql::execute("USE FILE /tmp/test.h5");
        HDFql::execute("CREATE GROUP test");
    
        //==== Load Image, save it to test.h5/test/image ====
        //Choose color or grayscale
        //cv::ImreadModes read_mode = cv::IMREAD_GRAYSCALE;
        cv::ImreadModes read_mode = cv::IMREAD_COLOR;
        cv::Mat img = cv::imread("/tmp/lena.jpg", read_mode);
        cv::Size size(img.size());
        int num_pixels = size.width*size.height*img.channels();
    
        cv::imshow("image", img);
        cv::waitKey();
    
        sprintf(script, "CREATE DATASET test/image AS UNSIGNED TINYINT(%d) "
            "VALUES FROM MEMORY %d", num_pixels,
                HDFql::variableTransientRegister(img.data));
        HDFql::execute(script);
        HDFql::execute("CLOSE FILE");
    
        //==== Load HDF5, and load saved image data to a new cv::Mat image ====
        HDFql::execute("USE FILE /tmp/test.h5");
        cv::Mat img_loaded;
        if(read_mode == cv::IMREAD_COLOR)
        {
            img_loaded = cv::Mat::zeros(size, CV_8UC3);
        } else
        {
            img_loaded = cv::Mat::zeros(size, CV_8UC1);
        }
        std::cout << "size = " << img_loaded.size() << ", type = " << img_loaded.type() 
            << ", channels = " << img_loaded.channels() <<std::endl;
    
        sprintf(script, "SELECT FROM test/image INTO MEMORY %d",
            HDFql::variableTransientRegister(img_loaded.data));
        HDFql::execute(script);
        HDFql::execute("CLOSE FILE");
    
        cv::imshow("loaded image", img_loaded);
        cv::waitKey();
    }
    

    This code works perfectly for both grayscale and color images. There were a few issues still with @SOG's answer, aside from a few minor things, I couldn't get this to work with datatype OPAQUE, so I have gone back to using UNSIGNED TINYINT. I have no idea why that is.

    Other than that, the thorough answer by @SOG pretty much captures the reasons my code wasn't working.