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



我使用TPL Dataflow从路径加载视频(我使用Emgu.CV库加载(,并通过TPL Dataflow首先将其绘制在Windows窗体应用程序中(之后将与板进行通信(。我有另一个帖子,在这里对我的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-it被提到了BoundedCCapacity方法。但我试过了,错误仍然存在。以下是代码:

public async void CreateVideoProcessingNetwork()
{
//string video_path = @"C:Projects_RepoComlabDMA_Ethernet_videontComlabGUI_Ultrascale_ethernetntComlabGUIvideo_640x360_360p.mp4";
string video_path = @"C:Projects_RepoComlabDMA_Ethernet_videontComlabGUI_Ultrascale_ethernetntComlabGUIvideo_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;

}

也许以下链接就是解决方案?:如何在TPL数据流中安排流量控制?

提前感谢您的帮助,并且为了快速响应,真的很感激!

最可能的问题是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窗体应用程序在启动时会在UI线程上安装一个特殊的SynchronizationContext,我们希望TaskScheduler.FromCurrentSynchronizationContext方法能够捕获此上下文。

最新更新