Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我可以让我的WPF应用程序以120 FPS的速度播放一个图像序列,而不用占用这么多内存吗?_C#_Wpf_Image_Caching_Memory - Fatal编程技术网

C# 我可以让我的WPF应用程序以120 FPS的速度播放一个图像序列,而不用占用这么多内存吗?

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

我正在编写一个应用程序,以可变FPS(高达120帧)播放大小不同的图像序列(可能有1000多幅图像),虽然我目前的解决方案适用于少量图像,但它会非常快地消耗内存(播放约109mb图像需要约526mb的进程内存,并且似乎呈指数增长)如果给定大量图像,将产生OutOfMemoryException

我的主窗口和VideoViewer类如下所示-

主窗口:

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