为什么 C# 回调只在 python 模块使用 cv.imshow() 时执行? [英] Why are C# callbacks only executed when the python module uses cv.imshow()?

查看:32
本文介绍了为什么 C# 回调只在 python 模块使用 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# 部分实现了线程.初始线程不起作用的原因是,所有互操作都必须在同一个线程中调用,这意味着所有导入的方法,例如 InitializeStartAddCallback 必须在同一个线程中设置和运行.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