前言

这段时间一直在做slam,使用到opencv,因为要把多张图片同时显示在一个窗口上以供对比,在python上很简单用一个plt.imshow()就可以实现了,于是我就想到在c++上也使用matplotlibcpp来实现这个效果,效果如下:

安装matplotlibcpp(ubuntu)

我写这篇文章是2021年七月一号,git上的地址是:https://github.com/lava/matplotlib-cpp ,上面对于这个库的介绍已经挺详尽并且example里面也有许多例程,这里不再赘述。把库下载之后(可以直接下载或者在终端git clone一下):

cd matplotlibcpp-master
mkdir build && cd build
cmake ..
make
sudo make install

前提是安装好了cmake。之后就会提示你安装到了usr/local/include下,你可以到该目录下面看到这个头文件,之后我们就能在代码里面直接引用这个头文件。

 配置cmakelists文件

我习惯用cmake来编译工程,特地去找了配置matplotlibcpp的方法,参考了https://mangoroom.cn/cpp/call-matplotlib-on-cpp.html 当中的方法,我这里用的是python3安装的matplotlib以及numpy,并且我的opencv是4.0的版本,你可以按照你的实际情况对python和opencv的版本进行相应的设置。

cmake_minimum_required(VERSION 2.8)
#设置cmake最低版本
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-std=c++11 -O2 ${SSE_FLAGS} -msse4")
#指定c++编译版本
project(mat)

find_package(Python3 COMPONENTS Development NumPy)
find_package(OpenCV 4 REQUIRED)
#找到必要的库,你可以根据你的实际情况修改成python2或者opencv其他版本
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(main main.cpp)
#这个也是根据你的实际情况进行修改
target_link_libraries(main ${OpenCV_LIBS})
target_include_directories(main PRIVATE ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS})
target_link_libraries(main Python3::Python Python3::NumPy)
#链接库

opencv与matplotlib结合

这个结合可以有多种方式,但是要注意的是opencv默认把图片读进来的通道顺序是BGR,需要在使用matplotlib显示之前把通道转换一下:

cvtColor(img1,img11,CV_BGR2RGB);

用这个方法需要包含头文件:

#include<opencv2/imgproc/imgproc.hpp>

在源代码中可以看到有两种实现方法,但是我在用其中一种方法的时候,发现源代码本身有问题,在这里留下这个问题,如果有熟悉c++的人能给我一些指导,下面是源代码里面对于plt::imshow()的说明:

inline void imshow(const unsigned char *ptr, const int rows, const int columns, const int colors, const std::map<std::string, std::string> &keywords = {}, PyObject** out = nullptr)
{
    detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out);
}

inline void imshow(const float *ptr, const int rows, const int columns, const int colors, const std::map<std::string, std::string> &keywords = {}, PyObject** out = nullptr)
{
    detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out);
}

#ifdef WITH_OPENCV
void imshow(const cv::Mat &image, const std::map<std::string, std::string> &keywords = {})
{
    // Convert underlying type of matrix, if needed
    cv::Mat image2;
    NPY_TYPES npy_type = NPY_UINT8;
    switch (image.type() & CV_MAT_DEPTH_MASK) {
    case CV_8U:
        image2 = image;
        break;
    case CV_32F:
        image2 = image;
        npy_type = NPY_FLOAT;
        break;
    default:
        image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels()));
    }

    // If color image, convert from BGR to RGB
    switch (image2.channels()) {
    case 3:
        cv::cvtColor(image2, image2, CV_BGR2RGB);
        break;
    case 4:
        cv::cvtColor(image2, image2, CV_BGRA2RGBA);
    }

    detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords);
}
#endif // WITH_OPENCV
#endif // WITHOUT_NUMPY

