C++ 使用pybind11 封装python matplotlib绘图

上面一节《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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343