摘要
介绍了C++在windows平台上如何调用matplotlib绘制图表,以及vs如何调用Python的工程属性配置、matplotlib-cpp项目几个示例的编译和使用。
matplotlib-cpp项目
一说起开源的绘图程序库就会想到基于Python的matplotlib库,使用简单且绘图功能强大,和matlab一起成为了使用最广泛的绘图库。但是matplotlib时基于Python的,matlab不仅要付费且也是基于自家的脚本运行。最近使用C++写算法时时长要做可视化的分析,之前都是将数据导出来了用Python再可视化,但感觉这样有点麻烦,就网上搜了搜看看C++有没有什么好用的图表库。一番查找下来就发现两个,一是gnu家的gnuplot,二是matplotlib-cpp。一开始看到matplot我有点惊喜,以为matplotlib还有C++版本,后来仔细一看是基于C++调用Python的方法将matplotlib封装了一层。但是也无妨,总比我之前自己尝试用OpenCV的imshow写的辣鸡图表绘制好多了。
matplotlib-cpp项目地址在:https://github.com/lava/matplotlib-cpp,仓库内容很简单,核心的东西就是一个头文件matplotlibcpp.h, 这个头文件封装了大量了的C++调用matplotlib的api,使用的时候只要将matplotlibcpp.h复制到自己的项目中include就可以了。至于怎么使用,作者也给了比较详细的教程和资料,Readme和examples都有详细的使用方法,contrib里面则是介绍windows平台编译example的方法。
编译example
使用CMake编译matplotlib-cpp是比较方便的,作者也非常贴心地给出了CMakeLists.txt,也非常简单假如要编译自己地matplotlib-cpp项目,CMakeLists.txt只需要以下几行配置就可以了
1
2
3
|
find_package(Python2 COMPONENTS Development NumPy)
target_include_directories(myproject PRIVATE ${Python2_INCLUDE_DIRS} ${Python2_NumPy_INCLUDE_DIRS})
target_link_libraries(myproject Python2::Python Python2::NumPy)
|
但以上为linux系统上编译的方法,假如在windows平台编译,则需要复杂一点点,需要设置Python的目录,可以直接参考作者给出的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
cmake_minimum_required(VERSION 3.7)
project (MatplotlibCPP_Test)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include_directories(${PYTHONHOME}/include)
include_directories(${PYTHONHOME}/Lib/site-packages/numpy/core/include)
link_directories(${PYTHONHOME}/libs)
add_definitions(-DMATPLOTLIBCPP_PYTHON_HEADER=Python.h)
# message(STATUS "*** dump start cmake variables ***")
# get_cmake_property(_variableNames VARIABLES)
# foreach(_variableName ${_variableNames})
# message(STATUS "${_variableName}=${${_variableName}}")
# endforeach()
# message(STATUS "*** dump end ***")
add_executable(minimal ${CMAKE_CURRENT_SOURCE_DIR}/../examples/minimal.cpp)
add_executable(basic ${CMAKE_CURRENT_SOURCE_DIR}/../examples/basic.cpp)
add_executable(modern ${CMAKE_CURRENT_SOURCE_DIR}/../examples/modern.cpp)
add_executable(animation ${CMAKE_CURRENT_SOURCE_DIR}/../examples/animation.cpp)
add_executable(nonblock ${CMAKE_CURRENT_SOURCE_DIR}/../examples/nonblock.cpp)
add_executable(xkcd ${CMAKE_CURRENT_SOURCE_DIR}/../examples/xkcd.cpp)
add_executable(bar ${CMAKE_CURRENT_SOURCE_DIR}/../examples/bar.cpp)
|
下载matplotlib-cpp项目,有了CMakeLists.txt我们就可以很方便地在windows上编译作者给出的example,在编译之前先确保满足以下条件:
- 已安装CMake
- 已安装VS(需要支持c++17,vs2017以上可以支持)
- 已安装Python并且安装时添加环境变量path(最好从Python官网下载安装,芒果使用anaconda安装的编译不成功,应该和Python的虚拟环境有关)
- Python已安装matplotlib、numpy包
安装步骤:
(1)下载matplotlib-cpp
仓库地址:https://github.com/lava/matplotlib-cpp
(2)设置Python环境变量
编辑matplotlib-cpp/contrib/WinBuild.cmd文件
将第7行
1
|
if NOT DEFINED PYTHONHOME set PYTHONHOME=C:/Users/%username%/Anaconda3
|
的PYTHONHOME路径修改为系统中Python的安装路径,此路径在环境变量可以查看,环境变量中的用户变量path里面。
(3)修改编译c++版本
作者提供的CMakeLists.txt中设置的c++版本为c++11的,在最新的代码中作者采用了新的c++语法特性,采用c++11已经无法编译通过。修改matplotlib-cpp/contrib/CMakeLists.txt:
将第4行:
1
|
set(CMAKE_CXX_STANDARD 11)
|
修改为:
1
|
set(CMAKE_CXX_STANDARD 17)
|
(4)
1
2
|
> cd contrib
> WinBuild.cmd
|
编译过程若发生报错
1
2
3
4
5
6
|
matplotlib-cpp-master\matplotlibcpp.h(304): error C2766: explicit specialization; 'matplotli
bcpp::detail::select_npy_type<int64_t>' has already been defined [C:\Users\haomi\Downloads\matplotlib-cpp-master\exampl
es\build\animation.vcxproj]
matplotlib-cpp-master\matplotlibcpp.h(306): error C2766: explicit specialization; 'matplotli
bcpp::detail::select_npy_type<uint64_t>' has already been defined [C:\Users\haomi\Downloads\matplotlib-cpp-master\examp
les\build\animation.vcxproj]
|
报错提示这两行已经被定义过了,打开matplotlib.h,将其注释掉。
1
2
3
4
|
static_assert(sizeof(long long) == 8);
//template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
static_assert(sizeof(unsigned long long) == 8);
// <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
|
重新编译即可。
使用vs直接编译matplotlib
使用vs直接编译的方法也很简单,关键就是属性表的配置部分需要设好,最简单的方式自然是直接照抄CMake编译生成的vs解决方案的,不过里面设置比较多,简答使用的话只需要摘抄关键的几个属性
- 项目必须是Release的,C++调用Python不支持Debug模式
- 附加包含目录(AdditionalIncludeDirectories)
- 预处理器定义:(PreprocessorDefinitions)
- 语言C++标准:设置为C++17
- 附加库目录:(AdditionalLibraryDirectories)
- 链接库依赖:设置位NO
使用matplotlib-cpp
关于如何使用matplotlib-cpp作者也给了很多的例子,这里我就把几个例子搬运过来。
Minimal example:
1
2
3
4
5
6
|
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main() {
plt::plot({1,3,2,4});
plt::show();
}
|
Basic example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include "../matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main()
{
// Prepare data.
int n = 5000;
std::vector<double> x(n), y(n), z(n), w(n,2);
for(int i=0; i<n; ++i) {
x.at(i) = i*i;
y.at(i) = sin(2*M_PI*i/360.0);
z.at(i) = log(i);
}
// Set the size of output image = 1200x780 pixels
plt::figure_size(1200, 780);
// Plot line from given x and y data. Color is selected automatically.
plt::plot(x, y);
// Plot a red dashed line from given x and y data.
plt::plot(x, w,"r--");
// 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, 1000*1000);
// Add graph title
plt::title("Sample figure");
// Enable legend.
plt::legend();
// save figure
const char* filename = "./basic.png";
std::cout << "Saving result to " << filename << std::endl;;
plt::save(filename);
}
|
Modern example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#define _USE_MATH_DEFINES
#include <cmath>
#include "../matplotlibcpp.h"
using namespace std;
namespace plt = matplotlibcpp;
int main()
{
// plot(y) - the x-coordinates are implicitly set to [0,1,...,n)
//plt::plot({1,2,3,4});
// Prepare data for parametric plot.
int n = 5000; // number of data points
vector<double> x(n),y(n);
for(int i=0; i<n; ++i) {
double t = 2*M_PI*i/n;
x.at(i) = 16*sin(t)*sin(t)*sin(t);
y.at(i) = 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t);
}
// plot() takes an arbitrary number of (x,y,format)-triples.
// x must be iterable (that is, anything providing begin(x) and end(x)),
// y must either be callable (providing operator() const) or iterable.
plt::plot(x, y, "r-", x, [](double d) { return 12.5+abs(sin(d)); }, "k-");
// show plots
plt::show();
}
|
Xkcd example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#define _USE_MATH_DEFINES
#include <cmath>
#include "../matplotlibcpp.h"
#include <vector>
namespace plt = matplotlibcpp;
int main() {
std::vector<double> t(1000);
std::vector<double> x(t.size());
for(size_t i = 0; i < t.size(); i++) {
t[i] = i / 100.0;
x[i] = sin(2.0 * M_PI * 1.0 * t[i]);
}
plt::xkcd();
plt::plot(t, x);
plt::title("AN ORDINARY SIN WAVE");
plt::show();
}
|
Bar example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#define _USE_MATH_DEFINES
#include <iostream>
#include <string>
#include "../matplotlibcpp.h"
namespace plt = matplotlibcpp;
int main(int argc, char **argv) {
std::vector<int> test_data;
for (int i = 0; i < 20; i++) {
test_data.push_back(i);
}
plt::bar(test_data);
plt::show();
return (0);
}
|
Aniation example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#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);
}
}
}
|
Line3d example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include "../matplotlibcpp.h"
#include <cmath>
namespace plt = matplotlibcpp;
int main()
{
std::vector<double> x, y, z;
double theta, r;
double z_inc = 4.0/99.0; double theta_inc = (8.0 * M_PI)/99.0;
for (double i = 0; i < 100; i += 1) {
theta = -4.0 * M_PI + theta_inc*i;
z.push_back(-2.0 + z_inc*i);
r = z[i]*z[i] + 1;
x.push_back(r * sin(theta));
y.push_back(r * cos(theta));
}
std::map<std::string, std::string> keywords;
keywords.insert(std::pair<std::string, std::string>("label", "parametric curve") );
plt::plot3(x, y, z, keywords);
plt::xlabel("x label");
plt::ylabel("y label");
plt::set_zlabel("z label"); // set_zlabel rather than just zlabel, in accordance with the Axes3D method
plt::legend();
plt::show();
}
|
Fill example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#define _USE_MATH_DEFINES
#include "../matplotlibcpp.h"
#include <cmath>
using namespace std;
namespace plt = matplotlibcpp;
// Example fill plot taken from:
// https://matplotlib.org/gallery/misc/fill_spiral.html
int main() {
// Prepare data.
vector<double> theta;
for (double d = 0; d < 8 * M_PI; d += 0.1)
theta.push_back(d);
const int a = 1;
const double b = 0.2;
for (double dt = 0; dt < 2 * M_PI; dt += M_PI/2.0) {
vector<double> x1, y1, x2, y2;
for (double th : theta) {
x1.push_back( a*cos(th + dt) * exp(b*th) );
y1.push_back( a*sin(th + dt) * exp(b*th) );
x2.push_back( a*cos(th + dt + M_PI/4.0) * exp(b*th) );
y2.push_back( a*sin(th + dt + M_PI/4.0) * exp(b*th) );
}
x1.insert(x1.end(), x2.rbegin(), x2.rend());
y1.insert(y1.end(), y2.rbegin(), y2.rend());
plt::fill(x1, y1, {});
}
plt::show();
}
|
Fill-inbetween example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#define _USE_MATH_DEFINES
#include "../matplotlibcpp.h"
#include <cmath>
#include <iostream>
using namespace std;
namespace plt = matplotlibcpp;
int main() {
// Prepare data.
int n = 5000;
std::vector<double> x(n), y(n), z(n), w(n, 2);
for (int i = 0; i < n; ++i) {
x.at(i) = i * i;
y.at(i) = sin(2 * M_PI * i / 360.0);
z.at(i) = log(i);
}
// Prepare keywords to pass to PolyCollection. See
// https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.fill_between.html
std::map<string, string> keywords;
keywords["alpha"] = "0.4";
keywords["color"] = "grey";
keywords["hatch"] = "-";
plt::fill_between(x, y, z, keywords);
plt::show();
}
|
Surface example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include "../matplotlibcpp.h"
#include <cmath>
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();
}
|
系列文章
c++调用matplotlib(二)
c++调用matplotlib(三)
c++调用matplotlib(四)
Reference
【1】matplotlib-cpp
本文由芒果浩明发布,转载请注明出处。
本文链接:https://blog.mangoeffect.net/cpp/call-matplotlib-on-cpp.html