先不管它是怎么实现的,源码中大体上有三种引用方式:传入char类型数组传入float类型数组传入一张Mat图像,但是第三种方法需要在WITH_OPENCV下进行(因为源码中有这样一句话#ifdef WITH_OPENCV ... #endif)

前面两种方法:

前面两种方法其实大同小异,不同的只是传入的数组的类型,所以放在一起说一下,在opencv中,图像的data成员就是这个函数所需要的参数:

    plt::subplot(2,2,1);
    cvtColor(img1,img11,CV_BGR2RGB);
    plt::imshow(img11.data,img11.rows,img11.cols,3);

    plt::subplot(2,2,2);
    cvtColor(img2,img21,CV_BGR2RGB);
    plt::imshow(img21.data,img21.rows,img21.cols,3);

    plt::subplot(2,2,3);
    cvtColor(outimg,outimg1,CV_BGR2RGB);
    plt::imshow(outimg1.data,outimg1.rows,outimg1.cols,3);

    plt::show();

第三种方法:

在用第三种方法的时候,需要在头文件中包含#define WITH_OPENCV,这样你在传参数的时候可以直接传一个Mat图片,但是编译的时候报错居然在matplotlibcpp.h头文件中,这个就很离谱?

/usr/local/include/matplotlibcpp.h:987:96: error: too few arguments to function ‘void matplotlibcpp::detail::imshow(void*, NPY_TYPES, int, int, int, const std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&, PyObject**)’
[build]   987 |     detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords);
[build]       |                                                                                                ^
[build] /usr/local/include/matplotlibcpp.h:918:13: note: declared here
[build]   918 | inline void imshow(void *ptr, const NPY_TYPES type, const int rows, const int columns, const int colors, const std::map<std::string, std::string> &keywords, PyObject** out)
[build]       |             ^~~~~~

大体意思就是说这个头文件里面第987行的引用错了,给的参数太少,我再回去看一下那个头文件:

 可以看到编译器已经给我标红了,说明在这个函数里面调用imshow()的时候参数设置有问题。希望懂得同学能给我指导一下怎么样修改这个头文件。

代码:

//#define WITH_OPENCV
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/legacy/constants_c.h>
#include<opencv2/features2d/features2d.hpp>
#include<./matplotlibcpp.h>
#include<opencv2/imgproc/imgproc.hpp>


using namespace std;
using namespace cv;
namespace plt=matplotlibcpp;

int main(int argc, char **argv){
    if (argc!=3){
        cout<<"two images are needed, but "<<argc-1<<" is given";
        return 1;
    }
    Mat img1=imread(argv[1],CV_LOAD_IMAGE_COLOR);
    Mat img2=imread(argv[2],CV_LOAD_IMAGE_COLOR);
    assert(img1.data!=nullptr && img2.data!=nullptr);
    cout<<"two images has been read"<<endl;

    vector<KeyPoint> keypointImg1,keypointImg2;
    Mat descriptorImg1,descriptorImg2;
    Ptr<FeatureDetector> dectector =ORB::create();
    Ptr<DescriptorExtractor>descriptor=ORB::create();
    Ptr<DescriptorMatcher> matcher=DescriptorMatcher::create("BruteForce-Hamming");

    dectector->detect(img1,keypointImg1);
    dectector->detect(img2,keypointImg2);

    descriptor->compute(img1,keypointImg1,descriptorImg1);
    descriptor->compute(img2,keypointImg2,descriptorImg2);

    Mat outimg;
    drawKeypoints(img1,keypointImg1,outimg,Scalar::all(-1),DrawMatchesFlags::DEFAULT);

    Mat img11;
    Mat img21;
    Mat outimg1;
    plt::subplot(2,2,1);
    cvtColor(img1,img11,CV_BGR2RGB);

    plt::imshow(img11.data,img11.rows,img11.cols,3);
    plt::subplot(2,2,2);
    cvtColor(img2,img21,CV_BGR2RGB);

    plt::imshow(img21.data,img21.rows,img21.cols,3);
    plt::subplot(2,2,3);
    cvtColor(outimg,outimg1,CV_BGR2RGB);

    plt::imshow(outimg1.data,outimg1.rows,outimg1.cols,3);
    plt::show();
    return 0;
}

Logo

更多推荐