mrkrd / matlab_wrapper

Easy to use MATLAB wrapper for Python
GNU General Public License v3.0
78 stars 23 forks source link

python does not wait for matlab to finish #11

Closed huguesfontenelle closed 8 years ago

huguesfontenelle commented 9 years ago

If I put, eval, get in a script and run it, it fails

  File "~/anaconda/lib/python2.7/site-packages/matlab_wrapper/matlab_session.py", line 174, in eval
    mxresult = self._libeng.engGetVariable(self._ep, 'ERRSTR__')

  File "~/anaconda/lib/python2.7/site-packages/matlab_wrapper/matlab_session.py", line 238, in error_check
    arguments=str(arguments)

RuntimeError: MATLAB function <_FuncPtr object at 0x112378530> failed (<matlab_wrapper.matlab_session.LP_mxArray object at 0x11242d7a0>) with arguments:
(<matlab_wrapper.matlab_session.LP_Engine object at 0x10ec60f80>, 'ERRSTR__')

while if I run the commands one-by-one in the console, it works.

I believe that the eval part takes time, and that the subsequent get fails because the variable does not exists yet.

It could be handled by matlab_wrapper, or perhaps by something like subprocess.wait()

That might be a bug or a feature. What's your take on it?

PS: my particular eval calls a function that does image processing. It does take a long second or so.

mrkrd commented 9 years ago

I guess, it's not about the time, because normally eval should block the execution. I just added a pause in the eval_m_file.py example and it still works.

Could you provide a small script (py and m-file, the smaller the better) that illustrates the issue?

huguesfontenelle commented 9 years ago

Here are scripts that reproduce the problem.

wrapper.py

import os, matlab_wrapper
os.environ['PATH'] += ':/Applications/MATLAB_R2013b.app/bin/'

LIBPATH='/PATH/TO/LIB'

class MatlabWrap(object):

    def __init__(self):
        self.session = matlab_wrapper.MatlabSession(
                options='-nojvm -nodesktop -nodisplay -nosplash',
                buffer_size=1000,
            )
        #self.session.eval(''.join(['addpath','(\'', LIBPATH, '\')']) )

    def __del__(self):
        del self.session

    def get_mask(self, img_filename):
        self.session.put('img_filename', img_filename)
        self.session.eval('img = imread(img_filename);')
        self.session.eval('mask = get_mask(img);')
        self.mask = self.session.get('mask')

I commented out the addpath since there is a bug there too, see https://github.com/mrkrd/matlab_wrapper/issues/9 . Not high priority since the path can always be set inside Matlab.

This wrapper class is invoked by:

if __name__ == '__main__':
    matlab =  MatlabWrap()
    img_filename = 'test.png' # test image provided below
    matlab.get_mask(img_filename)
    mask = matlab.mask

get_mask.m

Here is the Matlab functions.

function mask = get_mask(img)
% find a circular mask

sz=sort(size(img));
radius=sz(2)/2;

[centers,radii] = imfindcircles(img, floor([radius*.90 radius*1.1]), 'Sensitivity', 0.95);

mask = make_circular_mask(centers, radii, size(img));
end

function c_mask = make_circular_mask(centers, radii, mask_size)
% make a circular mask
cx=floor(centers(1));
cy=floor(centers(2));
r=radii;
ix=floor(mask_size(1));
iy=floor(mask_size(2));

[x,y]=meshgrid(-(cx-1):(iy-cx), -(cy-1):(ix-cy));
c_mask=((x.^2+y.^2)<=r^2);

end

These Matlab functions use the circular Hough transform (Image Processing toolbox) to fit a circle to an image, then makes a binary mask the size of the image.

It's useful to have a test image to run this script on: drive_01_test

mrkrd commented 9 years ago

Unfortunately, I was not able to reproduce the error.

I have saved all three files in the same directory, ran wrapper.py and it returned a Boolean mask.

Could you verify, that the example scripts from the repository [1] run without any problems?

wrapper.py

import os, matlab_wrapper
os.environ['PATH'] += ':/Applications/MATLAB_R2013b.app/bin/'

LIBPATH='/PATH/TO/LIB'

class MatlabWrap(object):

    def __init__(self):
        self.session = matlab_wrapper.MatlabSession(
                options='-nojvm -nodesktop -nodisplay -nosplash',
                buffer_size=1000,
            )
        #self.session.eval(''.join(['addpath','(\'', LIBPATH, '\')']) )

    def __del__(self):
        del self.session

    def get_mask(self, img_filename):
        self.session.put('img_filename', img_filename)
        self.session.eval('img = imread(img_filename);')
        self.session.eval('mask = get_mask(img);')
        self.mask = self.session.get('mask')

