14

mex在 Matlab 中调用文件(用 c/c++ 编写)来加速某些计算通常非常容易。然而,根据我的经验,Matlab 的真正瓶颈是数据绘图。创建句柄非常昂贵,即使您只更新句柄数据(例如,XData、YData、ZData),这也可能需要很长时间。更糟糕的是,由于 Matlab 是单线程程序,不可能同时更新多个绘图。

因此我的问题是:是否可以编写一个 Matlab GUI 并调用 C++(或其他一些可并行化的代码)来处理绘图/可视化?我正在寻找一个可以在 Windows、Mac 和 Linux 上运行的跨平台解决方案,但是任何能让我在任一操作系统上入门的解决方案都非常感谢!

我发现了一个似乎使用 Matlab语法的C++ 库plot(),但我不确定这是否会加快速度,因为我担心如果我在 Matlab 的figure()窗口中绘图,事情可能会再次变慢。

我将不胜感激以前处理过这种情况的人的任何评论和反馈!

编辑:显然,我已经分析了我的代码,瓶颈是绘图(有很多数据的面板)。

EDIT2:为了让你获得赏金,我需要一个真实的生活,关于如何做到这一点的最小工作示例 - 暗示性的答案对我没有帮助。

EDIT3:关于要绘制的数据:在最简单的情况下,考虑 20 个线图,每秒需要更新 1000000 个数据点。

EDIT4:我知道这是要绘制的大量点,但我从未说过这个问题很容易。我不能只遗漏某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的(数据以亚毫秒时间分辨率采样)。事实上,我的数据是使用带有数据查看器(用 C++ 编写)的商业数据采集系统获取的。该程序可以毫无问题地可视化多达 60 个线图,甚至超过 1000000 个数据点。

EDIT5:我不喜欢当前讨论的进展。我知道对我的数据进行二次采样可能会加快速度 - 但是,这不是问题所在。这里的问题是如何让 ac/c++/python/java 接口与 matlab 一起工作,以希望通过直接与硬件对话(或使用任何其他技巧/方式)来加快绘图速度

4

9 回答 9

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
于 2012-02-05T19:56:57.330 回答
4

您无法在一个小图上拟合 1000000 个数据点。您如何在每 10000 个点中选择一个并绘制它们?

您可以考虑调用imresize大向量来缩小它,但是通过省略 99% 的点手动构建向量可能会更快。

@memyself 采样操作已经在进行。Matlab 正在选择要包含在图中的数据。你为什么信任matlab?在我看来,您显示的图表严重歪曲了数据。密集区域应表明信号处于恒定值,但在您的图表中,这可能意味着信号在一半时间内处于该值 - 或者在对应于该像素的间隔期间至少处于该值一次?

于 2012-02-05T21:05:16.950 回答
4

正如许多人在他们的答案中提到的那样,您不需要绘制那么多点。我认为重复安德烈的评论很重要:

这是一个巨大的积分!屏幕上没有足够的像素来绘制该数量。

用不同的语言重写绘图例程是在浪费你的时间。编写 MATLAB 已经花费了大量时间,为什么您认为您可以编写一个明显更快的绘图程序(在合理的时间内)?虽然您的例程可能不太通用,因此会删除 MATLAB 代码将执行的一些检查,但您的“瓶颈”是您试图绘制如此多的数据。

我强烈推荐两种行动方案之一:

  • 对您的数据进行采样:您不需要图形上的 20 x 1000000 个点 - 人眼将无法区分所有点,因此这是浪费时间。例如,尝试对您的数据进行分箱。

  • 如果您坚持认为您需要屏幕上的所有这些点,我建议您使用不同的工具。VisItParaView是我想到的两个例子。它们是并行可视化程序,旨在处理超大数据集(我见过 VisIt 处理包含 PetaBytes 数据的数据集)。

于 2012-02-05T21:35:30.073 回答
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 函数,每个 vbos 包含 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;
}
于 2012-02-10T23:39:44.483 回答
3

是否可以使用替代架构?例如,使用 MATLAB 生成数据并使用快速库或应用程序(GNUplot?)来处理绘图?

当绘图仪使用数据时,甚至可以让 MATLAB 将数据写入流。然后绘图将随着 MATLAB 生成数据而更新。

这种方法可以避免 MATLAB 出奇地缓慢的绘图,并将工作分配给两个独立的进程。当然,OS/CPU 可能会将进程分配给不同的内核。

于 2012-01-20T19:36:13.513 回答
1

我认为这是可能的,但可能需要从头开始编写绘图代码(至少是您使用的部分),因为您可以重用的任何内容都正是让您放慢速度的原因。

为了测试可行性,我首先测试任何 Win32 GUI 是否可以从 MEX(调用MessageBox)开始,然后继续创建您自己的窗口,测试窗口消息是否到达您的 WndProc。完成所有操作后,您可以将 OpenGL 上下文绑定到它(或仅使用 GDI),然后开始绘图。

然而,节省可能来自更简单的绘图代码和使用更新的 OpenGL 功能(如 VBO),而不是线程。GPU 上的一切都已经是并行的,更多的线程无助于更快地将命令/数据传输到 GPU。

于 2012-01-20T19:04:50.627 回答
1

很多年前(2004 年?)我做了一件非常相似的事情。我需要一个类似示波器的显示器,用于实时显示千赫兹采样的生物信号。没有原始问题那么多的点,但对于 MATLAB 自己处理的点仍然太多。IIRC 我最终编写了一个 Java 组件来显示图表。

正如其他人所建议的那样,我最终也对数据进行了下采样。对于 x 轴上的每个像素,我计算了数据的最小值和最大值,然后在这些值之间画了一条短垂直线。整个图表由一系列短垂直线组成,每条垂直线都紧邻下一条。

实际上,我认为实现最终将图形写入使用bitblt连续滚动的位图,只绘制新点......或者位图是静态的并且视口沿着它滚动......无论如何它很长很久以前,我可能记错了。

于 2013-05-21T13:30:11.850 回答
0

Blockquote EDIT4:我知道这是要绘制的大量点,但我从未说过这个问题很容易。我不能只遗漏某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的 Blockquote

这是不正确的。有一种方法可以知道要省略哪些点。Matlab 已经在这样做了。无论你如何解决这个问题,在某个时候都必须做一些事情。我认为您需要将问题重定向为“我如何确定应该绘制哪些点?”。

根据屏幕截图,数据看起来像一个波形。你可能想看看大胆的代码。它是一个开源的音频编辑程序。它显示实时表示波形的图,它们的样式与最低屏幕截图中的样式相同。您可以从他们那里借用一些采样技术。

于 2012-02-08T23:47:53.017 回答
0

您正在寻找的是创建 MEX 文件。

比起我解释它,您可能会从阅读本文中受益更多:Creating C/C++ and Fortran Programs to be Callable from MATLAB (MEX-Files)(来自 MathWorks 的文档文章)。

希望这可以帮助。

于 2012-02-10T08:52:01.023 回答