C++ 使用pybind11 封裝python matplotlib繪圖

上面一節(jié)《C++使用matplotlib繪圖》講解了使用三方庫調(diào)用matplotlib庫進行繪圖的方法。
但是因為matplotlibcpp這個庫已經(jīng)很久沒有更新了,所以我們也繼續(xù)研究了一下自己使用pybind11封裝matplotlib python給C++調(diào)用的可能性,結(jié)果是可以的,過程如下。
本例基于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函數(shù),如果你有特殊的需求,也可以封裝其他函數(shù),例如plot函數(shù)等等。套路差不多。
程序輸出如下,


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容