为什么 C# 回调仅在 python 模块使用 cv.imshow() 时执行?
Why are C# callbacks only executed when the python module uses cv.imshow()?
我想不出一个更好的更具描述性的标题,因为它涉及 3 种语言,我现在将对其进行解释。
我围绕 Python 模块编写了一个 C++
包装器,顺便说一句,它在 C++ 中工作得很好。我用这个包装器制作了一个 DLL,并将一些功能公开为 C,并在 C# 应用程序中使用它们。
问题是,如果我不显示网络摄像头源,C#
应用程序就会挂起。
即在Python
模块中有这样的条件:
if self.debug_show_feed:
cv2.imshow('service core face Capture', frame)
并且当设置 True
时,将显示网络摄像头画面。
这主要是我放置的调试内容,对于实际生产需要禁用它。在 C++ 上没问题
我可以将其设置为 false
(通过构造函数),一切都很好。
但是,在 C#
上,如果我尝试使用该模块而不将网络摄像头馈送设置为 true
,则 C#
应用程序挂起,这是因为 [=调用主要操作的 24=] 变为阻塞调用,并返回 none 的回调。
顺便说一句,我的DllImport
如下:
[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# 回调:
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 模块:
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#
回调的方式中遇到一些问题.
这里有一个 Visual Studio 和一个最小的例子来证明这一点:https://workupload.com/file/epsgzmMMVMY
这里有什么问题?为什么 cv.imshow()
导致此行为?
我找到了 C# 端的回调没有输出任何内容的原因。回调都按应有的方式执行,但由于 Python 侧的主循环是阻塞方法,因此它们仅在阻塞方法结束时才开始执行(就像输出没有得到的递归函数一样直到最后都归还)。
然后我注意到 cv2.imshow()
产生了一个短暂的停顿,在那个时候,C#
客户端有机会更新输出及其发送到的目标。我首先尝试在 Python 中暂停当前的 运行ning 线程,它确实起作用了,输出开始在 C# 端弹出,但应用程序仍然没有响应。
我注意到当 showFeed
为 False 时,我实际上可以通过在 else 子句中使用 cv2.waitkey(1)
或 cv2.imread('')
使回调输出显示在 C# 中:
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 window 不是解决方案,因为它会闪烁并且非常糟糕。
我需要补充一点,在 c# 上使用 timer() 控制事件来打印输出并保持应用程序响应不起作用,创建新线程似乎也不起作用。似乎不能像这样使用这种方式编组的回调(不过我很高兴听到我错了)。
当我找到比这些更好的解决方案时,我会更新这个答案,无论是在 C# 方面(运行在线程上设置回调)还是在 Python 方面,使可见 window 或完全解决此问题。
更新
我删除了 Python 方面的更改并在 C# 部分实现了线程。初始线程不起作用的原因是,所有互操作都必须在同一个线程中调用,这意味着所有导入的方法,如 Initialize
、Start
和 AddCallback
必须在同一个线程中进行设置和 运行。
我想不出一个更好的更具描述性的标题,因为它涉及 3 种语言,我现在将对其进行解释。
我围绕 Python 模块编写了一个 C++
包装器,顺便说一句,它在 C++ 中工作得很好。我用这个包装器制作了一个 DLL,并将一些功能公开为 C,并在 C# 应用程序中使用它们。
问题是,如果我不显示网络摄像头源,C#
应用程序就会挂起。
即在Python
模块中有这样的条件:
if self.debug_show_feed:
cv2.imshow('service core face Capture', frame)
并且当设置 True
时,将显示网络摄像头画面。
这主要是我放置的调试内容,对于实际生产需要禁用它。在 C++ 上没问题
我可以将其设置为 false
(通过构造函数),一切都很好。
但是,在 C#
上,如果我尝试使用该模块而不将网络摄像头馈送设置为 true
,则 C#
应用程序挂起,这是因为 [=调用主要操作的 24=] 变为阻塞调用,并返回 none 的回调。
顺便说一句,我的DllImport
如下:
[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# 回调:
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 模块:
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#
回调的方式中遇到一些问题.
这里有一个 Visual Studio 和一个最小的例子来证明这一点:https://workupload.com/file/epsgzmMMVMY
这里有什么问题?为什么 cv.imshow()
导致此行为?
我找到了 C# 端的回调没有输出任何内容的原因。回调都按应有的方式执行,但由于 Python 侧的主循环是阻塞方法,因此它们仅在阻塞方法结束时才开始执行(就像输出没有得到的递归函数一样直到最后都归还)。
然后我注意到 cv2.imshow()
产生了一个短暂的停顿,在那个时候,C#
客户端有机会更新输出及其发送到的目标。我首先尝试在 Python 中暂停当前的 运行ning 线程,它确实起作用了,输出开始在 C# 端弹出,但应用程序仍然没有响应。
我注意到当 showFeed
为 False 时,我实际上可以通过在 else 子句中使用 cv2.waitkey(1)
或 cv2.imread('')
使回调输出显示在 C# 中:
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 window 不是解决方案,因为它会闪烁并且非常糟糕。
我需要补充一点,在 c# 上使用 timer() 控制事件来打印输出并保持应用程序响应不起作用,创建新线程似乎也不起作用。似乎不能像这样使用这种方式编组的回调(不过我很高兴听到我错了)。
当我找到比这些更好的解决方案时,我会更新这个答案,无论是在 C# 方面(运行在线程上设置回调)还是在 Python 方面,使可见 window 或完全解决此问题。
更新
我删除了 Python 方面的更改并在 C# 部分实现了线程。初始线程不起作用的原因是,所有互操作都必须在同一个线程中调用,这意味着所有导入的方法,如 Initialize
、Start
和 AddCallback
必须在同一个线程中进行设置和 运行。