TPL 数据流管道中的图像刷新问题

Image refreshing problem in TPL Dataflow pipeline

我正在使用 TPL 数据流从路径加载视频(我正在使用 Emgu.CV 库进行加载)并通过 TPL 数据流首先将其绘制在 Windows 表单应用程序中(之后是将是与董事会之间的沟通步骤)。我有另一个 post 在这里对 TPL 数据流帮助很大:

但是在设置 TPL 数据流后,第一张图像仅加载到 GUI 中,之后(虽然块似乎 运行 因为打印显示在 cmd 中)图像不会刷新...我不明白哪里出了问题?它与调度程序或 TPL 数据流有关吗? 下面是代码:

public async void CreateVideoProcessingNetwork()
{
    string video_path = @"C:\.......\video_640x360_360p.mp4";

    /* displayVideo Block*/
    var display_video = new ActionBlock<Bitmap>(async received_bitmap =>
    {
        Console.WriteLine("Inside display_video");

        PicturePlot2.Refresh();
        PicturePlot2.Image = received_bitmap;
        Console.WriteLine("Image size = " + received_bitmap.Size);
        Console.WriteLine("Image width = " + received_bitmap.Width);

        await Task.Delay(30);
   });

    var loadVideo = new ActionBlock<string>(async path =>
    {
        capture = new VideoCapture(path);
        Mat matrix = new Mat();
        capture.Read(matrix);
        var mem_stream = new MemoryStream();
        Bitmap HWimage;

        while (matrix.Rows != 0 && matrix.Width != 0)
        {
            Console.WriteLine("Inside LoadVideo");
            matrix = new Mat();
            capture.Read(matrix);
            Bitmap bitmap = new Bitmap(matrix.Width, matrix.Rows);
            bitmap = matrix.ToBitmap();
            bitmap.Save(mem_stream, System.Drawing.Imaging.ImageFormat.Jpeg);
            byte[] image_array = mem_stream.ToArray();
            Console.WriteLine("image_array = " + image_array.Length);
            using (var mem_stream_hw = new MemoryStream(image_array)) HWimage = new Bitmap(mem_stream_hw);
            var accepted = await display_video.SendAsync(HWimage);
            if (!accepted) break;
            await Task.Delay(25);
        }
    });

    PropagateCompletion(loadVideo, display_video);

    loadVideo.Post(video_path);
    loadVideo.Complete();
    await display_video.Completion;
}

我理解错了吗?我想通过 TPL 数据流在视频显示中进行某种流水线操作。

更新:

我做了更改后提到视频播放。但是还有一个问题。 “生产者”的数据创建速度更快(实际上它取决于 TransformManyBlock 中的 Thread.Sleep 时间)并且在播放视频几秒钟后应用程序崩溃并出现以下错误:

Unhandled Exception: System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
   at System.Drawing.Graphics.MeasureString(String text, Font font, SizeF layoutArea, StringFormat stringFormat)
   at System.Drawing.Graphics.MeasureString(String text, Font font, Int32 width)
   at System.Windows.Forms.ThreadExceptionDialog..ctor(Exception t)
   at System.Windows.Forms.Application.ThreadContext.OnThreadException(Exception t)
   at System.Windows.Forms.Control.WndProcException(Exception e)
   at System.Windows.Forms.Control.ControlNativeWindow.OnThreadException(Exception e)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at ntComlabGUI.Program.Main()

例如,如果 Thread.Sleep 被删除,错误几乎会立即发生(在前 1-2 秒内)。有人怎么能做流量控制?在 post 中提到了 BoundedCapacity 方法。但我试过了,错误仍然存​​在。下面是代码:

public async void CreateVideoProcessingNetwork()
{
    //string video_path = @"C:\Projects_Repo\ComlabDMA_Ethernet_video\ntComlabGUI_Ultrascale_ethernet\ntComlabGUI\video_640x360_360p.mp4";
    string video_path = @"C:\Projects_Repo\ComlabDMA_Ethernet_video\ntComlabGUI_Ultrascale_ethernet\ntComlabGUI\video_640x360_360p.mp4";


    /* Video Loading TPL Block */
    var video_loader = new TransformManyBlock<string, Bitmap>(load_video,
        new ExecutionDataflowBlockOptions { BoundedCapacity = 128 });

    IEnumerable<Bitmap> load_video(string path)
    {

        capture = new VideoCapture(path);
        Mat matrix = new Mat();
        capture.Read(matrix);
        var mem_stream = new MemoryStream();


        while (matrix.Rows != 0 && matrix.Width != 0)
        {
            capture.Read(matrix);
            Bitmap bitmap = new Bitmap(matrix.Width, matrix.Rows);

            bitmap = matrix.ToBitmap();

            yield return bitmap;
            //Thread.Sleep(1);

        }
        yield break;
    }



    /* Video Loading TPL Block */

    var display_video = new ActionBlock<Bitmap>(async received_image =>
    {
        PicturePlot2.Image = received_image;
        await Task.Delay(33);
    },
    new ExecutionDataflowBlockOptions()
    {
        TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
        BoundedCapacity = 128
    });



    var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };

    video_loader.LinkTo(display_video, linkOptions);
    video_loader.Post(video_path);
    video_loader.Complete();
    await display_video.Completion;


}

也许下面的link是解决方案? : How do I arrange flow control in TPL Dataflows?

在此先感谢您的帮助, 也为了快速响应, 非常感谢!

最有可能的问题是 PicturePlot2 组件不喜欢被非 UI 线程操纵。为确保 ActionBlock 的委托将在 UI 线程上调用,您可以像这样配置块的 TaskScheduler 选项:

var display_video = new ActionBlock<Bitmap>(async received_bitmap =>
{
    Console.WriteLine("Inside display_video");

    PicturePlot2.Refresh();
    PicturePlot2.Image = received_bitmap;
    Console.WriteLine("Image size = " + received_bitmap.Size);
    Console.WriteLine("Image width = " + received_bitmap.Width);

    await Task.Delay(30);
}, new ExecutionDataflowBlockOptions()
{
    TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});

为此,需要在 UI 线程上实例化该块。那是因为 Windows 表单应用程序安装了一个 special SynchronizationContext on the UI thread when they are launched, and we want the TaskScheduler.FromCurrentSynchronizationContext 方法来捕获此上下文。