1

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!

HansHirse
  • 18,010
  • 10
  • 38
  • 67
Mr Squid
  • 1,196
  • 16
  • 34

2 Answers2

1

The HDFql SELECT operation in your code snippet contains two issues: 1) you can't specify AS INT there and 2) the dataset image is located in group test. Also, the script to write data into the dataset is never called (i.e. executed).

In addition, several optimizations could be done to the code snippet, namely:

  1. Remove keyword "CONTIGUOUS" since when creating a dataset it is contiguous by default.
  2. Change the data type of the dataset to OPAQUE - I believe you want to store the raw data of the image so this data type is more appropriate.
  3. Register variables in a transient way so that after they are used (by HDFql) they are automatically unregistered - this will alleviate you from doing it.

Here goes the code snippet corrected/refactored:

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

int main() {
    char script[1024];

    HDFql::execute("CREATE AND 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 DATASET test/image AS OPAQUE(%d) VALUES FROM MEMORY %d", size.width*size.height*3, HDFql::variableTransientRegister(img.data));
    HDFql::execute(script);
    HDFql::execute("CLOSE FILE");

    cv::Mat img_loaded = cv::Mat::zeros(cv::Size(size.width, size.height), CV_8UC3);
    sprintf(script, "SELECT FROM /tmp/test.h5 test/image INTO MEMORY %d", HDFql::variableTransientRegister(img_loaded.data));
    HDFql::execute(script);

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

    return 0;
}
SOG
  • 876
  • 6
  • 10
1

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.

Mr Squid
  • 1,196
  • 16
  • 34