I am writing an app to read in a very large tif file (15000x10000), then chop it up into 256x256 tiles which get saved as jpegs. I am trying to use the WPF windows.media.imaging objects to do the chopping. The example below runs fine and will extract a number of tiles for me (it just gets multiple copies of the same tile for this example) but the app uses up memory and that memory never gets released. Even forcing a CG.Collect to test this still doesn't free up the memory.
Dim croppedImage As CroppedBitmap
Dim strImagePath As String = "C:\Huge.tif"
Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(strImagePath), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None).Frames(0) 'CreateImage(imageBytes, 0, 0)
Dim enc As JpegBitmapEncoder
Dim stream As FileStream
For i As Integer = 0 To 5000
croppedImage = New CroppedBitmap()
croppedImage.BeginInit()
croppedImage.Source = imageSource
croppedImage.SourceRect = New Int32Rect(0, 0, 256, 256)
croppedImage.EndInit()
enc = New JpegBitmapEncoder()
enc.QualityLevel = 70
enc.Frames.Add(BitmapFrame.Create(croppedImage))
stream = New FileStream("C:\output\" & i & ".jpg", FileMode.Create, FileAccess.Write)
enc.Save(stream)
stream.Close()
enc = Nothing
stream = Nothing
croppedImage.Source = Nothing
croppedImage = Nothing
Next
imageSource = Nothing
Am I missing something fundamental here? How can I ensure that these resources are released correctly?
Thanks
More Information:
The answers provided below definitely help. Thanks for that. I have a another issue to add to this now. I am trying to watermark each tile before it is save by adding the following code:
Dim targetVisual = New DrawingVisual()
Dim targetContext = targetVisual.RenderOpen()
targetContext.DrawImage(croppedImage, New Rect(0, 0, tileWidth, tileHeight))
targetContext.DrawImage(watermarkSource, New Rect(0, 0, 256, 256))
Dim target = New RenderTargetBitmap(tileWidth, tileHeight, 96, 96, PixelFormats.[Default])
targetContext.Close()
target.Render(targetVisual)
Dim targetFrame = BitmapFrame.Create(target)
This is starting to use some serious memory. Running through the large tif uses over 1200MB of memory as reported by task manager. It looks like this memory gets released eventually, but I am slightly concerned that something is not right with the code and is there anyway to stop it consuming all this memory in the first place. Perhaps this is simply down to the issue that Franci discussed?
Andrew
Your large object is definitely promoted to gen 2, after the ~25000 object allocations in the loop. Gen 2 objects are collected only on full collections, which are done rarely, thus your object might sit in memory for a while.
You can try forcing a full collection using GC.Collect(). You can also use the other GC methods to check for the allocated memory before you force the full allocation, then wait for it to finish and then check the allocated memory again to determine if indeed the large object was collected.
Note that even though a full collection might occur, the memory has already been included in the process working set, and it might take a bit for the OS to react to freeing that memory and remove it from the working set. This is important to keep in mind if you are trying to judge if the memory was properly collect by looking at the process working set in Task Manager.
I recreated your code locally and did a small experimentation, as a result I found that if you make the stream local to the for loop and use the enc.Save inside Using stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write) the total private bytes stays approximately at 60 MB (against 80+ MB when not used inside using).
My complete code for your reference:
Dim fileName As String = System.IO.Path.Combine(Environment.CurrentDirectory, "Image1.tif")
Dim outputDir As String = System.IO.Path.Combine(Environment.CurrentDirectory, "Out")
Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(fileName), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None).Frames(0)
Dim enc As JpegBitmapEncoder
Dim croppedImage As CroppedBitmap
For i As Integer = 0 To 4999
croppedImage = New CroppedBitmap()
croppedImage.BeginInit()
croppedImage.Source = imageSource
croppedImage.SourceRect = New Int32Rect(0, 0, 256, 256)
croppedImage.EndInit()
enc = New JpegBitmapEncoder()
enc.QualityLevel = 70
enc.Frames.Add(BitmapFrame.Create(croppedImage))
Using stream As New FileStream(System.IO.Path.Combine(outputDir, i.ToString() & ".jpg"), FileMode.Create, FileAccess.Write)
enc.Save(stream)
End Using
enc = Nothing
croppedImage.Source = Nothing
croppedImage = Nothing
Next
imageSource = Nothing
try creating your TiffBitmaDecoder like so:
Dim imageSource As BitmapSource = TiffBitmapDecoder.Create(New Uri(fileName), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad).Frames(0)
Related
Consider a text file stored in an online location that looks like this:
;aiu;
[MyEditor45]
Name = MyEditor 4.5
URL = http://www.myeditor.com/download/myeditor.msi
Size = 3023788
Description = This is the latest version of MyEditor
Feature = Support for other file types
Feature1 = Support for different encodings
BugFix = Fix bug with file open
BugFix1 = Fix crash when opening large files
BugFix2 = Fix bug with search in file feature
FilePath = %ProgramFiles%\MyEditor\MyEditor.exe
Version = 4.5
Which details information about a possible update to an application which a user could download. I want to load this into a stream reader, parse it and then build up a list of Features, BugFixes etc to display to the end user in a wpf list box.
I have the following piece of code that essentially gets my text file (first extracting its location from a local ini file and loads it into a streamReader. This at least works although I know that there is no error checking at present, I just want to establish the most efficient way to parse this first. One of these files is unlikely to ever exceed more than about 250 - 400 lines of text.
Dim UpdateUrl As String = GetUrl()
Dim client As New WebClient()
Using myStreamReader As New StreamReader(client.OpenRead($"{UpdateUrl}"))
While Not myStreamReader.EndOfStream
Dim line As String = myStreamReader.ReadLine
If line.Contains("=") Then
Dim p As String() = line.Split(New Char() {"="c})
If p(0).Contains("BugFix") Then
MessageBox.Show($" {p(1)}")
End If
End If
End While
End Using
Specifically I'm looking To collate the information about Features, BugFixes and Enhancements. Whilst I could construct what would in effect be a rather messy if statement I feel sure that there must be a more efficient way to do this , possibly involving linq. I'd welcome any suggestions.
I have added the wpf tag on the off chance that someone reading this with more experience of displaying information in wpf listboxes than I have might just spot a way to effectively define the info I'm after in such a way that it could then be easily displayed in a wpf list box in three sections (Features, Enhancements and BugFixes).
Dom, Here is an answer in C#. I will try to convert it to VB.Net momentarily. First, since the file is small, read all of it into a list of strings. Then select the strings that contain an "=" and parse them into data items that can be used. This code will return a set of data items that you can then display as you like. If you have LinqPad, you can test thecode below, or I have the code here: dotnetfiddle
Here is the VB.Net version: VB.Net dotnetfiddle
Imports System
Imports System.Collections.Generic
Imports System.Linq
Public Class Program
Public Sub Main()
Dim fileContent As List(Of String) = GetFileContent()
Dim dataItems = fileContent.Where(Function(c) c.Contains("=")).[Select](Function(c) GetDataItem(c))
dataItems.Dump()
End Sub
Public Function GetFileContent() As List(Of String)
Dim contentList As New List(Of String)()
contentList.Add("sb.app; aiu;")
contentList.Add("")
contentList.Add("[MyEditor45]")
contentList.Add("Name = MyEditor 4.5")
contentList.Add("URL = http://www.myeditor.com/download/myeditor.msi")
contentList.Add("Size = 3023788")
contentList.Add("Description = This is the latest version of MyEditor")
contentList.Add("Feature = Support for other file types")
contentList.Add("Feature1 = Support for different encodings")
contentList.Add("BugFix = Fix bug with file open")
contentList.Add("BugFix1 = Fix crash when opening large files")
contentList.Add("BugFix2 = Fix bug with search in file feature")
contentList.Add("FilePath = % ProgramFiles %\MyEditor\MyEditor.exe")
contentList.Add("Version = 4.5")
Return contentList
End Function
Public Function GetDataItem(value As String) As DataItem
Dim parts = value.Split("=", 2, StringSplitOptions.None)
Dim dataItem = New DataItem()
dataItem.DataType = parts(0).Trim()
dataItem.Data = parts(1).Trim()
Return dataItem
End Function
End Class
Public Class DataItem
Public DataType As String
Public Data As String
End Class
Or, in C#:
void Main()
{
List<string> fileContent = GetFileContent();
var dataItems = fileContent.Where(c => c.Contains("="))
.Select(c => GetDataItem(c));
dataItems.Dump();
}
public List<string> GetFileContent()
{
List<string> contentList = new List<string>();
contentList.Add("sb.app; aiu;");
contentList.Add("");
contentList.Add("[MyEditor45]");
contentList.Add("Name = MyEditor 4.5");
contentList.Add("URL = http://www.myeditor.com/download/myeditor.msi");
contentList.Add("Size = 3023788");
contentList.Add("Description = This is the latest version of MyEditor");
contentList.Add("Feature = Support for other file types");
contentList.Add("Feature1 = Support for different encodings");
contentList.Add("BugFix = Fix bug with file open");
contentList.Add("BugFix1 = Fix crash when opening large files");
contentList.Add("BugFix2 = Fix bug with search in file feature");
contentList.Add("FilePath = % ProgramFiles %\\MyEditor\\MyEditor.exe");
contentList.Add("Version = 4.5");
return contentList;
}
public DataItem GetDataItem(string value)
{
var parts = value.Split('=');
var dataItem = new DataItem()
{
DataType = parts[0],
Data = parts[1]
};
return dataItem;
}
public class DataItem
{
public string DataType;
public string Data;
}
The given answer only focuses on the first part, converting the data to a structure that can be shaped for display. But I think you main question is how to do the actual shaping.
I used a somewhat different way to collect the file data, using Microsoft.VisualBasic.FileIO.TextFieldParser because I think that makes coding just al little bit easier:
Iterator Function GetTwoItemLines(fileName As String, delimiter As String) _
As IEnumerable(Of Tuple(Of String, String))
Using tfp = New TextFieldParser(fileName)
tfp.TextFieldType = FieldType.Delimited
tfp.Delimiters = {delimiter}
tfp.HasFieldsEnclosedInQuotes = False
tfp.TrimWhiteSpace = False
While Not tfp.EndOfData
Dim arr = tfp.ReadFields()
If arr.Length >= 2 Then
Yield Tuple.Create(arr(0).Trim(), String.Join(delimiter, arr.Skip(1)).Trim())
End If
End While
End Using
End Function
Effectively the same thing happens as in your code, but taking into account Andrew's keen caution about data loss: a line is split by = characters, but the second field of a line consists of all parts after the first part with the delimiter re-inserted: String.Join(delimiter, arr.Skip(1)).Trim().
You can use this function as follows:
Dim fileContent = GetTwoItemLines(file, "=")
For display, I think the best approach (most efficient in terms of lines of code) is to group the lines by their first items, removing the numeric part at the end:
Dim grouping = fileContent.GroupBy(Function(c) c.Item1.TrimEnd("0123456789".ToCharArray())) _
.Where(Function(k) k.Key = "Feature" OrElse k.Key = "BugFix" OrElse k.Key = "Enhancement")
Here's a Linqpad dump (in which I took the liberty to change one item a bit to demonstrate the correct dealing with multiple = characters:
You could do it with Regular Expressions:
Imports System.Text.RegularExpressions
Private Function InfoReader(ByVal sourceText As String) As List(Of Dictionary(Of String, String()))
'1) make array of fragments for each product info
Dim products = Regex.Split(sourceText, "(?=\[\s*\w+\s*])")
'2) declare variables needed ahead
Dim productProperties As Dictionary(Of String, String)
Dim propertyNames As String()
Dim productGroupedProperties As Dictionary(Of String, String())
Dim result As New List(Of Dictionary(Of String, String()))
'2) iterate along fragments
For Each product In products
'3) work only in significant fragments ([Product]...)
If Regex.IsMatch(product, "\A\[\s*\w+\s*]") Then
'4) make array of property lines and extract dictionary of property/description
productProperties = Regex.Split(product, "(?=^\w+\s*=)", RegexOptions.Multiline).Where(
Function(s) s.Contains("="c)
).ToDictionary(
Function(s) Regex.Match(s, "^\w+(?=\s*=)").Value,
Function(s) Regex.Match(s, "(?<==\s+).*(?=\s+)").Value)
'5) extract distinct property names, ignoring numbered repetitions
propertyNames = productProperties.Keys.Select(Function(s) s.TrimEnd("0123456789".ToCharArray)).Distinct.ToArray
'6) make dictionary of distinctProperty/Array(Of String){description, description1, ...}
productGroupedProperties = propertyNames.ToDictionary(
Function(s) s,
Function(s) productProperties.Where(
Function(kvp) kvp.Key.StartsWith(s)
).Select(
Function(kvp) kvp.Value).ToArray)
'7) enlist dictionary to result
result.Add(productGroupedProperties)
End If
Next
Return result
End Function
I have using previous version of npgsql (2.0.7), it works fine.
Now I have upgrade to npgsql 3.0.5
and with new rewrite of copy method of 3.0.5
I have to change code for geometry
I try to using
while
{
var line = new NpgsqlLine(122149.006850, 483672.683450, 122156.366150);
writer.Write<NpgsqlLine>(line, NpgsqlDbType.Line)
}
writer.Close();
at debug mode : in the loop is ok, but when writer.Close()
Error!! with this message
XX000: Invalid endian flag value encountered.
Need help on this, any suggestion are highly appreciate.
Thanks in advance.
Hope it helps you as I was having the same problem.
In my case, it has been resolved by taking away my "using" directive :
instead of:
using (var writer = conn.BeginBinaryImport("sql copy from stdin command"){ ///writer.StartRow(); writer.Write(...); writer.Write();}
Take away using :
var writer = conn.BeginBinaryImport("sql copy from stdin command");
then in while statement:
while(condition){writer.StartRow(); writer.Write(...); writer.Write();}
I think the problem comes from writer.Dispose() managed by using directive : In fact, without using directive, if you call your writer.Dispose(), the same Npgsql exception is raised.
Good luck!
Above does not work - in 4.05 you have to do
dbimporter.Complete()
but this works for all geometry types loading into a geometry column in postgres
using
Dim conn As New NpgsqlConnection
Dim ct As String = "COPY " + datatab + "( " + datacolumns + ") FROM STDIN (FORMAT BINARY)"
Dim dbimporter = conn.BeginBinaryImport(ct)
when you need to import geometry data use this, you will need sqlserverdatatypes dll
dim wkt1 as string ="POINT(7 7)" - any wkt geometry representation
Dim udtText1 As New System.Data.SqlTypes.SqlChars(wkt1)
Dim sqlGeometry11 As Microsoft.SqlServer.Types.SqlGeometry = Microsoft.SqlServer.Types.SqlGeometry.STGeomFromText(udtText1, srid)
Dim ms1 As New MemoryStream()
Dim bw1 As New BinaryWriter(ms1)
Dim WKB1() As Byte = sqlGeometry11.STAsBinary().Buffer
bw1.Write(WKB1)
dbimporter.Write(WKB1, NpgsqlTypes.NpgsqlDbType.Bytea)
Currently when I load my program Bing Maps will only load the first pushpin onto the map, for my example I have 4 pushpins which should be displayed when the application is loaded, what additional code would I add in order to make it complete all four.
In addition I have a couple of questions if you don't mind answering
Do I need to use a loop for each location?
Do I have to give each one an individual name? (Pin)
Can I link a access database instead of copying the locations across?
Is it possible to hide or remove pushpins when a button is clicked?
Dim Pin = New Microsoft.Maps.MapControl.WPF.Pushpin()
UserControl11.BingMap.Children.Add(Pin)
Pin.Location = (New Location(55.852663, -2.3889276))
Pin.Location = (New Location(55.956023, -3.1607265))
Pin.Location = (New Location(54.840279, -3.2886766))
Pin.Location = (New Location(52.819511, -1.8851815))
If it is just these 4 pins you want to create, then you can use the following code:
Dim Pin = New Microsoft.Maps.MapControl.WPF.Pushpin()
Pin.Location = (New Location(55.852663, -2.3889276))
UserControl11.BingMap.Children.Add(Pin)
Dim Pin2 = New Microsoft.Maps.MapControl.WPF.Pushpin()
Pin2.Location = (New Location(55.956023, -3.1607265))
UserControl11.BingMap.Children.Add(Pin2)
Dim Pin3 = New Microsoft.Maps.MapControl.WPF.Pushpin()
Pin3.Location = (New Location(54.840279, -3.2886766))
UserControl11.BingMap.Children.Add(Pin3)
Dim Pin4 = New Microsoft.Maps.MapControl.WPF.Pushpin()
Pin4.Location = (New Location(52.819511, -1.8851815))
UserControl11.BingMap.Children.Add(Pin4)
Alternatively, if your location data is changing or you have an array/list of location information you can loop through, create pushpins and add them to the map like this:
Dim myLocations(4) As Location
myLocations(0) = New Location(55.852663, -2.3889276)
myLocations(1) = New Location(55.956023, -3.1607265)
myLocations(2) = New Location(54.840279, -3.2886766)
myLocations(3) = New Location(52.819511, -1.8851815)
For index = 0 to myLocations.Length - 1
Dim Pin = New Microsoft.Maps.MapControl.WPF.Pushpin()
Pin.Location = myLocations(index)
UserControl11.BingMap.Children.Add(Pin)
Next
I'm trying to rough out a concept here and I have an odd problem. For your amusement...
In summary:
What I want to do is generate an image in C# code, insert it into a varbinary(max) field of a SQL 2012 db, and then display it in an SSRS report. (Yes, I have remembered to delete the rdl.data cache after every test.) The image is simply a circle with a highlighted arc segment. This db table is based on the one in http://www.kodyaz.com/articles/display-database-image-using-sql-server-2008-reporting-services.aspx The only thing I've changed is to add a primary key. I've created a DataSet (xsd) file and dragged the table onto it from the Server Explorer.
The weird part is that, although I'm inserting records successfully and the fname fields is being inserted correctly, as far as I can tell, the binary field is always a duplicate of the first one added. I should also mention when I initially tested the main graphics code by writing to a local disk file, the image always came out correctly.
At first I thought it might be a problem doing it from a web service I have but I get the exact same behavior running the code in a local test web app. Also all the code is local to the method. Nothing remotely persistent. Admittedly I only dabble in graphics. This is hacked together from various other references. Here's the heart of the code. I have it in a try/catch and it's not giving any exceptions.
Bitmap x;
Pen p = new Pen(System.Drawing.Color.Red, 5);
Pen p2 = new Pen(System.Drawing.Color.Pink, 5);
SolidBrush b;
RectangleF rf;
Graphics gr;
p = new Pen(System.Drawing.Color.Red, iThick);
p2 = new Pen(System.Drawing.Color.Pink, iThick);
x = new Bitmap(500, 500);
rf = new RectangleF(0, 0, x.Width, x.Height);
gr = Graphics.FromImage(x);
b = new SolidBrush(System.Drawing.Color.White);
gr.FillRectangle(b, rf);
rf = new RectangleF(p.Width, p.Width, x.Width - p.Width * 2, x.Height - p.Width * 2);
gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
gr.DrawEllipse(p2, rf);
gr.DrawArc(p, rf, fStartAngle, fSweepAngle);
byte[] bytes;
using (System.IO.MemoryStream sampleStream = new System.IO.MemoryStream())
{
x.Save(sampleStream, System.Drawing.Imaging.ImageFormat.Bmp);
bytes = sampleStream.ToArray();
dsCountryTableAdapters.DBFiles1TableAdapter ta = new dsCountryTableAdapters.DBFiles1TableAdapter();
ta.Insert(sImageName, bytes);
}
Whatever the problem originally was, changing the insert method from a TableAdapter.Insert to a SQLConnection/SQLCommand setup cured it. (No change to the data table structure.) Still curious why the TA didn't work.
using (SqlCommand cmd = new SqlCommand("INSERT INTO DBFiles(fname, [file]) VALUES (#fname, #file)", conn))
{
using (System.IO.MemoryStream sampleStream = new System.IO.MemoryStream())
{
x.Save(sampleStream, System.Drawing.Imaging.ImageFormat.Bmp);
bytes = sampleStream.ToArray();
}
cmd.Parameters.Add("#fname", SqlDbType.NVarChar, 1000).Value = sImageName;
cmd.Parameters.Add("#file", SqlDbType.VarBinary, -1).Value = bytes;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
Is there any easy way of adding ImageSources to a stack and create a video from it?
I already did such a class. I only have to submit my "ImageInfo" which is a system.DrawingBitmap. This can be created easy by using the following code:
Public Function WpfBitmapSourceToBitmap(ByVal source As BitmapSource) As System.Drawing.Bitmap
If source Is Nothing Then Return Nothing
Dim bmp As New System.Drawing.Bitmap(source.PixelWidth, source.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
Dim data As System.Drawing.Imaging.BitmapData = bmp.LockBits(New System.Drawing.Rectangle(System.Drawing.Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.[WriteOnly], System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
source.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride)
bmp.UnlockBits(data)
Return bmp
End Function
Then I did a AviClass to add frames to it and store it as a AVI file with preselected Codec (for example XVid MPEG4)
Public Class clsAviWriter
Inherits MAINInterface.TB.Imaging.Pia7.clsDspTemplate
Private cAvi As AviReaderWriter.AviFile.AviManager
Private AviStream As AviReaderWriter.AviFile.VideoStream
Private AudioStream As AviReaderWriter.AviFile.AudioStream
Private cFps As clsTbQueue
Private OldFpsDate As Date = Now
''' <summary>
''' The image object to paint graphical objects on it
''' </summary>
''' <value>descriptor of the image</value>
Public Overrides Property ImageInfo() As MAINInterface.TB.Imaging.Pia7.clsImageInfo
Get
Return Me._ImageInfo
End Get
Set(ByVal value As MAINInterface.TB.Imaging.Pia7.clsImageInfo)
Me._ImageInfo = value
Call WriteFrame()
Call Me.OnPropertyChanged(Me.Guid)
End Set
End Property
Private Sub WriteFrame()
Dim D As Date = Now
Dim Fps As Single
Me.cFps.Values.Add(D.Subtract(Me.OldFpsDate).Ticks)
Me.OldFpsDate = D
Me.cFps.Trim()
Fps = 1000 / New TimeSpan(Me.cFps.Average).TotalMilliseconds
Me.cFps.BufferSize = TB.Math.myTrim(Fps * 1, 1, 1000)
If Me.AviStream IsNot Nothing Then
Me.AviStream.AddFrame(Me._ImageInfo.Image.Clone)
End If
End Sub
Public Sub New()
Me.ClassDescription = "Write images into an avi file"
Me.cFps = New clsTbQueue(10)
End Sub
Private Sub InitializeAvi()
Dim W As String
Dim Fps As Single
Dim di As New IO.DirectoryInfo(TB.SystemMain.AppPath & "Avi\")
TB.FileSystem.CreateDirectories(di)
W = IO.Path.Combine(di.FullName, "Record_" & Now.Ticks.ToString("0") & ".avi")
Me.cAvi = New AviReaderWriter.AviFile.AviManager(W, False)
Dim Opts As New AviReaderWriter.AviFile.Avi.AVICOMPRESSOPTIONS
Opts.fccType = 0
Opts.fccHandler = 1684633208
Opts.dwKeyFrameEvery = 0
Opts.dwQuality = 0 '0 ... 10000
Opts.dwFlags = 8 'AVICOMRPESSF_KEYFRAMES = 4
Opts.dwBytesPerSecond = 0
Opts.lpFormat = 0
Opts.lpParms = New IntPtr(0)
Opts.cbParms = 3532
Opts.dwInterleaveEvery = 0
Fps = 1000 / New TimeSpan(Me.cFps.Average).TotalMilliseconds
'Dim bm1 As Bitmap
'bm1 = TB.Imaging.CreateReScaledImage(Me.pic.Image, New Size(Me.pic.Image.Width, Me.pic.Image.Height), False)
Me.AviStream = cAvi.AddVideoStream(Opts, Math.Floor(TB.Math.myTrim(Fps, 1, 50)), Me._ImageInfo.Image.Clone)
End Sub
Public Overrides Property Run() As Boolean
Get
Return Me._Run
End Get
Set(ByVal value As Boolean)
If Me._Run <> value Then
Me._Run = value
If Me._Run = True Then
Call InitializeAvi()
Else
If Me.cAvi IsNot Nothing Then
Me.cAvi.Close()
Me.cAvi = Nothing
Me.AviStream = Nothing
End If
End If
Call Me.OnPropertyChanged(Me.Guid)
End If
End Set
End Property
End Class
For more codes look here: http://www.wischik.com/lu/programmer/avi_utils.html and MSDN or http://www.codeproject.com/KB/audio-video/avigenerator.aspx
I've posted the sourcecode to show how such a sequence can looks like (code above need some more references which are not public available). You can see that you just need to initialize, add frames, store the FPS value and safe it to harddisk.
Also if wanted, you can search for DirectShow to see how all works.
You can use
http://joshsmithonwpf.wordpress.com/2008/04/23/good-old-fashion-image-animations-in-wpf/
as an example. Afterwards you can use a screen capture program like snagit or microsoft expression encoder pro to capture it as a video
Josh Smith's blog pointed by Raj here (http://joshsmithonwpf.wordpress.com/2008/04/23/good-old-fashion-image-animations-in-wpf/) is a good example of showing images from a folder in the WPF app.
Once this is working you can look at Saveen Reddy's blog to convert app to video
http://blogs.msdn.com/b/saveenr/archive/2008/09/22/wpf-xaml-saving-an-animation-as-an-avi-video-file.aspx
Use this library avifilewrapper search for the sample code on how to create an avi from bitmaps. This article explains how you can render your visuals to bitmaps. I don't think it will get any easier than that.
Since WPF does not include video encoding libraries, you'll need to lean on an external one to do the encoding. This blog post describes how you can use Windows Media Encoder to do so. Alternatively, you could bundle something like mencoder with your app and start it as an external process that you control and monitor from your app.