美文网首页
C++ 使用pybind11 封装python matplotl

C++ 使用pybind11 封装python matplotl

作者: FredricZhu | 来源:发表于2024-04-09 19:47 被阅读0次

上面一节《C++使用matplotlib绘图》讲解了使用三方库调用matplotlib库进行绘图的方法。
但是因为matplotlibcpp这个库已经很久没有更新了,所以我们也继续研究了一下自己使用pybind11封装matplotlib python给C++调用的可能性,结果是可以的,过程如下。
本例基于conanio/gcc9镜像封装完成。步骤如下,

  1. 在conanio/gcc9容器中安装matplotlib
  pip install matplotlib
  1. conanfile.txt添加如下依赖,主要是pybind11就可以,其他两个库这个工程没有使用到,但也是常用库
[requires]
nlohmann_json/3.11.3
boost/1.72.0
pybind11/2.12.0

[generators]
cmake
  1. CMakeLists.txt
cmake_minimum_required(VERSION 3.3)


project(46_invoke_py2)

set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig/")

set ( CMAKE_CXX_FLAGS "-pthread")
set(CMAKE_CXX_STANDARD 17)
add_definitions(-g)

add_definitions(-DSAVE_PERCEPTION_CONCAT_IMG)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

include_directories(${INCLUDE_DIRS} /opt/pyenv/versions/3.7.13/include/python3.7m/)
LINK_DIRECTORIES(${LINK_DIRS} /opt/pyenv/versions/3.7.13/lib/)

file( GLOB main_file_list ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 
file( GLOB source_files ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)

foreach( main_file ${main_file_list} )
    file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${main_file})
    string(REPLACE ".cpp" "" file ${filename})
    add_executable(${file}  ${main_file} ${source_files})
    target_link_libraries(${file} ${CONAN_LIBS} pthread python3.7m)
endforeach( main_file ${main_file_list})

matplotlib.h

#ifndef _FREDRIC_MATPLOT_LIB_H_
#define _FREDRIC_MATPLOT_LIB_H_

#include <pybind11/embed.h>
#include <map>
#include <set>
#include <vector>
#include <string>

namespace py = pybind11;
using namespace py::literals;

namespace matplotlibc {
    struct matplotlib_t {
        matplotlib_t() {
            py::initialize_interpreter();
            plt = py::module_::import("matplotlib.pyplot");
        }

        ~matplotlib_t() {
            plt.release();
            py::finalize_interpreter();
        }
       
        template<typename NumericX>
        void to_py_list(const std::vector<NumericX>& x, py::list* result_x) {
            for (const auto &elem : x) {
                result_x->append(elem);
            }
        }

        void to_kw_args(std::map<std::string, std::string> const& keywords, std::set<std::string> const& real_keys,
        py::kwargs* kw_args)  {
            for (const auto& pair : keywords) {
                if(real_keys.find(pair.first) != real_keys.end()) {
                    (*kw_args)[py::str(pair.first)] = std::atof(pair.second.data());
                } else {
                    (*kw_args)[py::str(pair.first)] = pair.second;
                }    
            }
        }

        template<typename NumericX, typename NumericY>
        bool scatter(const std::vector<NumericX>& x,
            std::vector<NumericY> const& y,
            double const s=1.0, 
            std::map<std::string, std::string> const& keywords = {}) {
            
            py::list result_x;
            py::list result_y;
            
            to_py_list(x, &result_x);
            to_py_list(y, &result_y);
            
            py::object res;
            if(keywords.size() > 0) {
                py::kwargs kw_args;
                to_kw_args(keywords, {"linewidths"}, &kw_args);
                res = plt.attr("scatter")(result_x, result_y, s, **kw_args);
            } else {
                res = plt.attr("scatter")(result_x, result_y, s);
            }

            return !res.is_none();
        }

        void clf() {
            plt.attr("clf")();
        }

        void save(std::string const& file_name) {
            plt.attr("savefig")(file_name.c_str());
        }

    private:
        py::module_ plt;
    };
}
#endif

main.cpp

#include <iostream>
#include "matplotlib.h"

namespace plot = matplotlibc;

int main(int argc, char* argv[]) {
    plot::matplotlib_t plt;
    std::vector<float> x {1.0f, 2.0f, 3.0f, 4.0f};
    std::vector<float> y {1.0f, 2.0f, 3.0f, 4.0f};
    plt.clf();
    plt.scatter(x, y, 50, {{"color", "green"}, {"linewidths", "20"}});
    plt.save("result.png");
    return EXIT_SUCCESS;
}

本例只是封装了scatter函数,如果你有特殊的需求,也可以封装其他函数,例如plot函数等等。套路差不多。
程序输出如下,


image.png

相关文章

网友评论

      本文标题:C++ 使用pybind11 封装python matplotl

      本文链接:https://www.haomeiwen.com/subject/yswfxjtx.html