通常很容易在Matlab中调用mex
文件(用c / c ++编写)来加速某些计算。然而,根据我的经验,Matlab的真正瓶颈是数据绘图。创建句柄非常昂贵,即使您只更新句柄数据(例如,XData,YData,ZData),这可能需要很长时间。更糟糕的是,由于Matlab是单线程程序,因此不可能同时更新多个图。
因此我的问题:是否有可能编写一个Matlab GUI并调用C ++(或其他一些可并行化的代码)来处理绘图/可视化? I我正在寻找可在Windows,Mac和Linux上运行的跨平台解决方案,但任何能够让我开始使用任何操作系统的解决方案都非常感谢!
我发现C++ library似乎使用了Matlab的plot()
语法,但我不确定这是否会加快速度,因为我害怕如果我绘制到Matlab的figure()
窗口,事情可能会再次放慢速度。
我很感激以前处理过这种情况的人的任何意见和反馈!
编辑:显然,我已经对我的代码进行了描述,而且瓶颈就是绘图(十几个包含大量数据的面板)。
EDIT2:为了获得赏金,我需要一个现实生活,最简单的工作示例,如何做到这一点 - 暗示性答案对我没有帮助。
EDIT3:关于要绘制的数据:在最简单的情况下,考虑20个线图,需要每秒更新一次,例如1000000个数据点。
EDIT4:我知道这是一个很大的积分,但我从来没有说过问题很简单。我不能忽略某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的(数据采样时间为亚毫秒时间分辨率)。事实上,我的数据是使用商业数据采集系统获得的,该系统附带数据查看器(用c ++编写)。该程序可以直观地显示多达60个线图,甚至超过1000000个数据点。
EDIT5:我不喜欢当前的讨论。我知道对我的数据进行二次采样可能会加快速度 - 然而,这不是问题所在。这里的问题是如何让c / c ++ / python / java接口与matlab一起使用,以便通过直接与硬件交谈(或使用任何其他技巧/方式)来加速绘图
答案 0 :(得分:7)
您是否尝试过将渲染方法更改为OpenGL
?
opengl hardware;
set(gcf,'Renderer','OpenGL');
警告! 在这种模式下会有一些东西消失,看起来会有所不同,但通常情节会更快地运行 ,特别是如果你有硬件加速器。
顺便问一下,你确定你会真正获得性能提升吗?
例如,根据我的经验,WPF
中的C#
图形比Matlabs慢得多,尤其是散点图和圆圈。
编辑:我认为实际绘制到屏幕上的点数不会那么多。基本上,这意味着您需要在屏幕中有像素的位置进行插值。看看这个对象:
classdef InterpolatedPlot < handle
properties(Access=private)
hPlot;
end
methods(Access=public)
function this = InterpolatedPlot(x,y,varargin)
this.hPlot = plot(0,0,varargin{:});
this.setXY(x,y);
end
end
methods
function setXY(this,x,y)
parent = get(this.hPlot,'Parent');
set(parent,'Units','Pixels')
sz = get(parent,'Position');
width = sz(3); %Actual width in pixels
subSampleX = linspace(min(x(:)),max(x(:)),width);
subSampleY = interp1(x,y,subSampleX);
set(this.hPlot,'XData',subSampleX,'YData',subSampleY);
end
end
end
以下是如何使用它的示例:
function TestALotOfPoints()
x = rand(10000,1);
y = rand(10000,1);
ip = InterpolatedPlot(x,y,'color','r','LineWidth',2);
end
另一种可能的改进:
此外,如果您的x数据已排序,则可以使用interp1q
代替interp
,这样会更快。
classdef InterpolatedPlot < handle
properties(Access=private)
hPlot;
end
% properties(Access=public)
% XData;
% YData;
% end
methods(Access=public)
function this = InterpolatedPlot(x,y,varargin)
this.hPlot = plot(0,0,varargin{:});
this.setXY(x,y);
% this.XData = x;
% this.YData = y;
end
end
methods
function setXY(this,x,y)
parent = get(this.hPlot,'Parent');
set(parent,'Units','Pixels')
sz = get(parent,'Position');
width = sz(3); %Actual width in pixels
subSampleX = linspace(min(x(:)),max(x(:)),width);
subSampleY = interp1q(x,y,transpose(subSampleX));
set(this.hPlot,'XData',subSampleX,'YData',subSampleY);
end
end
end
用例:
function TestALotOfPoints()
x = rand(10000,1);
y = rand(10000,1);
x = sort(x);
ip = InterpolatedPlot(x,y,'color','r','LineWidth',2);
end
答案 1 :(得分:4)
您无法在小图上放置1000000个数据点。你如何选择每10000分中的一个并绘制那些?
您可以考虑在大型矢量上调用imresize
来缩小它,但是通过省略99%的点来手动构建矢量可能会更快。
@memyself采样操作已经发生。 Matlab正在选择要包含在图表中的数据。你为什么信任matlab?在我看来,您展示的图表显着错误地表示了数据。密集区域应该指示信号处于恒定值,但是在图表中,它可能意味着信号处于该值的一半时间 - 或者在对应于该像素的间隔期间至少一次该值?
答案 2 :(得分:4)
正如许多人在答案中提到的那样,你不需要绘制那么多点。我认为重复安德烈的评论非常重要:
这是一个巨大的积分!屏幕上没有足够的像素来绘制该数量。
重写不同语言的绘图程序是浪费您的时间。编写MATLAB已经花费了大量的时间,whay让你觉得你可以编写一个明显更快的绘图程序(在合理的时间内)?虽然您的例程可能不那么通用,因此会删除MATLAB代码将执行的一些检查,但您的“瓶颈”是您试图绘制如此多的数据。
我强烈建议采取以下两种行动之一:
答案 3 :(得分:4)
由于您需要最高性能,因此您应该考虑编写一个最小的OpenGL查看器。将所有点转储到文件并使用MATLAB中的“system”命令启动查看器。观众可以非常简单。这是使用GLUT实现的,为Mac OS X编译。代码是跨平台的,因此您应该能够为您提到的所有平台编译它。根据您的需要调整此查看器应该很容易。
如果您能够将此查看器与MATLAB更紧密地集成在一起,您可能无需写入和读取文件(=更快的更新)。但是,我没有这方面的经验。也许您可以将此代码放在mex文件中?
编辑:我已更新代码以从CPU内存指针中绘制线条。
// On Mac OS X, compile using: g++ -O3 -framework GLUT -framework OpenGL glview.cpp
// The file "input" is assumed to contain a line for each point:
// 0.1 1.0
// 5.2 3.0
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>
#include <GLUT/glut.h>
using namespace std;
struct float2 { float2() {} float2(float x, float y) : x(x), y(y) {} float x, y; };
static vector<float2> points;
static float2 minPoint, maxPoint;
typedef vector<float2>::iterator point_iter;
static void render() {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(minPoint.x, maxPoint.x, minPoint.y, maxPoint.y, -1.0f, 1.0f);
glColor3f(0.0f, 0.0f, 0.0f);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(points[0]), &points[0].x);
glDrawArrays(GL_LINE_STRIP, 0, points.size());
glDisableClientState(GL_VERTEX_ARRAY);
glutSwapBuffers();
}
int main(int argc, char* argv[]) {
ifstream file("input");
string line;
while (getline(file, line)) {
istringstream ss(line);
float2 p;
ss >> p.x;
ss >> p.y;
if (ss)
points.push_back(p);
}
if (!points.size())
return 1;
minPoint = maxPoint = points[0];
for (point_iter i = points.begin(); i != points.end(); ++i) {
float2 p = *i;
minPoint = float2(minPoint.x < p.x ? minPoint.x : p.x, minPoint.y < p.y ? minPoint.y : p.y);
maxPoint = float2(maxPoint.x > p.x ? maxPoint.x : p.x, maxPoint.y > p.y ? maxPoint.y : p.y);
}
float dx = maxPoint.x - minPoint.x;
float dy = maxPoint.y - minPoint.y;
maxPoint.x += dx*0.1f; minPoint.x -= dx*0.1f;
maxPoint.y += dy*0.1f; minPoint.y -= dy*0.1f;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(512, 512);
glutCreateWindow("glview");
glutDisplayFunc(render);
glutMainLoop();
return 0;
}
编辑:这是基于以下讨论的新代码。它呈现由20个vbos组成的sin函数,每个函数包含100k个点。每个渲染帧添加10k个新点。这使得总共2M点。我的笔记本电脑上的表现是实时的。
// On Mac OS X, compile using: g++ -O3 -framework GLUT -framework OpenGL glview.cpp
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>
#include <cmath>
#include <iostream>
#include <GLUT/glut.h>
using namespace std;
struct float2 { float2() {} float2(float x, float y) : x(x), y(y) {} float x, y; };
struct Vbo {
GLuint i;
Vbo(int size) { glGenBuffersARB(1, &i); glBindBufferARB(GL_ARRAY_BUFFER, i); glBufferDataARB(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); } // could try GL_STATIC_DRAW
void set(const void* data, size_t size, size_t offset) { glBindBufferARB(GL_ARRAY_BUFFER, i); glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); }
~Vbo() { glDeleteBuffers(1, &i); }
};
static const int vboCount = 20;
static const int vboSize = 100000;
static const int pointCount = vboCount*vboSize;
static float endTime = 0.0f;
static const float deltaTime = 1e-3f;
static std::vector<Vbo*> vbos;
static int vboStart = 0;
static void addPoints(float2* points, int pointCount) {
while (pointCount) {
if (vboStart == vboSize || vbos.empty()) {
if (vbos.size() >= vboCount+2) { // remove and reuse vbo
Vbo* first = *vbos.begin();
vbos.erase(vbos.begin());
vbos.push_back(first);
}
else { // create new vbo
vbos.push_back(new Vbo(sizeof(float2)*vboSize));
}
vboStart = 0;
}
int pointsAdded = pointCount;
if (pointsAdded + vboStart > vboSize)
pointsAdded = vboSize - vboStart;
Vbo* vbo = *vbos.rbegin();
vbo->set(points, pointsAdded*sizeof(float2), vboStart*sizeof(float2));
pointCount -= pointsAdded;
points += pointsAdded;
vboStart += pointsAdded;
}
}
static void render() {
// generate and add 10000 points
const int count = 10000;
float2 points[count];
for (int i = 0; i < count; ++i) {
float2 p(endTime, std::sin(endTime*1e-2f));
endTime += deltaTime;
points[i] = p;
}
addPoints(points, count);
// render
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(endTime-deltaTime*pointCount, endTime, -1.0f, 1.0f, -1.0f, 1.0f);
glColor3f(0.0f, 0.0f, 0.0f);
glEnableClientState(GL_VERTEX_ARRAY);
for (size_t i = 0; i < vbos.size(); ++i) {
glBindBufferARB(GL_ARRAY_BUFFER, vbos[i]->i);
glVertexPointer(2, GL_FLOAT, sizeof(float2), 0);
if (i == vbos.size()-1)
glDrawArrays(GL_LINE_STRIP, 0, vboStart);
else
glDrawArrays(GL_LINE_STRIP, 0, vboSize);
}
glDisableClientState(GL_VERTEX_ARRAY);
glutSwapBuffers();
glutPostRedisplay();
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(512, 512);
glutCreateWindow("glview");
glutDisplayFunc(render);
glutMainLoop();
return 0;
}
答案 4 :(得分:3)
是否可以使用备用架构?例如,使用MATLAB生成数据并使用快速库或应用程序(GNUplot?)来处理绘图?
当绘图仪消耗数据时,甚至可能让MATLAB将数据写入流中。然后在MATLAB生成数据时更新绘图。
这种方法可以避免MATLAB在两个独立进程之间进行可笑的缓慢绘图和划分工作。 OS / CPU可能会将进程分配给不同的核心。
答案 5 :(得分:1)
我认为这是可能的,但可能需要从头开始编写绘图代码(至少是您使用的部分),因为您可以重复使用的任何东西正在减慢您的速度。
为了测试可行性,我首先测试任何Win32 GUI是否可以从MEX(调用MessageBox
)运行,然后继续创建自己的窗口,测试窗口消息是否到达您的WndProc。完成所有操作后,您可以将OpenGL上下文绑定到它(或者只使用GDI),然后开始绘图。
然而,节省的成本可能来自更简单的绘图代码和使用更新的OpenGL功能(如VBO),而不是线程。 GPU上的所有内容都已经并行,更多的线程无法更快地将命令/数据传输到GPU。
答案 6 :(得分:1)
正如其他人的建议,我也最终对数据进行了下采样。对于x轴上的每个像素,我计算了数据所采用的最小值和最大值,然后在这些值之间画了一条短的垂直线。整个图由一系列短垂直线组成,每条垂直线紧邻下一条。
实际上,我认为实现最终将图形写入一个使用bitblt连续滚动的位图,只绘制了新点......或者位图是静态的,视口也沿着它滚动...无论如何它很久以前,我可能不会记得它。
答案 7 :(得分:0)
块引用 编辑4:我知道这是一个巨大的积分,但我从来没有说过这个问题很简单。我不能忽略某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的 块引用
这是不正确的。有一种方法可以知道遗漏哪些要点。 Matlab已经在做了。无论你怎么解决这个问题,都必须在某些方面做到这一点。我认为您需要将问题重定向为“我如何确定应该绘制哪些点?”。
根据截图,数据看起来像波形。您可能想要查看大胆的代码。它是一个开源音频编辑程序。它显示实时表示波形的图表,它们的样式与最低屏幕截图中的样式相同。你可以从中借用一些采样技术。
答案 8 :(得分:0)
您正在寻找的是创建MEX文件。
而不是我解释它,你可能会从阅读这篇文章中获益更多:Creating C/C++ and Fortran Programs to be Callable from MATLAB (MEX-Files)(来自MathWorks的文档文章)。
希望这有帮助。