TPL Dataflow 管道中的图像刷新问题
Posted
技术标签:
【中文标题】TPL Dataflow 管道中的图像刷新问题【英文标题】:Image refreshing problem in TPL Dataflow pipeline 【发布时间】:2021-09-24 08:40:54 【问题描述】:我正在使用 TPL Dataflow 从路径加载视频(我正在使用 Emgu.CV 库进行加载),并通过 TPL Dataflow 首先将其绘制在 Windows 窗体应用程序中(之后将在与木板)。我有另一篇文章对 TPL Dataflow 帮助很大: Asynchronous Task, video buffering
但是在设置 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 Dataflow 在视频显示中进行某种流水线操作。
更新:
在我完成上述更改后,视频播放。但是还有一个问题。 “生产者”的数据创建速度更快(实际上它取决于 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 秒内)。有人怎么能做流量控制?在 Asynchronous Task, video buffering 帖子中提到了 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;
也许以下链接是解决方案? :How do I arrange flow control in TPL Dataflows?
提前感谢您的帮助, 也为了快速响应, 非常感谢!
【问题讨论】:
我很惊讶甚至显示了一张图片。您不能从另一个线程修改 UI。时期。这些块的编写方式与您编写Task.Run
的方式相同,而不是作为管道中的块。您应该使用TransformBlock
或TransformManyBlock
并使用LinkTo
将其输出路由到下一个块,而不是在一个块中使用显式代码发布到另一个块。不需要明确的PropagateCompletion
您可以使用一个块来修改 UI,如果您指定它应该在具有 DataflowBlockOptions.TaskScheduler 属性的 UI 线程上运行
循环的代码试图做什么?为什么不直接使用ToBitmap()
的输出呢?或者更好的是,将Mat
发送到下一个区块?
@PanagiotisKanavos 你是对的。我不知道我为什么要做 MemoryStream。这是没用的,这就是问题所在。循环是加载图像直到视频结束。关于第一条评论,Theodor 也按照您建议的方式向我推荐了我,所以我将更改为 TransformManyBlock 方式。但这种方式也行得通。那么我应该如何处理另一个线程中的块?据我目前了解,我正在从同一线程中的块修改 UI。为 Blocks 设置另一个线程会更好吗?那么我应该如何与 UI 进行通信呢?
嗨@ThKont。在已经回答的问题中添加后续问题在这里是不受欢迎的。推荐的做法是提出一个新问题!
【参考方案1】:
问题很可能是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 线程上安装specialSynchronizationContext
,我们希望TaskScheduler.FromCurrentSynchronizationContext
方法能够捕获此上下文。
【讨论】:
顺便说一句,非常感谢你们两个!响应速度非常快!以上是关于TPL Dataflow 管道中的图像刷新问题的主要内容,如果未能解决你的问题,请参考以下文章
python Dataflow DoFn生命周期中的光束设置()刷新多长时间?
使用 TPL-Dataflow 进行聚合和连接(内、外、左……)?
防止 Apache Beam / Dataflow 流 (python) 管道中的融合以消除管道瓶颈