【问题标题】:Passing two parameters (int and array) to embedded Python function将两个参数(int 和数组)传递给嵌入式 Python 函数
【发布时间】:2017-08-17 13:59:12
【问题描述】:

我需要从我的模块中调用 Python 函数并为其设置两个参数:int 和 array。

现在我在调用这个函数时遇到了段错误,我不知道我做错了什么。有人可以指定我的错误在哪里吗?

我的 Python 模块 app.py 中的函数。如果我从 Python 代码中调用它,它会起作用:

def get_model(rate, signal):
    mfcc_train = MFCC().compute(rate, signal) 
    with open('mfcc_test', 'wb') as f:
        pickle.dump(mfcc_train, f)
    return clf()._fit(mfcc_train) 

我调用上述函数的 C 代码。最后一个pring是“打电话之前”

#include <Python.h>
#include <stdio.h>
#include "wav.h"
#include <numpy/arrayobject.h>

int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pArgs;
    uint8_t *samples = NULL;

    wavread("test.wav", &samples);

    printf("No. of channels: %d\n",     header->num_channels);
    printf("Sample rate:     %d\n",     header->sample_rate);
    printf("Bit rate:        %dkbps\n", header->byte_rate*8 / 1000);
    printf("Bits per sample: %d\n\n",     header->bps);
    printf("Sample 0:        %d\n", samples[0]);
    printf("Sample 1:        %d\n", samples[1]);
    // Initialize the Python Interpreter
    printf("Before init\n");
    Py_Initialize();
    PyObject *sysPath = PySys_GetObject("path");
    const char *scriptDirectoryName = ".";
    PyObject *path = PyUnicode_FromString(scriptDirectoryName);
    int result = PyList_Insert(sysPath, 0, path);
    printf("after init\n");
    // Build the name object
    pName = PyUnicode_DecodeFSDefault(argv[1]);
    printf("after pname %s %d\n", argv[1], pName == NULL ? 1 : 0);

    // Load the module object
    pModule = PyImport_Import(pName);
    printf("after pmodule %d\n", pModule == NULL ? 1 : 0);

    // pFunc is also a borrowed reference 
    pFunc = PyObject_GetAttrString(pModule, "get_model");
    printf("after pfunc\n");

    if (PyCallable_Check(pFunc)) 
    {
        pArgs = PyTuple_New(2);
        printf("after pytuple\n");
        PyTuple_SetItem(pArgs, 0, PyLong_FromLong(header->sample_rate));
        printf("after set item\n");
        uint8_t* array = malloc(header->datachunk_size);
        int dims[1];
        dims[0] = header->datachunk_size;
        printf("alloc\n");
        import_array();
        PyObject* pSamples = PyArray_SimpleNewFromData(1, dims, NPY_INT8, (void*)samples);
        printf("pSamples\n");
        PyArray_ENABLEFLAGS((PyArrayObject*)pSamples, NPY_ARRAY_OWNDATA);
        PyTuple_SetItem(pArgs, 1, pSamples);
        printf("Before calling\n");
        pValue = PyObject_CallObject(pFunc, pArgs);
        printf("After calling\n");
    } else 
    {
        PyErr_Print();
    }

    printf("pValue:        %d\n", pValue);
    // Clean up
    Py_DECREF(pModule);
    Py_DECREF(pFunc);
    Py_DECREF(pName);

    // Finish the Python Interpreter
    Py_Finalize();

    free(header);
    free(samples);
}

UPD: 更新了修复了一个问题的代码。但另一个问题仍然存在。它在PyObject* pSamples = PyArray_SimpleNewFromData(1, dims, NPY_INT8, (void*)samples); 中。而且我不知道它有什么问题。

还有 wav.h 以防万一:

#include <inttypes.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <err.h>
typedef struct {
    char     chunk_id[4];
    uint32_t chunk_size;
    char     format[4];
    char     fmtchunk_id[4];
    uint32_t fmtchunk_size;
    uint16_t audio_format;
    uint16_t num_channels;
    uint32_t sample_rate;
    uint32_t byte_rate;
    uint16_t block_align;
    uint16_t bps;
    char     datachunk_id[4];
    uint32_t datachunk_size;
}WavHeader;
WavHeader *header;
void wavread(char *file_name, int16_t **samples)
{
    int fd;
    if (!file_name)
        errx(1, "Filename not specified");
    if ((fd = open(file_name, O_RDONLY)) < 1)
        errx(1, "Error opening file");
    if (!header)
        header = (WavHeader*)malloc(sizeof(WavHeader));
    if (read(fd, header, sizeof(WavHeader)) < sizeof(WavHeader))
        errx(1, "File broken: header");
    if (strncmp(header->chunk_id, "RIFF", 4) ||
        strncmp(header->format, "WAVE", 4))
        errx(1, "Not a wav file");
    if (header->audio_format != 1)
        errx(1, "Only PCM encoding supported");
    if (*samples) free(*samples);
    *samples = (int16_t*)malloc(header->datachunk_size);
    if (!*samples)
        errx(1, "Error allocating memory");
    if (read(fd, *samples, header->datachunk_size) < header->datachunk_size)
        errx(1, "File broken: samples");
    close(fd);
}

【问题讨论】:

    标签: python c python-c-api python-embedding


    【解决方案1】:

    没有header 的定义很难说,但我相信问题出在这条线上

    PyTuple_SetItem(pArgs, 0, header->sample_rate);
    

    PyTuple_SetItem 需要一个 Python 对象,而你传递给它的是一个我认为是整数的东西,它被误解为 PyObject*

    我怀疑你想要

    PyTuple_SetItem(pArgs, 0, PyInt_FromLong(header->sample_rate));
    

    (Python3 中的PyLong_FromLong


    第二期:你freesamples两次。首先你将它传递给 numpy 并告诉 numpy 它拥有数据:

    PyObject* pSamples = PyArray_SimpleNewFromData(1, dims, NPY_INT8, (void*)samples);
    PyArray_ENABLEFLAGS((PyArrayObject*)pSamples, NPY_ARRAY_OWNDATA);
    

    然后在代码的末尾释放它

    free(samples);
    

    我怀疑您打算将新分配的 array 传递给 numpy 而不是 samples。 (如果是这种情况,您仍然需要在它们之间复制数据)

    UPD: cmets 的另一个正确解决方案是将 dims 的类型从 int 更改为 npy_intp

    【讨论】:

    • 是的,你是对的。我昨天发现这个问题时忘记更新这篇文章了。但不幸的是,这不仅是我的代码中的问题。现在我遇到了这条线的问题:PyObject* pSamples = PyArray_SimpleNewFromData(1, dims, NPY_INT8, (void*)samples); 它可以工作,但方式不正确,因为有时我在执行结束时遇到了段错误以及其他一些副作用。
    • 不要介意标题 wav.h。它只是用于读取 wav 文件。但以防万一我将其添加到帖子中。
    • 谢谢,现在我意识到我不需要最后一次free 电话。但我的主要问题与此无关。在PyObject_CallObject 期间,我每秒钟都会遇到段错误。我不明白你最后一句话的意思。我将包装 pSamples 放入参数中。它已经包含了必要的数据,不是吗?
    • 最后一行是在猜测您执行uint8_t* array = malloc(header-&gt;datachunk_size); 是为了将其传递给numpy 而不是samples(在这种情况下,您需要复制数据)。不过我可能猜错了
    • 恐怕我现在很难看到其他明显错误的地方。两个小建议:1)将dims 设为npy_intp 只是为了检查您是否没有整数类型不匹配。 2) 如果出现错误,大多数 Python 函数都会返回 NULL - 在每个函数之后添加检查
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多