I commented out the addpath since there is a bug there too, see https://github.com/mrkrd/matlab_wrapper/issues/9 . Not high priority since the path can always be set inside Matlab.

This wrapper class is invoked by:

if __name__ == '__main__':
    matlab =  MatlabWrap()
    img_filename = 'test.png' # test image provided below
    matlab.get_mask(img_filename)
    mask = matlab.mask

BTW: why did you wrap the wrapper in the wrapper class? I would rather write something like this (not tested):

matlab = matlab_wrapper.MatlabSession()

img = matlab.workspace.imread('test.png')
mask = matlab.workspace.get_mask(img)

or if you want to keep the img variable in the MATLAB workspace, use evals instead.

Cheers, Marek

[1] https://github.com/mrkrd/matlab_wrapper/tree/master/examples

huguesfontenelle commented 9 years ago

Thanks Marek. It's very hard to debug, given that if I run the code line-by-line in Anaconda's iPython, I see no problem, but fails when running the whole script at once. Thanks for the "workspace" suggestion, I'll look in it. I made a class so that I can keep reusing the same Matlab session for different scripts..

Regarding the tests, I cloned your repository and ran py.test. 6 fail, 31 pass.

$ PYTHON_PATH=`pwd`; MATLABROOT=/Applications/MATLAB_R2013b.app/ py.test --tb=short
================================================================= test session starts =================================================================
platform darwin -- Python 2.7.9 -- py-1.4.26 -- pytest-2.6.4
collected 37 items 

tests/test_matlab.py ...........F.FF...........F....F.F...

====================================================================== FAILURES =======================================================================
_________________________________________________________________ test_put_complex128 _________________________________________________________________
tests/test_matlab.py:156: in test_put_complex128
    assert_equal(output[3], '   0.1000 + 0.1000i   2.0800 + 2.0800i   4.0600 + 4.0600i')
../../anaconda/lib/python2.7/site-packages/numpy/testing/utils.py:334: in assert_equal
    raise AssertionError(msg)
E   AssertionError: 
E   Items are not equal:
E    ACTUAL: ''
E    DESIRED: '   0.1000 + 0.1000i   2.0800 + 2.0800i   4.0600 + 4.0600i'
__________________________________________________________________ test_put_unicode ___________________________________________________________________
tests/test_matlab.py:182: in test_put_unicode
    assert_equal(s, u"Łódź")
../../anaconda/lib/python2.7/site-packages/numpy/testing/utils.py:334: in assert_equal
    raise AssertionError(msg)
E   AssertionError: 
E   Items are not equal:
E    ACTUAL: u'\x1a\x1a\x1a\x1ad\x1a\x1a'
E    DESIRED: u'\u0141\xf3d\u017a'
________________________________________________________________ test_put_unicode_len _________________________________________________________________
tests/test_matlab.py:195: in test_put_unicode_len
    assert_equal(length, len(s))
../../anaconda/lib/python2.7/site-packages/numpy/testing/utils.py:334: in assert_equal
    raise AssertionError(msg)
E   AssertionError: 
E   Items are not equal:
E    ACTUAL: 7
E    DESIRED: 4
____________________________________________________________________ test_put_cell ____________________________________________________________________
tests/test_matlab.py:351: in test_put_cell
    assert_equal(output[3], "    [1]    'a'       ")
../../anaconda/lib/python2.7/site-packages/numpy/testing/utils.py:334: in assert_equal
    raise AssertionError(msg)
E   AssertionError: 
E   Items are not equal:
E    ACTUAL: ''
E    DESIRED: "    [1]    'a'       "
___________________________________________________________________ test_put_struct ___________________________________________________________________
tests/test_matlab.py:454: in test_put_struct
    assert_equal(output[3], "    f0: 1")
../../anaconda/lib/python2.7/site-packages/numpy/testing/utils.py:334: in assert_equal
    raise AssertionError(msg)
E   AssertionError: 
E   Items are not equal:
E    ACTUAL: '    f2: 1'
E    DESIRED: '    f0: 1'
__________________________________________________________________ test_put_strings ___________________________________________________________________
tests/test_matlab.py:494: in test_put_strings
    assert_equal(output[3], "    'asdf'    'a'    'BBB'")
E   IndexError: list index out of range
========================================================= 6 failed, 31 passed in 4.73 seconds =========================================================