最近需要做个图像采集的工作,其中一个步骤是需要将从摄像机采集的图像播放出来,由于摄像机采集的是一帧帧的图片,而播放的时候需要对播放速度进行控制,因此我考虑将图片放在一个缓冲区中,然后从缓存区中读取后进行播放。因此首先要做的就是如何将一帧帧的图片播放出来,
通过几天WPF学习,发现可以通过改变image控件的Imagesource或者改变 imageBrush.ImageSource,然后改变Rectangle等的Fill属性,来实现图片的切换。但是做了一个例子发现不是那回事,我在一个循环中不断改变ImageSource值却发现只有最后一帧放出来了,其它帧根本没有显示。并且加入Thread.sleep()让线程等待一段时间也不行。
再研究发现一些例子中都用了CompositionTarget这个类,但是MSDN中帮助让我看一头雾水,“使用 CompositionTarget 对象,可以基于每个帧回调来创建自定义动画。 CompositionTarget是一个静态类,表示您的应用程序要在其上进行绘制的显示图面。 每次绘制应用程序的场景时,都会引发 Rendering 事件。 呈现帧速率是指每秒绘制场景的次数。”弄了好半天才搞明白,其实说白了,就是程序窗口会按照一定的频率进行刷新,在这个刷新时会触发 Rendering 这个事件,在这个事件中编写你的动画过程(例如改变ImageSource)就可以了。真怀念VB6的帮助,详尽易懂,当时市面的书籍基本上照抄,无出其右。现在的帮助搞的我都怀疑自己是不是中国人,怎么中文搞得比英文还难看懂。
from:http://greatverve.cnblogs.com/archive/2011/06/27/csharp-flash.html
如果用CompositionTarget,可以用二种方法改变刷新频率(抄来的,没试过,不保证可用。以后有机会再试)。
方法一:
< param name ="enableGPUAcceleration" value ="true" /> //开启硬件加速
< param name ="enableCacheVisualization" value ="true" /> //测试时,帮忙诊断是否性能有所提示
< param name ="enableFrameRateCounter" value ="true" /> //发布时,和上述参数可一并删除
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
timer.Tick += this .timer_Tick;
// 定义刷新频率(帧率),即DispatcherTimer启动的间隔时间
int x = Convert.ToInt32(((cmbFrames.SelectedItem as ComboBoxItem).Content));
int p = 1000 / x;
timer.Interval = TimeSpan.FromMilliseconds(p);
timer.Start();
IEnumerator it = al.GetEnumerator();
private void timer_Tick( object sender, EventArgs e)
{
// 图片切换
if (it.MoveNext())
{
ImageBrush imageBrush = new ImageBrush();
imageBrush.ImageSource = (ImageSource)it.Current;
img.Fill = imageBrush;
}
}
最近刚接触WPF, 一边学着一边用着,知识点还没有系统化的进行学习整理.
现在手上有一些美术做好的图片,需要连起来观看形成动画的效果,由于需求比较急,一时半会也静不下心来看WPF关于动画的知识.
潜意识里一个Image控件,循环设定Source属性,一看效果总是显示集合的最后一项.难道是循环速度太快?基于此思路下折腾了好久也无结果。开始不得不借着可以找到的资源进行尝试。试想我们一张张的图片进行播放形成的动画,是不是类似帧的这种概念.顺这个便找到了CompositionTarget.Rendering事件创建基于帧的动画.
第一种尝试:
namespace
ImgAniDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public
partial
class
MainWindow : Window
{
ObservableCollection<BitmapImage> bmList;
public
MainWindow()
{
InitializeComponent();
InitList();
CompositionTarget.Rendering +=
new
EventHandler(CompositionTarget_Rendering);
}
public
void
InitList()
{
bmList =
new
ObservableCollection<BitmapImage>();
for
(
int
i = 1; i < 4; i++)
{
BitmapImage bmImg =
new
BitmapImage(
new
Uri(System.Environment.CurrentDirectory +
"\\"
+ i.ToString() +
".jpg"
));
bmList.Add(bmImg);
}
}
void
CompositionTarget_Rendering(
object
sender, EventArgs e)
{
for
(
int
i = 0; i < bmList.Count; i++)
{
this
.imgViewer.Source = bmList[i];
this
.imgViewer.Width =
this
.imgViewer.Source.Width;
this
.imgViewer.Height =
this
.imgViewer.Source.Height;
}
}
}
}
|
运行程序ImgAniDemo.exe结果还是只显示了最后一幅图片.
回头认真思考一下,可以做一个这样的猜测,WPF是不是做了优化,如果循环去设置Image的Source属性,用户看到的效果只会是最后一张图片呢?
Render是什么意思?不错,Rendering事件被处理的时候,我们是用了一个循环,按着上面的猜测,只会显示最后一张图片便在情理之中了.
聪明的你一定是想到了方法,我们在Rendering事件的时候不去循环了,只指定某一个图片给Source属性不就ok了吗?
修改后的代码:
namespace ImgAniDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<BitmapImage> bmList;
int index = 0; //记录索引
public MainWindow()
{
InitializeComponent();
InitList();
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
}
public void InitList()
{
bmList = new ObservableCollection<BitmapImage>();
for (int i = 1; i < 4; i++)
{
BitmapImage bmImg = new BitmapImage(new Uri(System.Environment.CurrentDirectory + "\\" + i.ToString() + ".jpg"));
bmList.Add(bmImg);
}
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (index < bmList.Count)
{
this.imgViewer.Source = bmList[index];
this.imgViewer.Width = this.imgViewer.Source.Width;
this.imgViewer.Height = this.imgViewer.Source.Height;
index++;
}
else
{
index = 0;
}
}
}
}
这时候我们已经可以看到图片在自动切换了.会不会感觉速度太快了,我想按着一定的速度来控件切换怎么办?
直接上代码了
namespace ImgAniDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<BitmapImage> bmList;
int index = 0; //记录索引
bool isRendering = false;
public MainWindow()
{
InitializeComponent();
InitList();
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
isRendering = true;
System.Threading.Thread.Sleep(1000); //停1秒
}
}
public void InitList()
{
bmList = new ObservableCollection<BitmapImage>();
for (int i = 1; i < 4; i++)
{
BitmapImage bmImg = new BitmapImage(new Uri(System.Environment.CurrentDirectory + "\\" + i.ToString() + ".jpg"));
bmList.Add(bmImg);
}
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (isRendering)
{
if (index < bmList.Count)
{
this.imgViewer.Source = bmList[index];
this.imgViewer.Width = this.imgViewer.Source.Width;
this.imgViewer.Height = this.imgViewer.Source.Height;
index++;
}
else
{
index = 0;
}
isRendering = false;
}
}
}
}
在运行看下效果呢?
呵呵,图片是不是在自动播放呢?
注:以上代码中用到的图片名称分别为1.jpg,2.jpg,3.jpg,且图片放在exe程序一起即可
以上仅是个人想到的一种方法,若有不正确的地方,还请多多指点...