fangfufu / Linux-Fake-Background-Webcam

Faking your webcam background under GNU/Linux, now supports background blurring, animated background, colour map effect, hologram effect and on-demand processing.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Race condition in multithreaded code.

fangfufu opened this issue · comments

There is race condition in the multithreaded version of the code:
https://github.com/fangfufu/Linux-Fake-Background-Webcam/tree/multithreaded

This was first reported in #105

Multithreaded version of the code seems to be desirable (#111 (comment))

@brandoningli , do you mind testing out the multithreaded branch again, to see if it still crashes. I added an extra .copy() for good luck.

I'm still getting segfaults on the multithreaded branch. 😞

Okay, I will investigate when I next have time. :)

@brandoningli , please give the latest version a go:
https://github.com/fangfufu/Linux-Fake-Background-Webcam/tree/multithreaded

The commit should be aaeec91

I doubt it will help much, but it is worth a try.

Yep, still producing a segfault.

Just out of curiosity, does this old version segfault?
https://github.com/fangfufu/Linux-Fake-Background-Webcam/tree/bodypix

If you don't mind, please try it out. Please also report the FPS. (I think you mentioned 1-2 in the other thread)

If I remember correctly, I never got it to work properly between the two scripts, but I used the docker version successfully. I had to drop down to 480p to get a usable framerate, though.

I don't blame you if you don't want to give the old version a go. I literally used it for 2 weeks before the novelty wore off. People on the Internet seemed to like it a lot, and I got loads of patches.

Last week a few people complained about the pinning of old Bodypix version. I updated it to a newer version, and that produced memory leak, which people complained about. I downgraded Bodypix, then people started complaining about Bodypix again. This is when I decided to replace the whole thing with Mediapipe...

Even the single-threaded mediapipe is a lot better than the Bodypix version. On both single- and multi-threaded (before it segfaulted) mediapipe I'm averaging 24fps with foreground, 26 or so without, and 17 or so when doing something else CPU intensive, like streaming video. This is now something I could legitimately use on a Zoom call.

@brandoningli , could you try the latest multithreaded version, please? (c7a69a7)

First run segfaulted immediately. Second run segfaulted after about 20 seconds.

Hmm I have no idea then... I think I will leave it for now.

Hi, could you try out d048305 please? Please note that you probably need to re-clone the repository, because I forced pushed to the multithread branch to undo some of the commits.

Still segfaulting after 30 seconds to a minute

It does work for me, and it reports higher FPS (Though the camera apparently only supports 30, but fake.py goes to 40+).
But looking at the camera feed it looks more fluid using the multithreading version.

It took like 40min or so, and then produced this error: Segmentation fault (core dumped).
No other information available.

@BlackDex , this is race condition for you, it is hard to trigger.

I did kinda workaround it.

while true; do ./fake.py ; sleep 1; done

Just for the record, the multithreaded code is a bit outdated now, because I added a new feature - now pressing Ctrl + C will pause the fake webcam, and disconnect the real webcam. This allows you to keep the program running in the background while not using it. Two people requested this feature separately. lol.

Some instructions on how to obtain more debugging info would be beneficial. Who could help with that?

Well, I think it probably needs better inter-thread communication. I don't use Python enough, and I didn't write this multithread code in the first place. The code looks fine to me, if I was in C, I would do exactly what this code has. So I am a bit clueless on how to fix it.

@BlackDex , @brandoningli and @katakombi could you the latest commit in the multithreaded branch a go please? (d5b1d6f)

Works quite well when it's just in the Zoom settings menu, but switching to a Zoom or Google Meet meeting causes a segfault every so often. At least in Zoom meetings, it's a lot less frequently than it has been in the past.

Still segfaulting every now and again, but seems even more stable still. Getting around 30-32fps in a Zoom meeting, and around 26-28 while also streaming YouTube. It segfaulted when the YouTube page was first loading, but that may be a coincidence and not a cause.

I pulled and started the program a minute or so after you pinged me, and since then it's only segfaulted one time.

Well it doesn't really solve the problem. I basically put some locks in, they don't seem to help. Feel free to have a look at the source code, and tell me what might be causing the problem.

But that one segfault implies the script is not correct. I don't really want to put the multithreaded version in the master branch, because segfaulting is really bad for user experiences.

Works quite well when it's just in the Zoom settings menu, but switching to a Zoom or Google Meet meeting causes a segfault every so often. At least in Zoom meetings, it's a lot less frequently than it has been in the past.

I never have had problems specific to zoom. The crashes seemed independent of what I've used.

