尝试使用scipy.io.loadmat读取.mat文件时出现“TypeError:缓冲区对于请求的数组来说太小”的错误

3
我有这样一段代码:
import tempfile
import subprocess
import shlex
import os
import numpy as np
import scipy.io

script_dirname = os.path.abspath(os.path.dirname(__file__))


def get_windows(image_fnames, cmd='selective_search'):
    f, output_filename = tempfile.mkstemp(suffix='.mat')
    os.close(f)
    fnames_cell = '{' + ','.join("'{}'".format(x) for x in image_fnames) + '}'
    command = "{}({}, '{}')".format(cmd, fnames_cell, output_filename)
    print(command)

    mc = "matlab -nojvm -r \"try; {}; catch; exit; end; exit\"".format(command)
    pid = subprocess.Popen(
        shlex.split(mc), stdout=open('/dev/null', 'w'), cwd=script_dirname)
    retcode = pid.wait()
    if retcode != 0:
        raise Exception("Matlab script did not exit successfully!")

    all_boxes = list(scipy.io.loadmat(output_filename)['all_boxes'][0])
    subtractor = np.array((1, 1, 0, 0))[np.newaxis, :]
    all_boxes = [boxes - subtractor for boxes in all_boxes]

    os.remove(output_filename)
    if len(all_boxes) != len(image_fnames):
        raise Exception("Something went wrong computing the windows!")
    return all_boxes

if __name__ == '__main__':

    import time

    image_filenames = [
        script_dirname + '/000015.jpg',
        script_dirname + '/cat.jpg'
    ] * 4
    t = time.time()
    boxes = get_windows(image_filenames)
    print(boxes[:2])
    print("Processed {} images in {:.3f} s".format(
        len(image_filenames), time.time() - t))

代码已经测试过,应该是可以正常工作的。

但当我运行代码时,出现了以下错误:

Traceback (most recent call last):
  File "selective_search.py", line 62, in <module>
    boxes = get_windows(image_filenames)

  File "selective_search.py", line 42, in get_windows
    all_boxes = list(scipy.io.loadmat(output_filename)['all_boxes'][0])

  File "/usr/lib/python2.7/dist-packages/scipy/io/matlab/mio.py", line 131, in loadmat
    MR = mat_reader_factory(file_name, appendmat, **kwargs)

  File "/usr/lib/python2.7/dist-packages/scipy/io/matlab/mio.py", line 55, in mat_reader_factory
    mjv, mnv = get_matfile_version(byte_stream)

  File "/usr/lib/python2.7/dist-packages/scipy/io/matlab/miobase.py", line 218, in get_matfile_version
    buffer = fileobj.read(4))

TypeError: buffer is too small for requested array

我正在使用MATLAB 2015。

我该如何解决这个问题?


2
我们应该猜测 TypeError 发生的位置吗? - hpaulj
2
“TypeError” 看起来像是 python/numpy 的错误,而不是 MATLAB 代码产生的。除了提供错误回调堆栈外,您还需要提供一些打印输出(如果需要,请添加诊断打印)。帮助我们确定错误的来源。 - hpaulj
实际上,同样的代码在另一台电脑上成功运行过。我发誓我没有改动代码。 - yusuf
1
由于新的堆栈信息,我推断出MATLAB脚本没有运行,或者至少没有覆盖此脚本在开始时创建的临时文件。 - hpaulj
2个回答

4

这个脚本构建了一个命令行,调用MATLAB,并读取生成的.mat文件。

我认为你需要测试以下几个方面:

  • MATLAB调用命令是否正确?

  • MATLAB是否正常运行?

  • .mat文件是否有效(可以用MATLAB读取)?

  • 你能否从Python中读取它 - 只需使用简单的loadmat命令?

它最后一次运行是多久以前?针对哪个版本的MATLAB?你可能需要更改此脚本,以便不会销毁临时文件,从而使您有机会进行交互式测试。

一个可能性是loadmat无法处理的.mat格式。MATLAB不断更改.mat格式,最新的是某种形式的hd5?。您可能需要更改MATLAB脚本,以便使用早期格式。我不记得在加载较新的不兼容版本时loadmat会产生什么样的错误。


TypeError: buffer is too small for requested array错误通常是由np.ndarray()调用引起的,而不是通常的np.array。但是,深入研究loadmat代码scipy/io/matlab/mio5.py后,我发现它确实使用了ndarray。这指向某种文件格式不兼容性,可能是MATLAB文件版本或32/64位机器差异。


错误出现在

def get_matfile_version(fileobj):

在函数开始处,它试图读取文件的前4个字节:

# Mat4 files have a zero somewhere in first 4 bytes
fileobj.seek(0)
mopt_bytes = np.ndarray(shape=(4,),
                       dtype=np.uint8,
                       buffer = fileobj.read(4))

这段代码正在读取字节,尝试直接从字节中创建一个数组。看起来这是一个直截了当的操作。但是,如果文件是空的会怎样呢?缓冲区将是0字节,太小了。如果是这样,那么问题就是MATLAB无法运行或保存其文件。我需要进行实验。


太好了——.mat文件是空的。

In [270]: with open('test.mat','w') as f:pass  # empty file
In [271]: matlab.loadmat('test.mat')
---------------------------------------------------------------------------
...
/usr/lib/python3/dist-packages/scipy/io/matlab/miobase.py in get_matfile_version(fileobj)
    216     mopt_bytes = np.ndarray(shape=(4,),
    217                            dtype=np.uint8,
--> 218                            buffer = fileobj.read(4))
    219     if 0 in mopt_bytes:
    220         fileobj.seek(0)

TypeError: buffer is too small for requested array

某些原因,MATLAB脚本失败了。 mkstemp 创建一个空的临时文件。 通常情况下,MATLAB脚本会覆盖它(或者追加?)。 但是如果脚本运行失败,则该文件保持为空,当您尝试读取它时会产生此错误。
如果您使用tempfile获取文件名,而不是创建文件,则会出现OSError,“没有这样的文件”。
我认为scipy开发人员没有预料到有人会尝试加载空的.mat文件,否则他们会捕获和翻译这个错误。

1
我没有给它点踩,但是我提交了一个关闭投票——因为关于错误的信息不完整。 - hpaulj
好的,我正在编辑问题。你能否简要地解释一下解决方案给我听? - yusuf
1
你在哪里更改了 'ndarray'(或反之)?np.arraynp.ndarray的使用方式不同。 - hpaulj
1
当我将ndarray确定为问题时,我并不是要你更改脚本。问题出在loadmat中,实际上是早期MATLAB失败的结果。 - hpaulj
2
我会更改如何创建outfilename(并注释掉#),然后找出MATLAB命令是否已创建文件以及它放在哪里。这是一个我们无法远程解决的操作系统级别的问题。 - hpaulj
显示剩余4条评论

2

我已经解决了这个问题。

我告诉你,我在另一台装有MATLAB 2014的电脑上运行了代码,成功了。

那台电脑的gcc版本是4.7,但是当前这台安装有MATLAB 2015的电脑gcc版本是4.9。

我卸载了gcc 4.9并安装了4.7,问题得以解决。:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接