为什么 C# 回调只在 python 模块使用 cv.imshow() 时执行? [英] Why are C# callbacks only executed when the python module uses cv.imshow()?
问题描述
我想不出更好的描述性标题,因为它涉及 3 种语言,我现在将解释.
我围绕 Python 模块编写了一个 C++
包装器,顺便说一下,它在 C++ 中工作得很好.我用这个包装器制作了一个 DLL,并将一些功能作为 C 公开,并在 C# 应用程序中使用它们.
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.
问题是,如果我不显示网络摄像头提要,C#
应用程序就会挂起.
那就是在 Python
模块中有这个条件:
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)
当设置 True
时,将显示网络摄像头提要.
这主要是我放置的调试内容,对于实际生产,需要禁用它.在 C++ 上很好我可以将其设置为 false
(通过构造函数),一切都很好.
但是,在 C#
上,如果我尝试使用模块而不将网络摄像头提要设置为 true
,则不会发生这种行为,C#
应用挂起,这是因为调用主操作的 Start()
变成了阻塞调用,并且没有返回任何回调.
顺便说一下,我的DllImport
如下:
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);
这是我的 C# 回调:
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;
}
这是执行的主要python模块:
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()
知道这只会发生在 C#
而不是 C++
,我可能在编组或尝试使用 C#
在这种情况下回调.
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.
这是一个带有最小示例的 Visual Studio:https://workupload.com/file/epsgzmMMVMY
Here is a Visual Studio with a minimal example that demonstrates this: https://workupload.com/file/epsgzmMMVMY
这里有什么问题?为什么 cv.imshow()
会导致这种行为?
What is the problem here? Why is cv.imshow()
causes this behavior?
推荐答案
我找到了 C# 端的回调没有输出任何东西的原因.回调都按预期执行,但是由于 Python 端的主循环是阻塞方法,因此它们仅在阻塞方法结束时才开始执行(就像递归函数一样,直到到最后).
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).
然后我注意到 cv2.imshow()
创建了一个短暂的暂停,在那段时间,C#
客户端有机会更新输出和它发送的内容到.我首先尝试在 Python 中暂停当前正在运行的线程,它实际上有点奏效,输出开始在 C# 端弹出,但应用程序仍然没有响应.
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.
我注意到我实际上可以通过简单地使用 cv2.waitkey(1)
或 cv2.imread('')
在 C# 中显示回调输出showFeed
为 False 时的 else 子句:
I noticed I can actually make the callback outputs show up 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)
...
并通过写作:
while (self.is_running):
...
if self.showFeed:
cv2.imshow("image", frame)
else:
cv2.namedWindow('image', cv2.WINDOW_OPENGL)
cv2.waitKey(1)
cv2.destroyAllWindows()
...
输出显示正常,应用程序再次响应,但是,不断创建和销毁空的 Opencv 窗口不是解决方案,因为它闪烁并且非常糟糕.
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.
我需要补充一点,在 c# 上使用 timer() 控制事件打印输出并保持应用程序响应不起作用,创建新线程似乎也不起作用.似乎这种方式的封送回调不能像这样使用(虽然我很高兴听到我错了).
I need to add that, using a timer() control event on c# to print the output and keep the app responsive doesn't work, creating a new thread also doesn't seem to work. it seems the marshaled callbacks this way can't be used like this (I am happy to hear that I am wrong though).
当我找到比 C# 明智(在线程上运行回调)或 Python 端更好的解决方案时,我会更新此答案,创建可见窗口或完全解决此问题.
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.
我删除了 Python 端的更改并在 C# 部分实现了线程.初始线程不起作用的原因是,所有互操作都必须在同一个线程中调用,这意味着所有导入的方法,例如 Initialize
、Start
和 AddCallback
必须在同一个线程中设置和运行.
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.
这篇关于为什么 C# 回调只在 python 模块使用 cv.imshow() 时执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!