C# 我可以让我的WPF应用程序以120 FPS的速度播放一个图像序列,而不用占用这么多内存吗?
我正在编写一个应用程序,以可变FPS(高达120帧)播放大小不同的图像序列(可能有1000多幅图像),虽然我目前的解决方案适用于少量图像,但它会非常快地消耗内存(播放约109mb图像需要约526mb的进程内存,并且似乎呈指数增长)如果给定大量图像,将产生OutOfMemoryException 我的主窗口和VideoViewer类如下所示- 主窗口:C# 我可以让我的WPF应用程序以120 FPS的速度播放一个图像序列,而不用占用这么多内存吗?,c#,wpf,image,caching,memory,C#,Wpf,Image,Caching,Memory,我正在编写一个应用程序,以可变FPS(高达120帧)播放大小不同的图像序列(可能有1000多幅图像),虽然我目前的解决方案适用于少量图像,但它会非常快地消耗内存(播放约109mb图像需要约526mb的进程内存,并且似乎呈指数增长)如果给定大量图像,将产生OutOfMemoryException 我的主窗口和VideoViewer类如下所示- 主窗口: public partial class MainWindow : Window { OpenFileDialog openFileDia
public partial class MainWindow : Window
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
VideoViewer vV = new VideoViewer();
DispatcherTimer timer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
this.DataContext = vV;
}
private void PlayButton_Click(object sender, RoutedEventArgs e)
{
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1f/120f) };
timer.Tick += TimerTick;
timer.Start();
}
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
timer.Stop();
}
private void TimerTick(object sender, EventArgs e)
{
vV.NextFrame();
}
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
// Set the file dialog to filter for graphics files.
this.openFileDialog1.Filter =
"Images (*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG|" +
"All files (*.*)|*.*";
// Allow the user to select multiple images.
this.openFileDialog1.Multiselect = true;
this.openFileDialog1.Title = "My Image Browser";
DialogResult dr = this.openFileDialog1.ShowDialog();
if (dr == System.Windows.Forms.DialogResult.OK)
{
if (dr == System.Windows.Forms.DialogResult.OK)
{
List<ImageSource> images = new List<ImageSource>();
// Read the files
foreach (String file in openFileDialog1.FileNames)
{
var fullFilePath = @file;
//Create a new bitmap to assign our image to
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bitmap.CacheOption = BitmapCacheOption.None;
bitmap.UriSource = new Uri(fullFilePath, UriKind.Absolute);
bitmap.EndInit();
images.Add(bitmap);
}
vV.imagesArray = images;
}
}
}
}
公共部分类主窗口:窗口
{
OpenFileDialog openFileDialog1=新建OpenFileDialog();
VideoViewer vV=新的VideoViewer();
调度程序计时器=新调度程序();
公共主窗口()
{
初始化组件();
this.DataContext=vV;
}
私有void播放按钮(对象发送者,路由目标e)
{
计时器=新调度程序{Interval=TimeSpan.FromSeconds(1f/120f)};
timer.Tick+=TimerTick;
timer.Start();
}
私有无效暂停按钮单击(对象发送器,路由目标)
{
timer.Stop();
}
私有void TimerTick(对象发送方、事件参数)
{
vV.NextFrame();
}
私有无效浏览按钮单击(对象发送者,路由目标)
{
//将“文件”对话框设置为过滤图形文件。
this.openFileDialog1.Filter=
“图像(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG |”+
“所有文件(*.*)|*.*”;
//允许用户选择多个图像。
this.openFileDialog1.Multiselect=true;
this.openFileDialog1.Title=“我的图像浏览器”;
DialogResult dr=this.openFileDialog1.ShowDialog();
if(dr==System.Windows.Forms.DialogResult.OK)
{
if(dr==System.Windows.Forms.DialogResult.OK)
{
列表图像=新列表();
//读文件
foreach(openFileDialog1.FileNames中的字符串文件)
{
var fullFilePath=@file;
//创建一个新位图以将图像分配给
BitmapImage位图=新的BitmapImage();
bitmap.BeginInit();
bitmap.CreateOptions=BitmapCreateOptions.IgnoreImageCache;
bitmap.CacheOption=BitmapCacheOption.None;
bitmap.UriSource=新Uri(fullFilePath,UriKind.Absolute);
EndInit();
添加(位图);
}
vV.imagesArray=图像;
}
}
}
}
视频查看器:
public class VideoViewer : INotifyPropertyChanged
{
private ImageSource image;
private List<ImageSource> imageLocs = new List<ImageSource>();
private int imageIndex = 0;
public event PropertyChangedEventHandler PropertyChanged;
public ImageSource imageSource
{
get
{
return image;
}
set
{
image = value;
OnPropertyChanged("imageSource");
}
}
public List<ImageSource> imagesArray
{
get
{
return imageLocs;
}
set
{
imageLocs = value;
OnPropertyChanged("imagesArray");
}
}
public void NextFrame()
{
//If not on first or last frame
if(imageIndex < (imageLocs.Count - 1))
{
imageIndex += 1;
imageSource = imagesArray[imageIndex];
OnPropertyChanged("imageSource");
}
else if(imageIndex == (imageLocs.Count - 1))
{
imageIndex = 0;
imageSource = imagesArray[imageIndex];
OnPropertyChanged("imageSource");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
公共类视频查看器:INotifyPropertyChanged
{
私有图像源图像;
私有列表imageLocs=新列表();
私有int imageIndex=0;
公共事件属性更改事件处理程序属性更改;
公共图像源图像源
{
得到
{
返回图像;
}
设置
{
形象=价值;
OnPropertyChanged(“imageSource”);
}
}
公共列表图像
{
得到
{
返回图像;
}
设置
{
imageLocs=值;
OnPropertyChanged(“imagesArray”);
}
}
public void NextFrame()
{
//如果不是在第一帧或最后一帧上
如果(imageIndex<(imageLocs.Count-1))
{
imageIndex+=1;
imageSource=imagesArray[imageIndex];
OnPropertyChanged(“imageSource”);
}
else if(imageIndex==(imageLocs.Count-1))
{
imageIndex=0;
imageSource=imagesArray[imageIndex];
OnPropertyChanged(“imageSource”);
}
}
//创建OnPropertyChanged方法以引发事件
受保护的void OnPropertyChanged(字符串名称)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(名称));
}
}
播放序列时,第一个循环缓慢播放,这是占用内存的地方。在第一个循环之后,它以所需的速度播放,内存处于一个恒定值,这让我认为这与图像的缓存有关
正如您所见,我尝试过调整位图图像的缓存选项,但没有产生任何影响。我目前也在构建x64,为自己提供额外的内存
是否可以在保持播放速度的同时减少内存使用?可能会尝试使用较少的位图内存
尝试使用.StreamSource
属性而不是.UriSource
创建BitmapSource
,并使用OnLoad
缓存选项
var fullFilePath = @file;
//Create a new bitmap to assign our image to
using (FileStream fs = new FileStream(path, FileMode.Open))
{
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = fs;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
images.Add(bitmap);
bitmap.Freeze();
}
见:
替代方法-将图像序列转换为视频并播放 实际上,您要做的是实现视频播放器的行为,但是使用一组“选定”的帧 另一种更有效的方法是获取这些帧集并生成视频文件(比如MP4) 您可以使用许多库来实现这一点:
- ffmpeg
- ffmpeg Autogen(网络包装器)
- accord网络扩展(VideoWriter)
- 点成像库 )
- 微软媒体基金会 <> >媒体基础.NET包装器 在媒体基础上对视频编码的一些例子(注1是UWP……但应该给出关于FultFrase/WPF实现相同的线索)。
使用媒体基础.H.264的图像编码
(如何在通用Windows应用程序中将多个图像编码为视频) 然后,您可以使用标准的内置MediaElem