2

MATLAB でサンプリングされた信号から一連の「聴覚スパイク」を生成しようとしていますが、これまでに実装した方法は遅いです。遅すぎる。

スパイクは、http: //patrec.cs.tu-dortmund.de/pubs/papers/Plinge2010-RNF.pdfのセクション II、B で生成されます: ゼロクロッシングを検出し、(平方根圧縮された) 正の部分を合計します。各ペア間の信号の。これにより、各スパイクの高さが得られます。それらの位置は、ゼロクロッシングの各ペア間の最大値を持つサンプルを識別することによって検出されます。

これには accumarray(...) を使用することを考えました。これにより、各列がゼロクロッシング (スパイク) のペアを表し、各行がサンプルを表す行列を生成するというアイデアに至りました。次に、各列には、対応するゼロクロッシングのペアの間に 1 が入ります。

現在の実装では、実際のデータ ベクトルからこれらの列を埋めるため、後で accumarray を使用する必要はありません。

現在の実装:

function out = audspike(data)
    % Find the indices of the zero crossings. Two types of zero crossing:
    %   * Exact, samples where data == 0
    %   * Change, where data(i) .* data(i+1) < 0; that is, data changes sign
    %   between two samples. In this implementation i+1 is returned as the
    %   index of the zero crossing.
    zExact = (data == 0);
    zChange = logical([0; data(1:end-1) .* data(2:end) < 0]);
    zeroInds = find(zExact | zChange);

    % Vector of the difference between each zero crossing index
    z=[zeroInds(1)-1; diff(zeroInds)];

    % Find the "number of zeros" it takes to move from the first sample to the 
    % a given zero crossing
    nzeros=cumsum(z);

    % If the first sample is positive, we cannot generate a spike for the first
    % pair of zero crossings as this represents part of the signal that is
    % negative; therefore, skip the first zero crossing and begin pairing from
    % the second
    if data(1) > 0
        nzeros = nzeros(2:2:end);
        nones = z(3:2:end)+1;
    else
        nzeros = nzeros(1:2:end);
        nones = z(2:2:end)+1;
    end

    % Allocate sparse array for result
    G = spalloc(length(data), length(nzeros), sum(nones));

    % Loop through pairs of zero crossings. Each pair gets a column in the
    % resultant matrix. The number of rows of the matrix is the number of 
    % samples. G(n, ii) ~= 0 indicates that sample n belongs to pair ii
    for ii = 1:min(length(nzeros), length(nones))
        sampleInd = nzeros(ii)+1:nzeros(ii)+nones(ii)-1;
        G(sampleInd, ii) = data(sampleInd);
    end

    % Sum the square root-compressed positive parts of signal between each zero
    % crossing
    height = sum(sqrt(G), 2);

    % Find the peak over average position
    [~, pos] = max(G, [], 2);

    out = zeros(size(data));
    out(pos) = height;
end

前述したように、これは遅く、一度に 1 つのチャネルでしか機能しません。遅い部分は (当然のことながら) ループです。行列 G の割り当てをスパース配列ではなく標準の zeros(...) に変更すると、明らかな理由から、遅い部分は sum(...) および max(...) の計算になります。

どうすればこれをより効率的に行うことができますか? 私は MEX 関数を書くことを嫌いではありません。

4

1 に答える 1

0

上記のコードと同じタスクを実行する MEX 関数を作成しましたが、最後の 3 行を除いて、MEX 関数を操作するためにほとんど変更する必要はありません。この方法は桁違いに高速です。

参照用のコードは次のとおりです。

#include "mex.h"
#include "matrix.h"

/*=======================
 * Output arguments
 *=======================
 */
#define OUT_zCross   plhs[0]
#define OUT_sums     plhs[1]
#define OUT_maxes    plhs[2]

/*=======================
 * Input arguments
 *=======================
 */
#define IN_x        prhs[0]
#define IN_fs       prhs[1]

#define myMax(x,y)     ( ( x ) > ( y ) ? ( x ) : ( y ) )

/*=======================
 * Main Function
 *=======================
 */
void mexFunction ( int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[] )
{
    /* params: signal vector; 
       outputs: indices (one-based) of the zero crossings,
       sum of positive values between each pair of zero crossings, and the indices
       (one-based) of the maximum element between each pair
    */

    double *x = NULL;
    double *zCross = NULL;
    double *sums = NULL;
    double *maxes = NULL;
    double curMax = 0;
    unsigned int curMaxPos = 0;
    int Fs = 0;
    unsigned int nZeroCrossings = 0;
    unsigned int nPeaks = 0;
    unsigned int nSamples = 0;
    unsigned int i = 0, j = 0, t = 0;
    bool bIgnoreFirst = false;
    bool bSum = false;

    // Get signal and its size
    x = mxGetPr(IN_x);
    i = mxGetN (IN_x); 
    j = mxGetM (IN_x);

    if (i>1 && j>1) {
        mexPrintf ( "??? Input x must be a vector.\n" );
        return;
    }

    // Length of vector
    nSamples = myMax (i, j);

    zCross = mxCalloc(nSamples, sizeof(double));
    sums = mxCalloc(nSamples, sizeof(double));
    maxes = mxCalloc(nSamples, sizeof(double));

    if (x[0] > 0)
    {
        /* If the first sample is positive, we cannot generate a spike for the first
         pair of zero crossings as this represents part of the signal that is
         negative; therefore, skip the first zero crossing and begin pairing from
         the second */
        bIgnoreFirst = true;
    }
    else if (x[0] == 0)
    {
        // Begin summation from first element
        bSum = true;

        nZeroCrossings = 1;
        sums[0] = x[0];
        curMax = x[0];
        curMaxPos = 0;
    }

    for (t = 1; t < nSamples; ++t)
    {
        // Look for a zero-crossing
        if (x[t] * x[t-1] < 0 || (x[t] == 0 && x[t-1] != 0))
        {
            bool bIgnore = false;

            // If not the first one, we can safely flip the boolean flag
            if (nZeroCrossings != 0)
            {
                bSum = !bSum;
            }
            else if (!bIgnoreFirst)
            {
                // If not, make sure we're not supposed to ignore the first one
                bSum = true;
            }
            else
            {
                bIgnore = true;
            }

            // Store the zero-crossing index
            zCross[nZeroCrossings] = t+1;

            // If this crossing terminated the summation, store and reset the position of the max. element
            if (!bSum && !bIgnore)
            {
                maxes[nPeaks] = curMaxPos+1;
                curMax = 0;
                curMaxPos = 0;
                ++nPeaks;
            }

            ++nZeroCrossings;
        }

        if (bSum)
        {
            sums[nPeaks] += x[t];
            if (x[t] > curMax)
            {
                curMax = x[t];
                curMaxPos = t;
            }
        }
    }

    // Allocate outputs
    OUT_zCross = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);

    OUT_sums = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);

    OUT_maxes = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL);

    mxSetPr(OUT_zCross, zCross);
    mxSetM(OUT_zCross, nZeroCrossings);
    mxSetN(OUT_zCross, 1);

    mxSetPr(OUT_sums, sums);
    mxSetM(OUT_sums, nPeaks);
    mxSetN(OUT_sums, 1);

    mxSetPr(OUT_maxes, maxes);
    mxSetM(OUT_maxes, nPeaks);
    mxSetN(OUT_maxes, 1);

    return;
}
于 2012-11-29T09:38:39.283 に答える