@fangfufu, it is quite stable for me. But if i start stressing out the CPU, and FPS goes below 5 or something it starts with the SegFaults. Maybe it doesn't get images correctly and crashes because of that, or some other strange CPU/Cycle race.

It does keep working with the single threaded, it goes even as low as 3 FPS and nothing happens.

@fangfufu I suspect we have to look out for two different bugs. I am running in single threaded mode and getting segfaults after a couple of minutes. Maybe it is all getting so confusing because there is one issue with multithreading and one unrelated to that.

@katakombi , are you having problem with the single threaded implementation on the master branch? If so, please open a separate issue. This issue is focused on the multithreaded branch.

Hi,
I also get a sigsagv on singlethreaded and multithreaded branch.
I've run the multithreaded branch with gdb.
The stacktrace suggests a problem with mediapipe. I'll post it in case someone else see's something interesting:

Thread 1 "python3" received signal SIGSEGV, Segmentation fault.
0x000000000050e999 in ?? ()
(gdb) backtrace
#0  0x000000000050e999 in ?? ()
#1  0x00007fffec29a159 in pybind11::handle::dec_ref() const & ()
   from /home/mweber/.local/lib/python3.9/site-packages/mediapipe/python/_framework_bindings.cpython-39-x86_64-linux-gnu.so
#2  0x00007fffeca7609c in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) ()
   from /home/mweber/.local/lib/python3.9/site-packages/mediapipe/python/_framework_bindings.cpython-39-x86_64-linux-gnu.so
#3  0x000000000054350c in ?? ()
#4  0x0000000000521d6b in _PyObject_MakeTpCall ()
#5  0x0000000000540385 in ?? ()
#6  0x00000000005177f3 in _PyEval_EvalFrameDefault ()
#7  0x0000000000514a75 in ?? ()
#8  0x000000000052d302 in _PyFunction_Vectorcall ()
#9  0x000000000054015f in ?? ()
#10 0x00000000005177f3 in _PyEval_EvalFrameDefault ()
#11 0x0000000000514a75 in ?? ()
#12 0x000000000052d302 in _PyFunction_Vectorcall ()
#13 0x0000000000516543 in _PyEval_EvalFrameDefault ()
#14 0x000000000052d163 in _PyFunction_Vectorcall ()
#15 0x0000000000516543 in _PyEval_EvalFrameDefault ()
#16 0x000000000052d163 in _PyFunction_Vectorcall ()
#17 0x0000000000516543 in _PyEval_EvalFrameDefault ()
#18 0x000000000052d163 in _PyFunction_Vectorcall ()
#19 0x000000000051635b in _PyEval_EvalFrameDefault ()
#20 0x0000000000514a75 in ?? ()
#21 0x000000000051480b in _PyEval_EvalCodeWithName ()
#22 0x00000000005fb257 in PyEval_EvalCode ()
#23 0x00000000006205fb in ?? ()
#24 0x000000000061b724 in ?? ()
#25 0x000000000061fb2d in ?? ()
#26 0x000000000061f63a in PyRun_SimpleFileExFlags ()
#27 0x0000000000613527 in Py_RunMain ()
#28 0x00000000005ef7fd in Py_BytesMain ()
#29 0x00007ffff7c1d565 in __libc_start_main (main=0x5ef7c0, argc=6, argv=0x7fffffffde38, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffffffde28) at ../csu/libc-start.c:332
#30 0x00000000005ef6fe in _start ()

I'll try to dig a little deeper.

@DrDynamic , could you give --no-ondemand a try?

The parameter --no-ondemand wasn't available on the multithreaded branch.
I tried it on master but seems to have the same result:

Thread 1 "python3" received signal SIGSEGV, Segmentation fault.
0x000000000050e999 in ?? ()
(gdb) 
(gdb) backtrace
#0  0x000000000050e999 in ?? ()
#1  0x00007fffec24d159 in pybind11::handle::dec_ref() const & ()
   from /home/mweber/.local/lib/python3.9/site-packages/mediapipe/python/_framework_bindings.cpython-39-x86_64-linux-gnu.so
#2  0x00007fffeca2909c in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) ()
   from /home/mweber/.local/lib/python3.9/site-packages/mediapipe/python/_framework_bindings.cpython-39-x86_64-linux-gnu.so
#3  0x000000000054350c in ?? ()
#4  0x0000000000521d6b in _PyObject_MakeTpCall ()
#5  0x0000000000540385 in ?? ()
#6  0x00000000005177f3 in _PyEval_EvalFrameDefault ()
#7  0x0000000000514a75 in ?? ()
#8  0x000000000052d302 in _PyFunction_Vectorcall ()
#9  0x000000000054015f in ?? ()
#10 0x00000000005177f3 in _PyEval_EvalFrameDefault ()

