Почему C# обратные вызовы выполняются только тогда, когда модуль python использует cv.imshow ()? - PullRequest
1 голос
/ 13 апреля 2020

Я не мог придумать лучшего, более описательного названия, так как оно включает 3 языка, которые я сейчас объясню.
Я написал оболочку C++ вокруг модуля Python, которая прекрасно работает в C ++ Кстати. Я сделал из этой оболочки DLL и показал некоторые функции как C и использовал их в приложении C#.

Проблема в том, что приложение C# просто зависает, если я не отображаю Канал веб-камеры.
То есть в модуле Python имеется следующее условие:

if self.debug_show_feed:
    cv2.imshow('service core face Capture', frame)

и при установке True будет отображаться лента веб-камеры.
Это в основном отладочная вещь, которую я ставь и для фактического производства его нужно отключить. На C ++ это нормально, я могу установить это на false (через конструктор), и все в порядке.
Однако на C# такого поведения не происходит, если я пытаюсь использовать модуль без настройки веб-камеры feed true, приложение C# зависает, и это потому, что Start(), который вызывает основную операцию, становится блокирующим вызовом, и ни один из обратных вызовов не возвращается.
my 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() вызывает такое поведение?

1 Ответ

0 голосов
/ 13 апреля 2020

Я нашел причину, по которой обратные вызовы на стороне C# ничего не выводили. Все обратные вызовы выполняются так, как должны, но, поскольку основной l oop на стороне Python является методом блокировки, они начинают свое выполнение только после завершения метода блокировки (как рекурсивная функция, где выходные данные не можно получить до самого конца).

Затем я заметил, что cv2.imshow() создает короткую паузу, и за это время клиент C# получает возможность обновить вывод и то, на что он был отправлен , Сначала я попытался приостановить текущий запущенный поток в Python, и он действительно работал, вывод начал всплывать на стороне C#, но приложение все равно не реагировало.

Я заметил, что могу на самом деле заставить выходные данные обратного вызова отображаться в C#, просто используя cv2.waitkey(1) или cv2.imread('') в предложении else, когда showFeed 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 не является решением, поскольку оно мерцает и просто очень плохо.

Мне нужно добавить, что при использовании события управления timer () на c# для печати вывода и поддержания отзывчивости приложения не работает, создание нового потока также, похоже, не работает. кажется, что маршаллированные обратные вызовы таким способом не могут быть использованы таким образом (хотя я рад слышать, что я не прав).

Я обновлю этот ответ, когда найду лучшие решения, чем оба C# мудро (запуская обратные вызовы в потоке) или на стороне Python, создавая видимое окно или вообще решая эту проблему.

Обновление

Я удалил изменения на стороне Python и реализовал многопоточность на C# части. Причина, по которой первоначальная многопоточность не работала, заключалась в том, что все взаимодействия должны вызываться в одном потоке, то есть все импортированные методы, такие как Initialize, Start и AddCallback, должны были быть настроены и запущены изнутри та же нить.

...