matplotlib-cpp是Matplotlib(MPL)为C++提供的一个用于python的matplotlib绘图库的C++包装器。它的构建类似于Matlab和matplotlib使用的绘图API。
However, the function signatures might differ and Matplotlib for C++ does not support the full functionality of MPL. The purpose is providing an easy-to-use wrapper to MPL in C++, not to fully translate the library. 然而,函数签名可能不同,C++的Matplotlib不支持MPL的全部功能。其目的是为C++中的MPL提供一个易于使用的包装器,而不是完全翻译库。
库下载和环境要求
下载matplotlib-cpp库:
git clone https://github.com/lava/matplotlib-cpp.git
matplotlibcpp库的结构比较简单,其目录结构如下:
.
├── cmake
├── CMakeLists.txt //cmake文件
├── contrib
├── examples
├── LICENSE
├── LICENSE.matplotlib
├── matplotlibcpp.h //头文件
└── README.md
其中最核心的就是matplotlib.h
,该头文件封装了大量C++调用matplotlib的API,在实际使用的时候,只需要将其复制到项目的include就可以。头matplotlibcpp.h
取决于Python头Python.h
、相应的Python库libpython
和numpy/arrayobject.h
。如果不在标准include路径中,则必须分别使用选项-I
、-L
和-l
为编译器指定头文件的路径、库的路径和库本身。
matplotlib-cpp通过包装器调用python的matplotlib来工作。因此使用matplotlib-cpp必须有python环境、matplotlib
和numpy/arrayobject.h
。目前Python2.7和Python3(>=3.6)已经过测试,如果没有可以使用:,
sudo apt-get install python-matplotlib python-numpy python2.7-dev
//or sudo apt-get install python-matplotlib python-numpy python3-dev
By default Matplotlib for C++ uses Numpy arrays. This requires the above header file. However it is possible to avoid this header by defining -DWITHOUT_NUMPY
. 默认情况下,C++的Matplotlib使用Numpy数组。这需要上面的头文件。但是,可以通过定义-DWITHOUT_NUMPY来避免此标头。
目前C++代码与python2和python3都兼容。但是,CMakeLists.txt
文件当前默认设置为使用python3,因此如果需要python2,则必须手动更改。
By design (of python), only a single python interpreter can be created per process. When using this library, no other library that is spawning a python interpreter internally can be used. 根据(python的)设计,每个进程只能创建一个python解释器。当使用这个库时,不能使用其他在内部生成python解释器的库。
如果不使用cmake,我们也可以使用最基本的g++编译器编译,调用示例如下:
g++ example.cpp -I/usr/include/python2.7 -lpython2.7
常用的函数和方法
matplotlib-cpp的所有函数都组织在名称空间matplotlibcpp
中。通常情况下,为了方便(并本着Python规范的精神),我们通常定义缩写plt,即:
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
后面的介绍默认使用了namespace plt = matplotlibcpp;
,即用缩写代替matplotlibcpp
plot相关
绘制曲线最常用的就是plot函数,其原型如下
//绘制x,y
template<typename VectorX, typename VectorY>
bool plot(const VectorX &x, const VectorY &y, const std::string &s = "", const std::map<std::string, std::string> &keywords = {})
//绘制y
//对于大小为n的向量y,x数据被设置为0,。。。,n−1
template<typename VectorY>
bool plot(const VectorY &y, const std::string &format = "", const std::map<std::string, std::string> &keywords = {})
该函数用来绘制y与x的关系图。两个向量x 并且y必须具有相同的长度。格式化字符串s可以指定线条的颜色、标记和样式。map关键字可能包含绘图的其他命名参数。
示例:
#include <vector>
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main() {
std::vector<double> x = {1, 2, 3, 4};
std::vector<double> y = {1, 4, 9, 16};
plt::plot(x, y);
//plt::plot(x, y, "r*"); // 红色的*作标记,没有连线
//plt::plot(x, y, "bo-"); // 蓝色的点+蓝色的线
//plt::plot(x, y, "bo-", {{"label", "f(x)"}}); // 添加f(x)标签
//plt::plot(x, y, {{"label", "$y = x^2$"}}); // 支持latex
//plt::legend(); // 激活图例
plt::show();
return 0;
}
运行结果:
另外matplotlib-cpp还提供了对数为刻度的坐标系函数,其用法与plot类似,它们对应的函数原型如下:
//以双对数刻度绘制y与x的关系图
template<typename VectorX, typename VectorY>
bool loglog(const VectorX &x, const VectorY &y, const std::string &s = "",
const std::map<std::string, std::string> &keywords = {})
//用对数x和线性y标度绘制y与x的关系图
template<typename VectorX, typename VectorY>
bool semilogx(const VectorX &x, const VectorY &y, const std::string &s = "", const std::map<std::string, std::string> &keywords = {})
//用对数x和线性y标度绘制y
template<typename VectorY>
bool semilogx(const VectorY &y, const std::string &s = "", const std::map<std::string, std::string> &keywords = {})
//以线性x和对数y标度绘制y与x的关系图
template<typename VectorX, typename VectorY>
bool semilogy(const VectorX &x, const VectorY &y, const std::string &s = "", const std::map<std::string, std::string> &keywords = {})
//以线性x和对数y标度绘制y
template<typename VectorY>
bool semilogy(const VectorY &y, const std::string &s = "", const std::map<std::string, std::string> &keywords = {})
除了对数坐标系外,matplotlib-cpp还提供了文本显示功能,其函数原型如下:
template<typename Numeric>
void text(Numeric x, Numeric y, const std::string &s = "")
x
、y
分别代表文本的位置坐标,s
为文本内容
示例:
#include <vector>
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main() {
std::vector<double> x = {0.1, 0.2, 0.5};
plt::plot(x, "s");
plt::text(1.0, 0.1, "Text under a square");
plt::show();
return 0;
}
输出结果:
figure相关
使用一个ID号初始化一个新图形。
//number: 图形的编号。如果设置为-1,则使用默认编号(从0开始递增)
inline long figure(long number = -1)
设置figure的宽度和高度
//w: 图形的宽度(以像素为单位)
//h: 图形的高度(以像素为单位)
void figure_size(size_t w, size_t h)
启动图例
/*
loc: 图例的位置。可以是以下任意一种:“best”, “upper left”, “upper center”,
“upper left”, “center left”, “center”,
“center right” (= “right”), “lower left”,
“lower center”, “lower right”
bbox_to_anchor: 如果设置为长度为2或4的矢量,则指定图例边界框的位置(和大小)。
格式为(x,y)或 (x,y,宽度,高度)。
坐标以与绘图轴相同的单位进行解释(因此没有归一化坐标)
*/
template<typename Vector = std::vector<double>>
inline void legend(const std::string &loc = "best", const Vector &bbox_to_anchor = Vector())
示例:
//将图例放在右下象限的中心。
//第一个参数:loc,第二个:bbox_to_anchor
plt::legend("center", {0.5, 0, 0.5, 0.5});
设置x轴范围。
//left: 左轴限制
//right: 右轴限制
template<typename Numeric>
void xlim(Numeric left, Numeric right)
设置y轴范围。
//bottom: 底部轴限制
//top: 顶部轴限制
template<typename Numeric>
void ylim(Numeric bottom, Numeric top)
获取x、y轴范围。
//返回值:指向长度为2的数组的指针,该数组包含[left,right]
inline double *xlim()
//返回值:指向长度为2的数组的指针,该数组包含[bottom,top]
inline double *ylim()
设置figure的标题。
//titlestr: 情节的标题
//keywords: 其他关键字
inline void title(const std::string &titlestr, const std::map<std::string, std::string> &keywords = {})
将居中的标题添加到figure中。
//suptitle str: 图形的标题
//keywords: 其他关键字
inline void suptitle(const std::string &suptitlestr, const std::map<std::string, std::string> &keywords = {})
设置坐标轴的一些属性。
/*
option: 要激活的选项
其支持的选项有:
on-------启用轴线和标签
off------关闭轴线和标签
equal----通过更改轴限制来设置相等的缩放比例。
scaled---通过更改绘图框的尺寸来设置相等的缩放比例。
tight----设置足够大的限制以显示所有数据。
auto-----自动缩放(用数据填充绘图框)。
image----以等于数据限制的轴限制进行缩放。
square---方形地块;类似于缩放,但最初强制相同的x轴和y轴长度。
*/
inline void axis(const std::string &option)
保存当前图形。
/*
filename: 将图形保存为文件名(必须包含文件格式),支持的文件类型取决于用户后端,但通常包含 pdf、eps和png等。
keywords: 其他关键字
*/
inline void savefig(const std::string &filename, const std::map<std::string, std::string> &keywords = {})
其会始终存储图形的当前状态。例如:
plt::plot(time, apple_sales);
plt::savefig("sales.pdf"); // 仅包含apple_sales
plt::plot(time, kiwi_sales);
plt::savefig("sales.pdf"); // 包含apple 和 kiwi sales
我们可以使用调用plt::show()
清除绘图,例如:
plt::plot(x, y);
plt::show();
plt::savefig("is_this_empty.pdf"); // 是的,这个是空的
plt::plot(x, y);
plt::savefig("this_isnt_empty.pdf"); // 建议始终在show之前调用savefig
plt::show();
有时候如果轴标签在外面太远在保存图片的时候可能会被切断,我们可以尝试使用:
//将图片以恰当的匹配形式保存
plt::savefig("fig.pdf", {{"bbox_inches", "tight"}});
显示图形。
/*
block: 如果为true,则停止执行代码,直到关闭显示的图形为止。
否则,代码不会停止。根据后端的不同,数字可能根本无法显示。
*/
inline void show(const bool block = true)
其他示例
matplotlibcpp还提供了一些其他示例,我们可以根据需要,在其上面做相应的调整供自己使用。
绘制三维图像
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main()
{
std::vector<std::vector<double>> x, y, z;
for (double i = -5; i <= 5; i += 0.25) {
std::vector<double> x_row, y_row, z_row;
for (double j = -5; j <= 5; j += 0.25) {
x_row.push_back(i);
y_row.push_back(j);
z_row.push_back(::std::sin(::std::hypot(i, j)));
}
x.push_back(x_row);
y.push_back(y_row);
z.push_back(z_row);
}
plt::plot_surface(x, y, z);
plt::show();
return 0;
}
输出效果:
绘制动图:
#define _USE_MATH_DEFINES
#include <cmath>
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main()
{
int n = 1000;
std::vector<double> x, y, z;
for(int i=0; i<n; i++) {
x.push_back(i*i);
y.push_back(sin(2*M_PI*i/360.0));
z.push_back(log(i));
if (i % 10 == 0) {
// Clear previous plot
plt::clf();
// Plot line from given x and y data. Color is selected automatically.
plt::plot(x, y);
// Plot a line whose name will show up as "log(x)" in the legend.
plt::named_plot("log(x)", x, z);
// Set x-axis to interval [0,1000000]
plt::xlim(0, n*n);
// Add graph title
plt::title("Sample figure");
// Enable legend.
plt::legend();
// Display plot continuously
plt::pause(0.01);
}
}
}
输出效果: