问题
I couldn't come up with a better more descriptive title as it involves 3 languages which I'll explain now.
I wrote a C++
wrapper around a Python module, which works just fine in C++ by the way. I made a DLL out of this wrapper and exposed some functionalities as a C and used them in a C# application.
The issue is, the C#
application just hangs if I do not display the webcam feed.
That is in Python
module there is this condition:
if self.debug_show_feed:
cv2.imshow('service core face Capture', frame)
and when set True
, will display the webcam feed.
This is mostly a debug thing I put and for actual production it needs to be disabled. On C++ its fine
I can set this to false
(through the constructor) and all is fine.
However, On C#
, this behavior doesn't happen, if I try to use the module without setting the webcam feed to true
, The C#
app hangs, and that's because the Start()
which calls the main operation becomes a blocking call and none of the callbacks are returned.
my DllImport
are as follows by the way:
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Initialize(bool showFeed);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Start(bool async);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Stop();
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCpuAffinity(int mask);
public delegate void CallbackDelegate(bool status, string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddCallback(IntPtr fn);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RemoveCallback(IntPtr fn);
And this is my C# callback:
private CallbackDelegate del;
public void SetUpCallback()
{
txtLog.Text += "Registering C# callback...\r\n";
del = new CallbackDelegate(callback01);
AddCallback(Marshal.GetFunctionPointerForDelegate(del));
txtLog.Text += "Calling passed C++ callback...\r\n";
}
bool status;
string id;
public void callback01(bool status, string id)
{
this.status = status;
this.id = id;
}
And this is the main python modules that are executed :
def start(self):
try:
self.is_running = True
self._main_loop()
except Exception as ex:
path='exceptions-servicecore.log'
track = traceback.format_exc()
exception_time = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
with open(path, 'a') as f:
f.writelines(f'\n{exception_time} : exception occured {ex.args} \n{track}')
def start_async(self):
st = threading.Thread(target=self.start)
st.start()
def _main_loop(self):
name = None
is_valid = False
while self.is_running and self.cap.isOpened():
is_success, frame = self.cap.read()
if is_success:
name="sth"
is_valid=True
self._execute_callbacks(is_valid, name, frame)
self._execute_c_callbacks(is_valid, name)
if self.debug_show_feed:
cv2.imshow('service core face Capture', frame)
if self.save:
self.video_writer.write(frame)
if (cv2.waitKey(1)&0xFF == ord('q')) or (not self.is_running):
break
self.cap.release()
if self.save:
self.video_writer.release()
cv2.destroyAllWindows()
Knowing this only happens in C#
and not C++
, I may have some issues in my marshaling or the way I'm trying to use the C#
callback in this scenario.
Here is a Visual Studio with a minimal example that demonstrates this: https://workupload.com/file/epsgzmMMVMY
What is the problem here? Why is cv.imshow()
causes this behavior?
回答1:
I found the reason why callbacks on C#
side didn't output anything.
The callbacks are all executing as they should, but since the main loop on python side is a blocking method, they only start their execution when the blocking method is over.( just like a recursive function where the outputs don't get to be returned until the very end).
I then noticed cv2.imshow()
creates a short pause and in that time, the C#
client gets the chance to update the output and what it has been sent to.
I first tried to pause the current running thread in Python, and it actually kind of worked, the output started to popup on C#
side, but still the app was not responsive.
I noticed I can actually make the callback outputs showup in C# by simply using
a cv2.waitkey(1)
or cv2.imread('')
in the else clause when showFeed
is False:
while (self.is_running):
...
if self.showFeed:
cv2.imshow("image", frame)
else:
#cv2.imread('')
# or
cv2.waitkey(1)
...
And by writing :
while (self.is_running):
...
if self.showFeed:
cv2.imshow("image", frame)
else:
cv2.namedWindow('image', cv2.WINDOW_OPENGL)
cv2.waitKey(1)
cv2.destroyAllWindows()
...
The outputs are showing just fine and the application is responsive again, However, constantly creating and destroying an empty opencv window is not a solution as it flickers and is just very bad.
I need to add that, using a timer() control event on c# to print the output and keep the app responsive doesnt work, creating a new thread also doesnt seem to work. it seems the marshaled callbacks this way cant be used like this (if I'm wrong I'd greatly appreciate if you could explain how to do it)
I'll update this answer when I find better solutions than these both C# wise(running the callbacks on a thread) or on Python side, making a visible window or altogether fix this issue.
Update :
I removed the changes on Python
side and implemented the threading on C#
part. The reason the initial threading didn't work was because, all interops had to be called in the same thread, meaning all of the imported methods such as Initialize
, Start
and AddCallback
had to be setup and run from within the same thread.
来源:https://stackoverflow.com/questions/61183964/why-are-c-sharp-callbacks-only-executed-when-the-python-module-uses-cv-imshow