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
方法来捕获此上下文。
我正在使用 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 秒内)。有人怎么能做流量控制?在
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
方法来捕获此上下文。