Trying to find a way to LOG Graphical data in OpenCV/BOOST

460 views Asked by At

To begin with: I am working on Image Processing using OpenCV C++. After loading a Mat image in a C++ program, I plotted a graph of the image using GNUPLOT.

Now, The Requirement is to log the graphical data of the Mat image.

To do this, I created a BOOST C++ Logger by including all BOOST Libraries. BOOST is an excellent library for Testing and logging data as well but, the problem with the it's Log is that it could log only text messages. Correct me if I'm wrong.

Below is my CODE for plotting graph using GNUPlot in OpenCV:

try
{
    Gnuplot g1("lines"); 

    std::vector<double> rowVector;
    std::vector<double> rowVectorExp;

    for (int i = 0; i < 50; i++)  
    {
        rowVector.push_back((double)i); 
        rowVectorExp.push_back((double)exp((float)i/10.0));

    }
    cout << "*** user-defined lists of doubles" << endl;
    g1 << "set term png";
    g1 << "set output \"test.png\"";

    //type of plot pattern
    g1.set_grid().set_style("lines"); 

    g1.plot_xy(rowVector, rowVectorExp, "user-defined points 2d");

    waitKey(0);
}
catch (GnuplotException ge)
{
    cout << ge.what() << endl;
}

cout << endl << "*** end of gnuplot example" << endl;

Here is my BOOST Log CODE:

namespace logging = boost::log;
void PlainGetEdgeVector::init()
{

 logging::add_file_log("sample%3N.log");

}

BOOST_LOG_TRIVIAL(info) << "This is my first Log line";

The good news is, my BOOST Logger successfully logs the text message. It would be great if it could log my graphical data as well.

Any suggestions? If anyone knows how to implement the same using BOOST, I would be very grateful or if there are any alternatives, good to know that as well.

1

There are 1 answers

0
Andrey Semashev On

The solution to your problem greatly depends on the nature of the data how do you want to use the logged data.

1. Re-consider converting binary data to text

For debugging purposes it is often more convenient to convert your binary data to text. Even with large amounts of data this approach can be useful because there are generally many more tools for text processing than for working with arbitrary binary data. For instance, you could compare two logs from different runs of your application with conventional merge/compare tools to see the difference. Text logs are also easier to filter with tools like grep or awk, which are readily available, as opposed to binary data for which you will likely have to write a parser.

There are many ways to convert binary data to text. The most direct approach is to use the dump manipulator, which will efficiently produce textual view of a raw binary data. It suits graphical data as well because it tends to be relatively large in amounts and it is often easy enough to compare in text representation (e.g. when a color sample fits a byte).

std::vector< std::uint8_t > image;
// Outputs hex dump of the image
BOOST_LOG_TRIVIAL(info) << logging::dump(image.data(), image.size());

A more structured way to output binary data is to use other libraries, such as iterator_range from Boost.Range. This can be useful if your graphical data is composed of something more complex than raw bytes.

std::vector< double > image;
// Outputs all elements of the image vector
BOOST_LOG_TRIVIAL(info) << boost::make_iterator_range(image);

You can also write your own manipulator that will format the data the way you want, e.g. split the output by rows.

2. For binary data use attributes and a custom sink backend

If you intend to process the logged data by a more specialized piece of software, like an image viewer or editor, you might want to save the data in binary form. This can be done with Boost.Log, but it will require more effort because the sinks provided by the library are text-oriented and you cannot save a binary data into a text file as is. You will have to write a sink backend that will write binary data in the format you want (e.g. if you plan to use an image editor you might want to write files in the format supported by that editor). There is a tutorial here, which shows the interface you have to implement and a sample implementation. The important bit is the consume function of the backend, which will receive a log record view with your data.

typedef boost::iterator_range< const double* > image_data;
BOOST_LOG_ATTRIBUTE_KEYWORD(a_image, "Image", image_data)

class image_writer_backend :
    public sinks::basic_sink_backend< sinks::synchronized_feeding >
{
public:
    void consume(logging::record_view const& rec)
    {
        // Extract the image data from the log record
        if (auto image = rec[a_image])
        {
            image_data const& im = image.get();

            // Write the image data to a file
        }
    }
};

In order to pass your image binary data to your sink you will need to attach it to the log record as an attribute. There are multiple ways to do that, but assuming you don't intend to filter log records based on the image, the easiest way to do this is to use the add_value manipulator.

std::vector< double > image;
BOOST_LOG_TRIVIAL(info) << logging::add_value(a_image, image) << "Catch my image";

Caveat: In order to avoid copying the potentially large image data, we're passing a lightweight iterator_range as the attribute value. This will only work with synchronous logging because the image vector needs to stay alive while the log record is being processed. For async logging you will have to pass the image by value or use reference counting.

If you do want to apply filters to the image data then you can use scoped attributes or add the attribute to a logger.

Note that by adding your new sink for writing binary data you do not preclude also writing textual logs with other sinks, so that "Catch my image" message can be processed by a text sink. By using other attributes, like log record counters you can associate log records in different files produced by different sinks.