@DrDynamic , please continue your bug report on #127.

Could anyone gdb the multithreaded version? @brandoningli , could you do it please? You seem to be able to trigger segfault reliably. @BlackDex ?

On commit 2d8d20d
All requirements are already satisfied per pip3

Thread 1 "python3" received signal SIGSEGV, Segmentation fault.
0x00000000005d18e9 in dict_dealloc (mp=0x7fffdeb53fc0) at ../Objects/dictobject.c:1986
1986	../Objects/dictobject.c: No such file or directory.

(gdb) backtrace
#0  0x00000000005d18e9 in dict_dealloc (mp=0x7fffdeb53fc0) at ../Objects/dictobject.c:1986
#1  0x00007fffdf9dd069 in pybind11::handle::dec_ref() const & () from /home/brandon/.local/lib/python3.8/site-packages/mediapipe/python/_framework_bindings.cpython-38-x86_64-linux-gnu.so
#2  0x00007fffe01a7eac in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) () from /home/brandon/.local/lib/python3.8/site-packages/mediapipe/python/_framework_bindings.cpython-38-x86_64-linux-gnu.so
#3  0x00000000005f2cc9 in cfunction_call_varargs (kwargs=<optimized out>, args=<optimized out>, func=<built-in method add_packet_to_input_stream of PyCapsule object at remote 0x7fffe08d4480>) at ../Objects/call.c:773
#4  PyCFunction_Call (func=<built-in method add_packet_to_input_stream of PyCapsule object at remote 0x7fffe08d4480>, args=<optimized out>, kwargs=<optimized out>) at ../Objects/call.c:773
#5  0x00000000005f30ff in _PyObject_MakeTpCall (callable=<built-in method add_packet_to_input_stream of PyCapsule object at remote 0x7fffe08d4480>, args=<optimized out>, nargs=<optimized out>, keywords=<optimized out>)
    at ../Objects/call.c:159
#6  0x000000000050bf55 in _PyObject_Vectorcall (kwnames=('stream', 'packet'), nargsf=<optimized out>, args=0x7fffdeb919e8, callable=<built-in method add_packet_to_input_stream of PyCapsule object at remote 0x7fffe08d4480>)
    at ../Include/cpython/abstract.h:125
#7  _PyObject_Vectorcall (kwnames=('stream', 'packet'), nargsf=<optimized out>, args=0x7fffdeb919e8, callable=<built-in method add_packet_to_input_stream of PyCapsule object at remote 0x7fffe08d4480>)
    at ../Include/cpython/abstract.h:115
