pyscience11 icon indicating copy to clipboard operation
pyscience11 copied to clipboard

How to use plt.imshow() for display cv::Mat images?

Open aj96 opened this issue 7 years ago • 3 comments

I was looking for an easy way to plot subplots of images in C++. But this was the best that I could find. I know that plt.imshow() is not used for subplts. I'd use plt.subplots. But first I'd like to figure out how to use plt.imshow() for displaying images in C++. How would I do this given that images in C++ are of type cv::Mat?

Thank you so much!

aj96 avatar Dec 24 '18 02:12 aj96

Hi,

the easiest way to display C++ matrix using imshow is to convert it into nested python list using pybind11 API.

#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pyscience11/matplotlib.h>
#include <pyscience11/matplotlib/pyplot.h>

namespace py = pybind11;
namespace m11 = matplotlib11;

int main(void)
{
    py::scoped_interpreter interpreter;
    py::list img;
    for (size_t j = 0; j < 100; ++j)
    {
        py::list line;
        for (size_t i = 0; i < 100; ++i)
        {
            line.append(i * j);
        }
        img.append(line);
    }

    // These lines are needed on macOS
    auto matplotlib = m11::import_matplotlib();
    matplotlib.use("TkAgg");

    auto pl = m11::matplotlib::import_pyplot();
    pl.imshow(img);
    pl.show();
    return 0;
}

This yields the following image. figure_1

Also subplotting is available, though we need to use pybind11 API directly because pyscience11 does not currently support class methods.

#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pyscience11/matplotlib.h>
#include <pyscience11/matplotlib/pyplot.h>

namespace py = pybind11;
namespace m11 = matplotlib11;

int main(void)
{
    py::scoped_interpreter interpreter;
    py::list img;
    for (size_t j = 0; j < 100; ++j)
    {
        py::list line;
        for (size_t i = 0; i < 100; ++i)
        {
            line.append(i * j);
        }
        img.append(line);
    }

    // These lines are needed on macOS
    auto matplotlib = m11::import_matplotlib();
    matplotlib.use("TkAgg");

    auto pl = m11::matplotlib::import_pyplot();
    auto sub = pl.subplot(221);
    sub.attr("imshow")(img);
    pl.show();
    return 0;
}

Hope this helps.

yokaze avatar Dec 24 '18 07:12 yokaze

Hello, thank you so much. I just have a few questions.

  1. Why does it use a python list instead of a numpy array? I suppose the two are easily interchangeable, but I'm still confused. I originally tried using your numpy wrapper to create a numpy array through calling numpy.zeros(), passing it to pl.imshow() and it didn't work.

  2. For converting cv::Mat to py::list do I have to use nested loops? That seems pretty slow. Is there no faster way to do it?

Thank you.

aj96 avatar Dec 24 '18 07:12 aj96

Hi,

the above example is written as the easiest example to show images in C++ using matplotlib. If you are concerned with performance, you need to use ndarray instead of python list. Sorry that I haven't used OpenCV, I don't know there is an easy way to do the conversion.

Possibly pybind11:array_t<T> can be used.

I can create a two-dimensional ndarray using the following example, that can be passed to imshow.

auto numpy = n11::import_numpy();
py::list shape;
shape.append(10);
shape.append(10);
auto arr = numpy.zeros(shape);

Please check variables using py::repr to confirm you are creating a matrix with correct shape.

py::print(py::repr(arr));

yokaze avatar Dec 24 '18 08:12 yokaze