#8  method_vectorcall (method=<optimized out>, args=0x7fffdeb919f0, nargsf=<optimized out>, kwnames=('stream', 'packet')) at ../Objects/classobject.c:60
#9  0x000000000056bc9b in _PyObject_Vectorcall (kwnames=('stream', 'packet'), nargsf=<optimized out>, args=<optimized out>, callable=<method at remote 0x7fffdebb1140>) at ../Include/cpython/abstract.h:127
#10 call_function (kwnames=('stream', 'packet'), oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=<optimized out>) at ../Python/ceval.c:4963
#11 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3515
#12 0x0000000000568d9a in PyEval_EvalFrameEx (throwflag=0, 
    f=Frame 0x7fffdeb91840, for file /home/brandon/.local/lib/python3.8/site-packages/mediapipe/python/solution_base.py, line 1587, in process (self=<SelfieSegmentation(_input_stream_type_info={'image': <_PacketDataType(_value_='image_frame', _name_='IMAGE_FRAME', __objclass__=<EnumMeta(_generate_next_value_=<function at remote 0x7ffff69ca700>, __module__='mediapipe.python.solution_base', __doc__='The packet data types supported by the SolutionBase class.', from_registered_name=<staticmethod at remote 0x7fffdef47160>, _member_names_=['STRING', 'BOOL', 'BOOL_LIST', 'INT', 'FLOAT', 'FLOAT_LIST', 'AUDIO', 'IMAGE', 'IMAGE_FRAME', 'PROTO', 'PROTO_LIST'], _member_map_={'STRING': <_PacketDataType(_value_='string', _name_='STRING', __objclass__=<...>) at remote 0x7fffdef473a0>, 'BOOL': <_PacketDataType(_value_='bool', _name_='BOOL', __objclass__=<...>) at remote 0x7fffdeed1ac0>, 'BOOL_LIST': <_PacketDataType(_value_='bool_list', _name_='BOOL_LIST', __objclass__=<...>) at remote 0x7fffdeed88e0>, 'INT': <_PacketDataType(_val...(truncated)) at ../Python/ceval.c:741
#13 _PyEval_EvalCodeWithName (_co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=<optimized out>, kwargs=0x7fffdeb60888, kwcount=<optimized out>, kwstep=1, 
    defs=0x0, defcount=0, kwdefs=0x0, closure=0x0, name='process', qualname='SolutionBase.process') at ../Python/ceval.c:4298
#14 0x000000000050b868 in _PyFunction_Vectorcall (kwnames=<optimized out>, nargsf=<optimized out>, stack=0x7fffdeb60880, func=<function at remote 0x7fffdef4caf0>) at ../Objects/call.c:436
#15 _PyObject_Vectorcall (kwnames=<optimized out>, nargsf=<optimized out>, args=0x7fffdeb60880, callable=<function at remote 0x7fffdef4caf0>) at ../Include/cpython/abstract.h:127
#16 method_vectorcall (method=<optimized out>, args=0x7fffdeb60888, nargsf=<optimized out>, kwnames=<optimized out>) at ../Objects/classobject.c:60
#17 0x000000000056bc9b in _PyObject_Vectorcall (kwnames=('input_data',), nargsf=<optimized out>, args=<optimized out>, callable=<method at remote 0x7ffff6c989c0>) at ../Include/cpython/abstract.h:127
#18 call_function (kwnames=('input_data',), oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=<optimized out>) at ../Python/ceval.c:4963
#19 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3515
#20 0x0000000000568d9a in PyEval_EvalFrameEx (throwflag=0, 
    f=Frame 0x7fffdeb60700, for file /home/brandon/.local/lib/python3.8/site-packages/mediapipe/python/solutions/selfie_segmentation.py, line 76, in process (self=<SelfieSegmentation(_input_stream_type_info={'image': <_PacketDataType(_value_='image_frame', _name_='IMAGE_FRAME', __objclass__=<EnumMeta(_generate_next_value_=<function at remote 0x7ffff69ca700>, __module__='mediapipe.python.solution_base', __doc__='The packet data types supported by the SolutionBase class.', from_registered_name=<staticmethod at remote 0x7fffdef47160>, _member_names_=['STRING', 'BOOL', 'BOOL_LIST', 'INT', 'FLOAT', 'FLOAT_LIST', 'AUDIO', 'IMAGE', 'IMAGE_FRAME', 'PROTO', 'PROTO_LIST'], _member_map_={'STRING': <_PacketDataType(_value_='string', _name_='STRING', __objclass__=<...>) at remote 0x7fffdef473a0>, 'BOOL': <_PacketDataType(_value_='bool', _name_='BOOL', __objclass__=<...>) at remote 0x7fffdeed1ac0>, 'BOOL_LIST': <_PacketDataType(_value_='bool_list', _name_='BOOL_LIST', __objclass__=<...>) at remote 0x7fffdeed88e0>, 'INT': <_Packe...(truncated)) at ../Python/ceval.c:741
#21 _PyEval_EvalCodeWithName (_co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=<optimized out>, kwargs=0x7fffdeb54f08, kwcount=<optimized out>, kwstep=1, 
    defs=0x0, defcount=0, kwdefs=0x0, closure=(<cell at remote 0x7fffdef0fe20>,), name='process', qualname='SelfieSegmentation.process') at ../Python/ceval.c:4298
#22 0x00000000005f5b33 in _PyFunction_Vectorcall (func=<optimized out>, stack=0x7fffdeb54ef8, nargsf=<optimized out>, kwnames=<optimized out>) at ../Objects/call.c:436
#23 0x000000000056acb6 in _PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x7fffdeb54ef8, callable=<function at remote 0x7fffdeb4f4c0>) at ../Include/cpython/abstract.h:127
#24 call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x963670) at ../Python/ceval.c:4963
#25 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3486
#26 0x00000000005f5956 in PyEval_EvalFrameEx (throwflag=0, 
    f=Frame 0x7fffdeb54d60, for file fake.py, line 260, in compose_frame (self=<FakeCam(no_background=False, use_foreground=False, hologram=False, tiling=False, background_blur=25, background_keep_aspect=False, background_image='./background.jpg', foreground_image='./foreground.jpg', foreground_mask_image='./foreground-mask.png', real_cam=<RealCam(cam=<cv2.VideoCapture at remote 0x7fffdeb7eaf0>, stopped=False, frame=<numpy.ndarray at remote 0x7fffdeb78030>, thread=<Thread(_target=<method at remote 0x7ffff6ac2dc0>, _name='Thread-1', _args=(), _kwargs={}, _daemonic=False, _ident=140736678102784, _native_id=13937, _tstate_lock=<_thread.lock at remote 0x7fffdeb5b750>, _started=<Event(_cond=<Condition(_lock=<_thread.lock at remote 0x7fffdeb5b420>, acquire=<built-in method acquire of _thread.lock object at remote 0x7fffdeb5b420>, release=<built-in method release of _thread.lock object at remote 0x7fffdeb5b420>, _waiters=<collections.deque at remote 0x7fffdeb620a0>) at remote 0x7fffdeb5b4f0>, _flag=True) at remote 0x7fffd...(truncated)) at ../Python/ceval.c:741
#27 function_code_fastcall (globals=<optimized out>, nargs=<optimized out>, args=<optimized out>, co=<optimized out>) at ../Objects/call.c:284
#28 _PyFunction_Vectorcall (func=<optimized out>, stack=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at ../Objects/call.c:411
#29 0x000000000056acb6 in _PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x7fffdeb913e0, callable=<function at remote 0x7fffdeb4ff70>) at ../Include/cpython/abstract.h:127
#30 call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x963670) at ../Python/ceval.c:4963
#31 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3486
#32 0x00000000005f5956 in PyEval_EvalFrameEx (throwflag=0, 
    f=Frame 0x7fffdeb91240, for file fake.py, line 306, in run (self=<FakeCam(no_background=False, use_foreground=False, hologram=False, tiling=False, background_blur=25, background_keep_aspect=False, background_image='./background.jpg', foreground_image='./foreground.jpg', foreground_mask_image='./foreground-mask.png', real_cam=<RealCam(cam=<cv2.VideoCapture at remote 0x7fffdeb7eaf0>, stopped=False, frame=<numpy.ndarray at remote 0x7fffdeb78030>, thread=<Thread(_target=<method at remote 0x7ffff6ac2dc0>, _name='Thread-1', _args=(), _kwargs={}, _daemonic=False, _ident=140736678102784, _native_id=13937, _tstate_lock=<_thread.lock at remote 0x7fffdeb5b750>, _started=<Event(_cond=<Condition(_lock=<_thread.lock at remote 0x7fffdeb5b420>, acquire=<built-in method acquire of _thread.lock object at remote 0x7fffdeb5b420>, release=<built-in method release of _thread.lock object at remote 0x7fffdeb5b420>, _waiters=<collections.deque at remote 0x7fffdeb620a0>) at remote 0x7fffdeb5b4f0>, _flag=True) at remote 0x7fffdeb5b550>, ...(truncated)) at ../Python/ceval.c:741
#33 function_code_fastcall (globals=<optimized out>, nargs=<optimized out>, args=<optimized out>, co=<optimized out>) at ../Objects/call.c:284
#34 _PyFunction_Vectorcall (func=<optimized out>, stack=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at ../Objects/call.c:411
#35 0x000000000056acb6 in _PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x100c1d0, callable=<function at remote 0x7fffdeb4e160>) at ../Include/cpython/abstract.h:127
#36 call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x963670) at ../Python/ceval.c:4963
#37 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3486
#38 0x00000000005f5956 in PyEval_EvalFrameEx (throwflag=0, 
    f=Frame 0x100c050, for file fake.py, line 651, in main (args=<Namespace(width=1280, height=720, fps=30, codec='MJPG', webcam_path='/dev/video2', v4l2loopback_path='/dev/video7', image_folder='.', no_background=False, background_image='background.*', tile_background=False, background_blur=25, background_keep_aspect=False, no_foreground=True, foreground_image='foreground.*', foreground_mask_image='foreground-mask.*', hologram=False) at remote 0x7fffdeb5b400>, cam=<FakeCam(no_background=False, use_foreground=False, hologram=False, tiling=False, background_blur=25, background_keep_aspect=False, background_image='./background.jpg', foreground_image='./foreground.jpg', foreground_mask_image='./foreground-mask.png', real_cam=<RealCam(cam=<cv2.VideoCapture at remote 0x7fffdeb7eaf0>, stopped=False, frame=<numpy.ndarray at remote 0x7fffdeb78030>, thread=<Thread(_target=<method at remote 0x7ffff6ac2dc0>, _name='Thread-1', _args=(), _kwargs={}, _daemonic=False, _ident=140736678102784, _native_id=13937, _tstate_lock=<_threa...(truncated)) at ../Python/ceval.c:741
#39 function_code_fastcall (globals=<optimized out>, nargs=<optimized out>, args=<optimized out>, co=<optimized out>) at ../Objects/call.c:284
#40 _PyFunction_Vectorcall (func=<optimized out>, stack=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at ../Objects/call.c:411
#41 0x000000000056aadf in _PyObject_Vectorcall (kwnames=0x0, nargsf=<optimized out>, args=0x7ffff6bf07b0, callable=<function at remote 0x7fffdeb4e3a0>) at ../Include/cpython/abstract.h:127
#42 call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>, tstate=0x963670) at ../Python/ceval.c:4963
#43 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3500
#44 0x0000000000568d9a in PyEval_EvalFrameEx (throwflag=0, f=Frame 0x7ffff6bf0640, for file fake.py, line 398, in <module> ()) at ../Python/ceval.c:741
#45 _PyEval_EvalCodeWithName (_co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=<optimized out>, kwargs=0x0, kwcount=<optimized out>, kwstep=2, defs=0x0, 
    defcount=0, kwdefs=0x0, closure=0x0, name=0x0, qualname=0x0) at ../Python/ceval.c:4298
#46 0x000000000068cdc7 in PyEval_EvalCodeEx (closure=0x0, kwdefs=0x0, defcount=0, defs=0x0, kwcount=0, kws=0x0, argcount=0, args=0x0, locals=<optimized out>, globals=<optimized out>, _co=<optimized out>) at ../Python/ceval.c:4327
#47 PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=<optimized out>) at ../Python/ceval.c:718
#48 0x000000000067e161 in run_eval_code_obj (co=0x7ffff6afa7c0, 
    globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='fake.py') at remote 0x7ffff6c07310>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff6c7a0e0>, '__file__': 'fake.py', '__cached__': None, 'itertools': <module at remote 0x7ffff6b27d10>, 'signal': <module at remote 0x7ffff6af1a90>, 'sys': <module at remote 0x7ffff6c6ee00>, 'ArgumentParser': <type at remote 0xa4bc40>, 'partial': <type at remote 0x929380>, 'Any': <_SpecialForm at remote 0x7ffff69adc40>, 'Dict': <_GenericAlias(_inst=False, _special=True, _name='Dict', __origin__=<type at remote 0x90bf00>, __args__=(<TypeVar at remote 0x7ffff699eee0>, <TypeVar at remote 0x7ffff699ef40>), __parameters__=(<...>, <...>), __slots__=None) at remote 0x7ffff69b9b20>, 'cv2': <module at remote 0x7ffff699d310>, 'np': <module at remote 0x7ffff69be8b0>, 'pyfakewebcam': <module at remote 0x7--Type <RET> for more, q to quit, c to continue without paging--
ffff69a5090>, 'os': <module at remote 0x7ffff6c27450>, 'fnmatch': <module at remote 0x7f...(truncated), 
    locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='fake.py') at remote 0x7ffff6c07310>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff6c7a0e0>, '__file__': 'fake.py', '__cached__': None, 'itertools': <module at remote 0x7ffff6b27d10>, 'signal': <module at remote 0x7ffff6af1a90>, 'sys': <module at remote 0x7ffff6c6ee00>, 'ArgumentParser': <type at remote 0xa4bc40>, 'partial': <type at remote 0x929380>, 'Any': <_SpecialForm at remote 0x7ffff69adc40>, 'Dict': <_GenericAlias(_inst=False, _special=True, _name='Dict', __origin__=<type at remote 0x90bf00>, __args__=(<TypeVar at remote 0x7ffff699eee0>, <TypeVar at remote 0x7ffff699ef40>), __parameters__=(<...>, <...>), __slots__=None) at remote 0x7ffff69b9b20>, 'cv2': <module at remote 0x7ffff699d310>, 'np': <module at remote 0x7ffff69be8b0>, 'pyfakewebcam': <module at remote 0x7ffff69a5090>, 'os': <module at remote 0x7ffff6c27450>, 'fnmatch': <module at remote 0x7f...(truncated)) at ../Python/pythonrun.c:1166
#49 0x000000000067e1df in run_mod (mod=<optimized out>, filename=<optimized out>, 
    globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='fake.py') at remote 0x7ffff6c07310>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff6c7a0e0>, '__file__': 'fake.py', '__cached__': None, 'itertools': <module at remote 0x7ffff6b27d10>, 'signal': <module at remote 0x7ffff6af1a90>, 'sys': <module at remote 0x7ffff6c6ee00>, 'ArgumentParser': <type at remote 0xa4bc40>, 'partial': <type at remote 0x929380>, 'Any': <_SpecialForm at remote 0x7ffff69adc40>, 'Dict': <_GenericAlias(_inst=False, _special=True, _name='Dict', __origin__=<type at remote 0x90bf00>, __args__=(<TypeVar at remote 0x7ffff699eee0>, <TypeVar at remote 0x7ffff699ef40>), __parameters__=(<...>, <...>), __slots__=None) at remote 0x7ffff69b9b20>, 'cv2': <module at remote 0x7ffff699d310>, 'np': <module at remote 0x7ffff69be8b0>, 'pyfakewebcam': <module at remote 0x7ffff69a5090>, 'os': <module at remote 0x7ffff6c27450>, 'fnmatch': <module at remote 0x7f...(truncated), 
    locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='fake.py') at remote 0x7ffff6c07310>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff6c7a0e0>, '__file__': 'fake.py', '__cached__': None, 'itertools': <module at remote 0x7ffff6b27d10>, 'signal': <module at remote 0x7ffff6af1a90>, 'sys': <module at remote 0x7ffff6c6ee00>, 'ArgumentParser': <type at remote 0xa4bc40>, 'partial': <type at remote 0x929380>, 'Any': <_SpecialForm at remote 0x7ffff69adc40>, 'Dict': <_GenericAlias(_inst=False, _special=True, _name='Dict', __origin__=<type at remote 0x90bf00>, __args__=(<TypeVar at remote 0x7ffff699eee0>, <TypeVar at remote 0x7ffff699ef40>), __parameters__=(<...>, <...>), __slots__=None) at remote 0x7ffff69b9b20>, 'cv2': <module at remote 0x7ffff699d310>, 'np': <module at remote 0x7ffff69be8b0>, 'pyfakewebcam': <module at remote 0x7ffff69a5090>, 'os': <module at remote 0x7ffff6c27450>, 'fnmatch': <module at remote 0x7f...(truncated), flags=<optimized out>, arena=<optimized out>) at ../Python/pythonrun.c:1188
#50 0x000000000067e281 in pyrun_file (fp=fp@entry=0x9622e0, filename=filename@entry='fake.py', start=start@entry=257, 
    globals=globals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='fake.py') at remote 0x7ffff6c07310>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff6c7a0e0>, '__file__': 'fake.py', '__cached__': None, 'itertools': <module at remote 0x7ffff6b27d10>, 'signal': <module at remote 0x7ffff6af1a90>, 'sys': <module at remote 0x7ffff6c6ee00>, 'ArgumentParser': <type at remote 0xa4bc40>, 'partial': <type at remote 0x929380>, 'Any': <_SpecialForm at remote 0x7ffff69adc40>, 'Dict': <_GenericAlias(_inst=False, _special=True, _name='Dict', __origin__=<type at remote 0x90bf00>, __args__=(<TypeVar at remote 0x7ffff699eee0>, <TypeVar at remote 0x7ffff699ef40>), __parameters__=(<...>, <...>), __slots__=None) at remote 0x7ffff69b9b20>, 'cv2': <module at remote 0x7ffff699d310>, 'np': <module at remote 0x7ffff69be8b0>, 'pyfakewebcam': <module at remote 0x7ffff69a5090>, 'os': <module at remote 0x7ffff6c27450>, 'fnmatch': <module at remote 0x7f...(truncated), 
    locals=locals@entry={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <SourceFileLoader(name='__main__', path='fake.py') at remote 0x7ffff6c07310>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff6c7a0e0>, '__file__': 'fake.py', '__cached__': None, 'itertools': <module at remote 0x7ffff6b27d10>, 'signal': <module at remote 0x7ffff6af1a90>, 'sys': <module at remote 0x7ffff6c6ee00>, 'ArgumentParser': <type at remote 0xa4bc40>, 'partial': <type at remote 0x929380>, 'Any': <_SpecialForm at remote 0x7ffff69adc40>, 'Dict': <_GenericAlias(_inst=False, _special=True, _name='Dict', __origin__=<type at remote 0x90bf00>, __args__=(<TypeVar at remote 0x7ffff699eee0>, <TypeVar at remote 0x7ffff699ef40>), __parameters__=(<...>, <...>), __slots__=None) at remote 0x7ffff69b9b20>, 'cv2': <module at remote 0x7ffff699d310>, 'np': <module at remote 0x7ffff69be8b0>, 'pyfakewebcam': <module at remote 0x7ffff69a5090>, 'os': <module at remote 0x7ffff6c27450>, 'fnmatch': <module at remote 0x7f...(truncated), closeit=closeit@entry=1, flags=0x7fffffffda98) at ../Python/pythonrun.c:1085
#51 0x000000000067e627 in pyrun_simple_file (flags=0x7fffffffda98, closeit=1, filename='fake.py', fp=0x9622e0) at ../Python/pythonrun.c:439
#52 PyRun_SimpleFileExFlags (fp=0x9622e0, filename=<optimized out>, closeit=1, flags=0x7fffffffda98) at ../Python/pythonrun.c:472
#53 0x00000000006b6e62 in pymain_run_file (cf=0x7fffffffda98, config=0x962ad0) at ../Modules/main.c:385
#54 pymain_run_python (exitcode=0x7fffffffda90) at ../Modules/main.c:610
#55 Py_RunMain () at ../Modules/main.c:689
#56 0x00000000006b71ed in Py_BytesMain (argc=<optimized out>, argv=<optimized out>) at ../Modules/main.c:743
#57 0x00007ffff7dd80b3 in __libc_start_main (main=0x4ef190 <main>, argc=7, argv=0x7fffffffdc78, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc68) at ../csu/libc-start.c:308
#58 0x00000000005f96de in _start () at ../Include/internal/pycore_pyerrors.h:13

Hmm, this is not helpful at all.

@fangfufu is this helpful?

 ./fake.py 
Real camera original values are set as: 640x480 with 30 FPS and video codec 1448695129
Real camera new values are set as: 1280x720 with 30 FPS and video codec 1196444237
Running...
Please CTRL-C to reload the background / foreground images
Please CTRL-\ to exit
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
Fatal Python error: Segmentation fault

Thread 0x00007b9c79b44640 (most recent call first):
  File "/data/source/Fake-Bg/./fake.py", line 98 in update
  File "/usr/lib/python3.9/threading.py", line 892 in run
  File "/usr/lib/python3.9/threading.py", line 954 in _bootstrap_inner
  File "/usr/lib/python3.9/threading.py", line 912 in _bootstrap

Thread 0x00007b9cb16c3740 (most recent call first):
  File "/data/source/Fake-Bg/venv/lib/python3.9/site-packages/mediapipe/python/solution_base.py", line 318 in process
  File "/data/source/Fake-Bg/venv/lib/python3.9/site-packages/mediapipe/python/solutions/selfie_segmentation.py", line 76 in process
  File "/data/source/Fake-Bg/./fake.py", line 260 in compose_frame
  File "/data/source/Fake-Bg/./fake.py", line 306 in run
  File "/data/source/Fake-Bg/./fake.py", line 395 in main
  File "/data/source/Fake-Bg/./fake.py", line 398 in <module>

I added the following as a shebang: #!/usr/bin/env -S python3 -q -X faulthandler

And here an other attempt, which run a bit longer:

./fake.py -b living3.jpg --no-foreground
Real camera original values are set as: 640x480 with 30 FPS and video codec 1448695129
Real camera new values are set as: 1280x720 with 30 FPS and video codec 1196444237
Running...
Please CTRL-C to reload the background / foreground images
Please CTRL-\ to exit
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
Fatal Python error: Segmentation fault

Thread 0x00007e05ae1ec640 (most recent call first):
  File "/usr/lib/python3.9/copy.py", line 153 in deepcopy
  File "/data/source/Fake-Bg/./fake.py", line 101 in update
  File "/usr/lib/python3.9/threading.py", line 892 in run
  File "/usr/lib/python3.9/threading.py", line 954 in _bootstrap_inner
  File "/usr/lib/python3.9/threading.py", line 912 in _bootstrap

Current thread 0x00007e05e56ba740 (most recent call first):
  File "/data/source/Fake-Bg/venv/lib/python3.9/site-packages/mediapipe/python/solution_base.py", line 322 in process
  File "/data/source/Fake-Bg/venv/lib/python3.9/site-packages/mediapipe/python/solutions/selfie_segmentation.py", line 76 in process
  File "/data/source/Fake-Bg/./fake.py", line 260 in compose_frame
  File "/data/source/Fake-Bg/./fake.py", line 306 in run
  File "/data/source/Fake-Bg/./fake.py", line 395 in main
  File "/data/source/Fake-Bg/./fake.py", line 398 in <module>
Segmentation fault (core dumped)

Thanks, @BlackDex . Those are quite helpful, but I don't have a solution yet. I have to look into thread safety of numpy.

From a quick google, it looks like many numpy functions are not thread safe without you explicitly adding locks.

Yes, but there are locks already added.

@brandoningli , I know it has been a while. Do you fancy testing f722e39.

@fangfufu, i just tested it, and it still happens when there is a high CPU load. If there is no high load, it keeps running without issues. I also tried to use the latest mediapipe, but there was no difference there.

Hmm, I guess we should just stick to the single threaded version for now.

I think the main issue here is mediapipe in combination with numpy that is where it fails.
And even though it does give me more FPS, but compared to the single-threaded, the single one works just fine without any issues for me.

Abandoning this branch as the performance improvement introduced in 2f7d698 is very significant. I am getting at least 25 FPS on a i7-4900MQ.