Page 1 of 1

Gif Animation Editing And Processing Program Rate Topic: -----

#1 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 17 November 2011 - 07:44 PM

Gif Animation Editing And Processing Program

Hello!
I had promissed in a former tutorial that I was going to fiddle some more with the code of Rick Van Den Bosch at:

http://bloggingabout...05/10/3830.aspx
to encode gif animations.
Well, I did and this program is what I have to offer you now.

It is the merging of two utilities I created before for extracting frames from a Gif animation and to assemble them again.

Attached Image

With this program you could assemble your own gifs or you could extract the frames of an existing gif to various different image formats and to manipulate them on various ways. After modifying them, you could create a new animation.

The different manipulations you can perform on the images we have:

Resizing: 25%, 33%, 50%, 75%, 150% And 200%
Reverting Movement
Cropping Rectangular Areas
Flipping Right To Left And Left To right
Converting To Gray Scale
Converting To Sepia Tone
Changing Intensity
Making Slide Show
Getting Edges
Deleting Some Frames
Selecting A Range Of Frames
Joining Two Or More Animations

And probably you would like to add your own.

Attached Image

Attached Image

At startup most of the controls and setting are dissabled to avoid accidental errors. These controls are enabled

when you disassemble an animation.

Most of the operations are done accessing the images extracted to disk. I selected the jpeg format, because the indexed ones give problems on further processing, the old history "Graphics can not be created from indexed..."
Besides, to process everything in memory requieres the use of byte arrays on certain instances to store bitmaps on memory streams and can not be used directly without more transformations. I make use of a memory stream to save the image on gif format without accessing the disk, but is a small operation on individual files. A large image, long gif animation take a lot of memory, depending on the amount of frames, that could be few hundreds.

Due to the jpeg selection, my program is set to scan the folder for jpg images, on the option of reading everything to select a new group of files to assemble.

The opening screen has a small animation that you can process, but in order to do so, you have to extract the frames. You could also open another gif file from your computer, and the same applies to it.

I have tried different ideas during the creation of this program, so my code may be redundant and buggy, so please feedback if you note something.

The controls on top and left are contained in frames, so it makes it easy to enable o disable them, enabling and disabling the frame, instead of the individual controls.

Here I will introduce a small warning: When you are playing with this program, checking different option, a large group of files are created in the output directory, so, you might need to check and delete the ones you no longer need.

Ok, here is the description of the different commands:

Form load creates the tooltips and loads the combobox that gives the type of image that is going to result from the extraction of the frames. The default is ".jpg". Also from the beginning you have the default name to use for the extraction files. To this prefix we will append a sequence of numbers, generated from a function that checks for existing filenames in the directory. The default is "Frame" So if there are no jpg files with this name, the first frame will be extracted with the name: Frame0000.jpg, the next one with Frame0001.jpg and so on.

Imports System.Collections.Generic
Imports System.Text
Imports System.IO
Imports System.Collections
Imports System.Collections.Specialized
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System
Imports System.Drawing.Drawing2D
Imports System.Drawing.Image
Imports System.Threading

Public Class BatchCrop
    Dim OriginalGif As Image
    Dim NameToUse As String = "Frame"
    Dim ImgForSepia As Image

    Dim W, H As Integer
    Dim ImageArray, MemPic, WorkAry As New ArrayList
    Dim N As Integer = 0
    Dim Start, Finish As Integer 'For Selecting A Range Of Frames

    Dim InitX As Integer = 0 'Top Left corner of crop
    Dim InitY As Integer = 0
    Dim Pictures As Integer = 0 'Frames Counter
    'bitmap to contain the cropped image
    Dim cropBitmap As Bitmap
    Dim Im As Image 'Splash
    'the position and size to crop the image file
    Dim Xcropping As Integer
    Dim Ycropping As Integer
    Dim CroppingW As Integer
    Dim CroppingH As Integer
    Dim XStretching As Double = 1
    Dim YStretching As Double = 1
    Dim rect As Rectangle
    'create a pen object
    Public Marker As Pen
    Dim NewName As String

    'select a default crop line size
    Public MarkerSize As Integer = 2

    'will contain the dashStyle of the pen
    Public crpStyle As Drawing2D.DashStyle = Drawing2D.DashStyle.Solid

    'set a default crop line color
    Public MarkerColor As Color = Color.Red
    Dim tt As New ToolTip
    Dim WasAnError As Boolean = False

    Private _numberPreviewImages As Integer = 100
    Private _imageSize As Integer = 100 '75

    Dim Position As Integer = 0 'Where Are We On ImageArray When Clicking A Cell

    Private Sub Form1_Load1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        tt.ShowAlways = True
        tt.AutoPopDelay = 3000
        tt.InitialDelay = 1000
        tt.ReshowDelay = 2000
        tt.SetToolTip(ckSlide, "When Checked Assembles Animation" & vbNewLine & "With Long Delay As Slide Show")
        tt.SetToolTip(ckFlip, "Flips Images Around Vertical Axis")
        tt.SetToolTip(ckGhost, "When Checked, Intends To Get The Edges Of The Images," & vbNewLine & "Works 

Together With The Tracklbar Below It." & vbNewLine & "WARNING, It's Very Slow!!!")
        tt.SetToolTip(ckJoin, "Joins Two Or More Animations. Please Check For Dimensions. The Frame Size" & 

vbNewLine & "Is Determined By First Gif And The Others Will Be Padded Or Clipped")
        tt.SetToolTip(ckDark, "Lightens Or Darkens Images")
        tt.SetToolTip(rbNormal, "When Checked Images Mantain Original Color, Except For Ghost")
        tt.SetToolTip(rbGrayScale, "When Checked, Inmages Are Changed To Grayscale")
        tt.SetToolTip(rbSepiaTone, "When Checked, Inmages Are Changed To Sepia Shades")
        tt.SetToolTip(btnExtract, "Click To Extract Frames" & vbNewLine & "And Enable Controls")
        tt.SetToolTip(tbTolerance, "Works Witth CheckBox Above, & vbnewline" & "To Set Tolerance On Edge 

Detection")
        tt.SetToolTip(btnAnimate, "Assembles Images To Gif Animation" & vbNewLine & "Same Size, But With Settings 

Above")
        tt.SetToolTip(btnReverse, "Reverses Motion")
        tt.SetToolTip(btnCrop, "Crops The Rectangular Area" & vbNewLine & "Selected With Mouse On PictureBox1," & 

vbNewLine & "Repeats For All The Other Frames" & vbNewLine & "Assembles Animation")
        tt.SetToolTip(btn75, "Resizes To 75% The Animation")
        tt.SetToolTip(btn50, "Resizes Animation To 50%")
        tt.SetToolTip(btn33, "Resizes Animation To 33%")
        tt.SetToolTip(btn25, "Resizes Animation To 25%")
        tt.SetToolTip(btn150, "Resizes Animation To 150%")
        tt.SetToolTip(btn200, "Resizes Animation To 200%")
        tt.SetToolTip(btnBack, "View Former Frame")
        tt.SetToolTip(btnForward, "View Next Frame")
        tt.SetToolTip(btnStart, "Start Selection For A Subset Of Images")
        tt.SetToolTip(btnEnd, "Last Image Of Subset Selection")
        tt.SetToolTip(btnDelete, "Eliminate This Frame From Animation")
        tt.SetToolTip(lblTour, "Frame Sequence")
        tt.SetToolTip(lbOutput, "Name Of Animation Generated")
        tt.SetToolTip(lblInfo, "Information On Image Cropping")
        tt.SetToolTip(cbType, "Select Extract File Type")
        tt.SetToolTip(tbName, "Type Name Prefix")
        tt.SetToolTip(btnForward, "Call Crop And Resizing Form")
        tt.SetToolTip(btnCrop, "Extract Frames To Image Files")
        tt.SetToolTip(tbName, "Imput Base Name For Your Generated Images")
        tt.SetToolTip(plDoIt, "WARNING! When Playing With These Buttons" & vbNewLine & "Yo Will Generate Several 

Files In Your Output Folder.")
        tt.BackColor = Color.AliceBlue
        ' tt.ForeColor = Color.Red

        cbType.Items.Add(".Bmp")
        cbType.Items.Add(".Gif")
        cbType.Items.Add(".Jpg")
        cbType.Items.Add(".Png")
        cbType.Items.Add(".Tif")
        ImageArray = New ArrayList
        MemPic = New ArrayList
        cbType.SelectedIndex = 2
        OriginalGif = pbSelect.Image

    End Sub



From Here you can load a file or use the animation on the picturebox:

    Private Sub OpenToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

OpenToolStripMenuItem.Click
        Dim f As New OpenFileDialog
        Try
            With f
                .Filter = "Gif Files|*.gif"

                If .ShowDialog = DialogResult.OK Then
                    OriginalGif = Image.FromFile(.FileName)
                    pbPreview.Image = Image.FromFile(.FileName)

                    If ImageArray IsNot Nothing Then
                        If ckJoin.Checked = False Then ImageArray.Clear() 'For joining
                    End If

                    plDoIt.Enabled = False
                    plSelect.Enabled = False
                    N = 0
                    Me.Text = "Gif Processor -> " & .FileName.Substring(.FileName.LastIndexOf("\") + 1)

                End If

            End With

        Catch ex As Exception
            MessageBox.Show(ex.ToString)

        Finally
            If Not f Is Nothing Then
                f.Dispose()
                f = Nothing
            End If
        End Try
        pbPreview.Visible = True
        pbSelect.Visible = False
        pbPreview.BringToFront()
    End Sub



This will put the animation on pbPreview.

The other menu items are "Scan Folder" and "View Directory" (No Naming Convention) and "Exit". The first one will load all the names of the jpg files in the application initial directory. The second will open a Windows Explorer view of the folder. You figure out what exit stands for.

When you have a file loaded, you can select a file format and a default name from the options enabled. you will press the button "Extract Frames", that will enable the other options and extract the frames that form the animation.

    Private Sub btnExtract_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

btnExtract.Click
        Rip(NameToUse)
        pbPreview.Visible = False
        pbSelect.Visible = True
        pbSelect.Image = Image.FromFile(ImageArray(0))
        plDoIt.Enabled = True
        plSelect.Enabled = True
        lblImages.Text = "Frames In Animation: " & ImageArray.Count.ToString
        LoadImages()
    End Sub



That will call the "Rip" and the "LoadImages" subroutines:

    Public Sub Rip(ByVal Prefix As String)
        ImageArray.Clear()
        Dim Picture As Image
        MemPic.Clear()
        Dim MS As MemoryStream
        Dim Buf As Byte()
        Dim TempName As String
        Dim oDimension As New FrameDimension(OriginalGif.FrameDimensionsList(0))
        Dim FrameCount As Integer = OriginalGif.GetFrameCount(oDimension)
        ' Dim MemStr As Bitmap
        For i As Integer = 0 To FrameCount - 1
            OriginalGif.SelectActiveFrame(oDimension, i)
            MS = New MemoryStream
            Using Frames As New Bitmap(OriginalGif)
                TempName = GetNewName(Prefix, ComboBox1.Text)
                ImageArray.Add(TempName)
                Frames.Clone.Save(MS, ImageFormat.Jpeg)
                Buf = MS.ToArray
                Picture = BytesToBitmap(Buf)

                MemPic.Add(Picture)
                MS.SetLength(0)
                Select Case Trim(ComboBox1.Text.ToUpper)
                    Case ".BMP"
                        Frames.Save(GetNewName(Prefix, ComboBox1.Text), ImageFormat.Bmp)
                    Case ".JPG"
                        Frames.Save(GetNewName(Prefix, ComboBox1.Text), ImageFormat.Jpeg)
                    Case ".GIF"
                        Frames.Save(GetNewName(Prefix, ComboBox1.Text), ImageFormat.Gif)
                    Case ".PNG"
                        Frames.Save(GetNewName(Prefix, ComboBox1.Text), ImageFormat.Png)
                    Case ".TIF"
                        Frames.Save(GetNewName(Prefix, ComboBox1.Text), ImageFormat.Tiff)
                End Select

            End Using

        Next


    End Sub




This Will disassemble the loaded animation. Now we have in place our images to work with. The preview picturebox will disapear and the first frame will appear on "pbSelect". It is a still image.

The LoadImages subroutine will create the grid on the datagridview and load all the extracted frames from the animation:

    Private Sub LoadImages()
        Try
            If ImageArray Is Nothing Then
                Return
            End If

            If Me.WindowState = FormWindowState.Minimized Then
                Return
            End If
            Me.Cursor = Cursors.WaitCursor
            dataVImages.Rows.Clear()
            dataVImages.Columns.Clear()

            Dim ColumnsThatFit As Integer = (dataVImages.Width - 10) / (_imageSize + 25)
            Dim numRows As Integer = 0

            Dim ImagesToDisplay As Integer = ImageArray.Count

            numRows = CInt(Math.Ceiling(CDbl(ImageArray.Count) / CDbl(ColumnsThatFit)))

            Dim CellsForPictureNumber As Integer = numRows * ColumnsThatFit
            '  MessageBox.Show(ColumnsThatFit.ToString & "," & numRows.ToString & "," & 

CellsForPictureNumber.ToString)
            ' Dynamically create the columns
            For index As Integer = 0 To ColumnsThatFit - 1
                Dim dataGridViewColumn As New DataGridViewImageColumn()

                dataVImages.Columns.Add(dataGridViewColumn)
                dataVImages.Columns(index).Width = _imageSize + 20
            Next

            ' Create the rows

            For index As Integer = 0 To numRows - 1
                dataVImages.Rows.Add()
                dataVImages.Rows(index).Height = _imageSize + 20
            Next

            Dim columnIndex As Integer = 0
            Dim rowIndex As Integer = 0

            For index As Integer = 0 To (ImagesToDisplay - 1)
                ' Load the image from the file and add to the DataGridView
                Dim image As Image = Helper.ResizeImage(ImageArray(index), _imageSize, _imageSize, False)
                dataVImages.Rows(rowIndex).Cells(columnIndex).Value = image
                dataVImages.Rows(rowIndex).Cells(columnIndex).ToolTipText = "Frame-" & index.ToString & ": " & 

Path.GetFileName(ImageArray(index)) & " " & GetImageWidth(ImageArray(index)) & "x" & GetImageHeight(ImageArray

(index))

                ' Have we reached the end column? if so then start on the next row
                If columnIndex = ColumnsThatFit - 1 Then
                    rowIndex += 1
                    columnIndex = 0
                Else
                    columnIndex += 1
                End If
            Next
            ' Blank the unused cells
            If CellsForPictureNumber > ImagesToDisplay Then
                For index As Integer = 0 To CellsForPictureNumber - ImagesToDisplay - 1
                    Dim dataGridViewCellStyle As New DataGridViewCellStyle()
                    dataGridViewCellStyle.NullValue = Nothing
                    dataGridViewCellStyle.Tag = "BLANK"
                    dataVImages.Rows(rowIndex).Cells(columnIndex + index).Style = dataGridViewCellStyle
                Next
            End If
        Catch ex As Exception
            Console.WriteLine(ex)
        End Try
        Me.Cursor = Cursors.Default
    End Sub



If you press the chevrons left or right you could cycle through the different frames, forward or reverse. The name and number will appear on the label.

    Private Sub btnForward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

btnForward.Click
        'View Jpg Files in Directory
        If ImageArray.Count = 1 Then Exit Sub
        N += 1
        If N > ImageArray.Count - 1 Then
            N = 0
        End If
        If ImageArray(N) IsNot Nothing Then
            pbSelect.Image = Image.FromFile(ImageArray(N))
        End If
        lblTour.Text = ImageArray(N) & vbNewLine & "Image(" & N & ")"
        Hilite()
    End Sub

    Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBack.Click
        'View Jpg Files in Directory
        If ImageArray.Count = 1 Then Exit Sub

        N = N - 1
        If N < 0 Then
            N = ImageArray.Count - 1

        End If
        If ImageArray(N) IsNot Nothing Then
            pbSelect.Image = Image.FromFile(ImageArray(N))
        End If

        lblTour.Text = ImageArray(N) & vbNewLine & "Image(" & N & ")"
        Hilite()
    End Sub



And the corresponding frame will be highlighted on the datagridview.

If you click an image on the datagridview or when it has the focus and you press the arrow up, down, left or right, you would navigate the frames and the picturebox will be updated. Here the selections won't go past the last one or before the first one, but with the chevrons you will start again on the other end if you go past the end or the beginning.
This naavigation is controlled whith these routines make the rows and columns match the ImageArray Index and count:

    Private Sub dataVImages_CellClick(ByVal sender As Object, ByVal e As 

System.Windows.Forms.DataGridViewCellEventArgs) Handles dataVImages.CellClick
        'Select Frame By Using Mouse
        Dim i, j As Integer
        i = e.RowIndex
        j = e.ColumnIndex
        Position = i * dataVImages.Columns.Count + j ' + 1  'index of imagearray
        If Position > ImageArray.Count - 1 Then Exit Sub
        pbSelect.Image = Image.FromFile(ImageArray(Position))
        lblTour.Text = ImageArray(Position) & vbNewLine & "Image(" & Position & ")"
        N = Position
        Refresh()
    End Sub

    Private Sub Hilite()
        Dim rcol, rrow As Integer
        rcol = N Mod dataVImages.Columns.Count
        '  rrow = N / dataVImages.Columns.Count
        rrow = CInt(Math.Ceiling(CDbl(N + 1) / CDbl(dataVImages.Columns.Count))) - 1
        dataVImages.CurrentCell = dataVImages(rcol, rrow)
        dataVImages.BeginEdit(True)

        '   MessageBox.Show("Col " & rcol.ToString & "  Row " & rrow.ToString)
    End Sub

    Private Sub dataVImages_CellEnter(ByVal sender As Object, ByVal e As 

System.Windows.Forms.DataGridViewCellEventArgs) Handles dataVImages.CellEnter
        'Select Frame By Using Arrows
        Dim i, j As Integer
        i = e.RowIndex
        j = e.ColumnIndex
        Position = i * dataVImages.Columns.Count + j ' + 1  'index of imagearray
        If Position > ImageArray.Count - 1 Then Exit Sub
        pbSelect.Image = Image.FromFile(ImageArray(Position))
        lblTour.Text = ImageArray(Position) & vbNewLine & "Image(" & Position & ")"
        N = Position
        Refresh()
    End Sub




If there is an image you don't want on the animation you can press "Delete Frame", this will delete the name of the image shown from the arraylist and adjust the amount of frames. If instead you want a subset of images, you can press "Start Here", move few frames forward and press "Finish Here". This will create a shorter animation starting with the image selected first and finishing with the one showing when pressed the other button. The final frame must be higher than the initial one. The name of the frames is loaded on the work array(WorkAry).

    Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click
        ImageArray.RemoveAt(N)
        LoadImages()
    End Sub

    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        Start = N
    End Sub

    Private Sub btnEnd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnd.Click
        Finish = N
        WorkAry.Clear()
        For i As Integer = Start To Finish
            WorkAry.Add(ImageArray(i))
        Next
        ImageArray.Clear()
        For i As Integer = 0 To WorkAry.Count - 1

            ImageArray.Add(WorkAry(i))
        Next
        N = ImageArray.Count - 1
    End Sub




Now you have set the amount of frames, you could reassemble the animation by pressing "Animate".

This will call the assemble Subroutine withe the option "1"

    Public Sub Assemble(ByVal But As Integer)
        Me.Cursor = Cursors.WaitCursor
        'Variable declaration
        Dim memoryStream As MemoryStream
        Dim binaryWriter As BinaryWriter
        Dim image As Image
        Dim buf1 As [Byte]()
        Dim buf2 As [Byte]()
        Dim buf3 As [Byte]()
        memoryStream = New MemoryStream()
        buf2 = New [Byte](18) {}
        buf3 = New [Byte](7) {}
        buf2(0) = 33
        'extension introducer
        buf2(1) = 255
        'application extension
        buf2(2) = 11
        'size of block
        buf2(3) = 78
        'N
        buf2(4) = 69
        'E
        buf2(5) = 84
        'T
        buf2(6) = 83
        'S
        buf2(7) = 67
        'C
        buf2(8) = 65
        'A
        buf2(9) = 80
        'P
        buf2(10) = 69
        'E
        buf2(11) = 50
        '2
        buf2(12) = 46
        '.
        buf2(13) = 48
        '0
        buf2(14) = 3
        'Size of block
        buf2(15) = 1
        '
        buf2(16) = 0
        '
        buf2(17) = 0
        '
        buf2(18) = 0
        'Block terminator
        buf3(0) = 33
        'Extension introducer
        buf3(1) = 249
        'Graphic control extension
        buf3(2) = 4
        'Size of block
        buf3(3) = 9
        'Flags: reserved, disposal method, user input, transparent color
        buf3(4) = 10 ' 88 
        If ckSlide.Checked = True Then
            'Delay time low byte
            buf3(5) = 1
            'Delay time high byte
            buf3(6) = 0
        Else
            'Delay time low byte
            buf3(5) = 0 '1 
            'Delay time high byte
            buf3(6) = 255
        End If

        'Transparent color index
        buf3(7) = 0
        'Block terminator

        NewName = GetNewName1()
        lbOutput.Text = NewName
        binaryWriter = New BinaryWriter(File.Open(NewName, FileMode.Create))

        For picCount As Integer = 0 To ImageArray.Count - 1 'stringCollection.Count - 1

            Select Case But
                Case 1
                    'Animate
                    image = Bitmap.FromFile(ImageArray(picCount))
                    If ckDark.Checked = True Then image = Dark(image)
                    If rbSepiaTone.Checked = True Then image = Sepia(image)
                    If rbGrayScale.Checked = True Then image = Grayscale(image)
                    If ckGhost.Checked = True Then Ghost(image)
                    If ckFlip.Checked = True Then image.RotateFlip(RotateFlipType.RotateNoneFlipX)
                Case 2
                    If MemPic IsNot Nothing Then
                        image = MemPic(picCount)
                        If ckDark.Checked = True Then image = Dark(image)
                        If rbSepiaTone.Checked = True Then image = Sepia(image)
                        If rbGrayScale.Checked = True Then image = Grayscale(image)
                        If ckGhost.Checked = True Then Ghost(image)
                        If ckFlip.Checked = True Then image.RotateFlip(RotateFlipType.RotateNoneFlipX)
                    Else
                        MessageBox.Show("Please Select Something")
                    End If

            End Select
            image.Save(memoryStream, ImageFormat.Gif)
            buf1 = memoryStream.ToArray()

            If picCount = 0 Then
                'only write these the first time....
                binaryWriter.Write(buf1, 0, 781)
                'Header & global color table
                'Application extension
                binaryWriter.Write(buf2, 0, 19)
            End If
            binaryWriter.Write(buf3, 0, 8)
            'Graphic extension
            binaryWriter.Write(buf1, 781, buf1.Length - 782)
            'Image data
            ' If picCount = stringCollection.Count - 1 Then
            If picCount = ImageArray.Count - 1 Then
                'only write this one the last time....
                'Image terminator
                binaryWriter.Write(";")
            End If


            memoryStream.SetLength(0)
            Progress.Value = 100 * picCount / ImageArray.Count
        Next

        binaryWriter.Close()
        pbSelect.Invalidate()
        memoryStream.Close()
        Me.Cursor = Cursors.Default
        pbSelect.Image = image.FromFile(ImageArray(N))
        pbSelect.Refresh()
        Progress.Value = 0
    End Sub



Here is where the images are converted to gif, and assembled. A new animation will be created with a sequential name:

    Public Function GetNewName1() As String
        Dim i As Integer
        Dim str As String = ""
        For i = 1 To 1000
            If i < 10 Then str = "Animation_00" & i.ToString & ".gif"
            If i > 9 And i < 100 Then str = "Animation_0" & i.ToString & ".gif"
            If i > 99 Then str = "Animation_" & i.ToString & ".gif"
            If i > 900 Then MsgBox("You Have Over 900 Pictures, Please Check And Delete Unnecesary Ones")
            If Not System.IO.File.Exists(str) Then
                Exit For
            End If
        Next
        Return str
    End Function



and the resulting image displayed on pbResult.

That's it! And it was easy.

If you want other types of manipulation you have checkboxes , radio buttons and other command buttons:
Selecting "SlideShow" will make the animation very slow, frame by frame.
"Flip" will make a mirror animation, "Ghost" will get edges of the images, it works in conjunction with a trackbar to get more or les border. This bar appears when the "Ghost" option is selected. Also I provided a Progress bar to show the activity due to the slow process, even with lockbits, because all the images are processed one by one.
There is also the option of "Dark" that changes the intensity of the image. The ghost option works better with dark images. The trackbar that adjusts it appears when the option is selected. When Scrolling any of the two trackbars labels with values will appear and disappear.


    Private Function Ghost(ByVal img) As Image
        Dim countx, county As Integer
        Dim Tol As Integer = TrackBar1.Value '30

        Try
            Dim rect As Rectangle = Rectangle.Round(img.GetBounds(GraphicsUnit.Pixel))
            Dim wid As Integer = img.Width
            Dim hgt As Integer = img.Height
            ' Dim img As New Bitmap(wid, hgt)
            ' img = WorkImg.Clone
            Dim bmpSr As BitmapData = img.LockBits(New Rectangle(0, 0, img.Width, img.Height), 

ImageLockMode.ReadWrite, img.PixelFormat)

            Dim ptrSr As IntPtr = bmpSr.Scan0
            Dim R, G, B, A, A1, A2, A3, A4 As Integer
            R = 2
            G = 1
            B = 0
            A = 3
            Dim bytesSr As Integer = bmpSr.Stride * img.Height
            Dim rgbvaluesSr(bytesSr) As Byte
            System.Runtime.InteropServices.Marshal.Copy(ptrSr, rgbvaluesSr, 0, bytesSr)
            'For i As Integer = 0 To rgbvaluesSr.Length - 4 Step 4
            For county = 0 To img.Height - 2
                If img.PixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb Then
                    For countx = county * bmpSr.Stride To (county + 1) * bmpSr.Stride - 6 Step 3
                        A1 = CInt(rgbvaluesSr(countx + R)) + CInt(rgbvaluesSr(countx + G)) + CInt(rgbvaluesSr

(countx + B)/>)
                        A2 = CInt(rgbvaluesSr(countx + 4 + R)) + CInt(rgbvaluesSr(countx + 4 + G)) + CInt

(rgbvaluesSr(countx + 4 + B)/>)
                        A3 = CInt(rgbvaluesSr(countx + R + bmpSr.Stride)) + CInt(rgbvaluesSr(countx + G + 

bmpSr.Stride)) + CInt(rgbvaluesSr(countx + B + bmpSr.Stride))
                        A4 = CInt(rgbvaluesSr(countx + 4 + R + bmpSr.Stride)) + CInt(rgbvaluesSr(countx + 4 + G + 

bmpSr.Stride)) + CInt(rgbvaluesSr(countx + 4 + B + bmpSr.Stride))

                        'If (A2 - A1) > Tol Or (A3 - A1) > Tol Or (A4 - A1) > Tol Then
                        If Math.Abs(A2 - A1) > Tol Or Math.Abs(A3 - A1) > Tol Or Math.Abs(A4 - A1) > Tol Then

                            ' Comment Next Lines To Get Color Edges

                            rgbvaluesSr(countx + R) = 0
                            rgbvaluesSr(countx + G) = 0
                            rgbvaluesSr(countx + B)/> = 0


                        Else
                            rgbvaluesSr(countx + R) = 255
                            rgbvaluesSr(countx + G) = 255
                            rgbvaluesSr(countx + B)/> = 255

                        End If
                        If countx = (county + 1) * bmpSr.Stride - 6 Then
                            'Dark Line To the Right
                            rgbvaluesSr(countx + R + 4) = 255
                            rgbvaluesSr(countx + G + 4) = 255
                            rgbvaluesSr(countx + B + 4) = 255
                            rgbvaluesSr(countx + R + 8) = 255
                            rgbvaluesSr(countx + G + 8) = 255
                            rgbvaluesSr(countx + B + 8) = 255
                            rgbvaluesSr(countx + R + 12) = 255
                            rgbvaluesSr(countx + G + 12) = 255
                            rgbvaluesSr(countx + B + 12) = 255

                        End If
                    Next countx

                ElseIf img.PixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppArgb Then
                    For countx = county * bmpSr.Stride To (county + 1) * bmpSr.Stride - 8 Step 4
                        A1 = CInt(rgbvaluesSr(countx + R)) + CInt(rgbvaluesSr(countx + G)) + CInt(rgbvaluesSr

(countx + B)/>)
                        A2 = CInt(rgbvaluesSr(countx + 4 + R)) + CInt(rgbvaluesSr(countx + 4 + G)) + CInt

(rgbvaluesSr(countx + 4 + B)/>)
                        A3 = CInt(rgbvaluesSr(countx + R + bmpSr.Stride)) + CInt(rgbvaluesSr(countx + G + 

bmpSr.Stride)) + CInt(rgbvaluesSr(countx + B + bmpSr.Stride))
                        A4 = CInt(rgbvaluesSr(countx + 4 + R + bmpSr.Stride)) + CInt(rgbvaluesSr(countx + 4 + G + 

bmpSr.Stride)) + CInt(rgbvaluesSr(countx + 4 + B + bmpSr.Stride))

                        'If (A2 - A1) > Tol Or (A3 - A1) > Tol Or (A4 - A1) > Tol Then
                        If Math.Abs(A2 - A1) > Tol Or Math.Abs(A3 - A1) > Tol Or Math.Abs(A4 - A1) > Tol Then

                            rgbvaluesSr(countx + A) = 255
                            rgbvaluesSr(countx + R) = 0
                            rgbvaluesSr(countx + G) = 0
                            rgbvaluesSr(countx + B)/> = 0


                        Else
                            rgbvaluesSr(countx + A) = 255
                            rgbvaluesSr(countx + R) = 255
                            rgbvaluesSr(countx + G) = 255
                            rgbvaluesSr(countx + B)/> = 255

                        End If
                        If countx = (county + 1) * bmpSr.Stride - 8 Then
                            'Dark Line To the Right
                            rgbvaluesSr(countx + A + 4) = 255
                            rgbvaluesSr(countx + R + 4) = 255
                            rgbvaluesSr(countx + G + 4) = 255
                            rgbvaluesSr(countx + B + 4) = 255
                            rgbvaluesSr(countx + A + 8) = 255
                            rgbvaluesSr(countx + R + 8) = 255
                            rgbvaluesSr(countx + G + 8) = 255
                            rgbvaluesSr(countx + B + 8) = 255
                            rgbvaluesSr(countx + A + 12) = 255
                            rgbvaluesSr(countx + R + 12) = 255
                            rgbvaluesSr(countx + G + 12) = 255
                            rgbvaluesSr(countx + B + 12) = 255

                        End If
                    Next countx
                Else
                    MessageBox.Show("No Provisions Fot This Format")
                End If


            Next county
            System.Runtime.InteropServices.Marshal.Copy(rgbvaluesSr, 0, ptrSr, bytesSr)

            img.UnlockBits(bmpSr)
        Catch
            MessageBox.Show("Error")
        End Try
        Return img

    End Function




If you select "Gray" or "Sepia" you transform the colors of the images. "Normal" will cancel them.

    Private Function Sepia(ByVal img) As Image
        Dim image_attr As New ImageAttributes
        ' Dim img As Image
        '~29%,57%,14%


        Dim cmy As ColorMatrix = New ColorMatrix(New Single()() _
        { _
        New Single() {0.32645, 0.29305, 0.22633, 0.0, 0.0}, _
        New Single() {0.64149, 0.57594, 0.44491, 0.0, 0.0}, _
        New Single() {0.15752, 0.14144, 0.10929, 0.0, 0.0}, _
        New Single() {0.0, 0.0, 0.0, 1.0, 0.0}, _
        New Single() {0.001, 0.001, 0.001, 0.0, 1.0}})

        Try
            Dim rect As Rectangle = Rectangle.Round(img.GetBounds(GraphicsUnit.Pixel))
            Dim wid As Integer = img.Width
            Dim hgt As Integer = img.Height
            img = ConvertToRGB(img)
            Dim gr As Graphics = Graphics.FromImage(img)
            gr.SmoothingMode = SmoothingMode.HighQuality
            gr.CompositingQuality = CompositingQuality.HighQuality
            gr.InterpolationMode = InterpolationMode.High

            image_attr.SetColorMatrix(cmy)
            gr.DrawImage(img, rect, 0, 0, wid, hgt, GraphicsUnit.Pixel, image_attr)

            Return img
        Catch
            MessageBox.Show("Error")
        End Try
        Return img

    End Function

    Private Function Grayscale(ByVal img As Image) As Image
        Dim image_Attr As New ImageAttributes

        Dim rLum As Single = 0.2225 '0.3086
        Dim gLum As Single = 0.7169 '0.0694
        Dim bLum As Single = 0.0606 '0.0820


        Dim cmy As ColorMatrix = New ColorMatrix(New Single()() _
            { _
            New Single() {rLum, rLum, rLum, 0, 0}, _
            New Single() {gLum, gLum, gLum, 0, 0}, _
            New Single() {bLum, bLum, bLum, 0, 0}, _
            New Single() {0, 0, 0, 1, 0}, _
            New Single() {0, 0, 0, 0, 0}})
        Try
            Dim rect As Rectangle = Rectangle.Round(img.GetBounds(GraphicsUnit.Pixel))
            Dim wid As Integer = img.Width
            Dim hgt As Integer = img.Height
            img = ConvertToRGB(img)
            Dim gr As Graphics = Graphics.FromImage(img)
            gr.SmoothingMode = SmoothingMode.HighQuality
            gr.CompositingQuality = CompositingQuality.HighQuality
            gr.InterpolationMode = InterpolationMode.High

            image_Attr.SetColorMatrix(cmy)
            gr.DrawImage(img, rect, 0, 0, wid, hgt, GraphicsUnit.Pixel, image_Attr)

            Return img
        Catch
            MessageBox.Show("Error")
        End Try
        Return img
    End Function




The button "Reverse" Changes the order of the names in the arraylist, so when assembled they go backwards. This is the same routine "Assemble".

    Private Sub btnReverse_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

btnReverse.Click
        ImageArray.Reverse()
        Assemble(1)
        pbResult.Image = Image.FromFile(NewName)

    End Sub



I found wastefull to save all the animations to disk and then select the ones worth to keep, so I supplied a "Preview" routine that will show one frame roughly the way the animation will be produced:
    Private Sub btnFramePView_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

btnFramePView.Click
        preview()
    End Sub
    Private Sub Preview()
        Dim Im As Bitmap
        Im = pbSelect.Image.Clone
        If ckDark.Checked = True Then Im = Dark(Im)
        If rbSepiaTone.Checked = True Then Im = Sepia(Im)
        If rbGrayScale.Checked = True Then Im = Grayscale(Im)
        If ckGhost.Checked = True Then Ghost(Im)
        If ckFlip.Checked = True Then Im.RotateFlip(RotateFlipType.RotateNoneFlipX)
        pbResult.Image = Im
    End Sub




This is the same procedure on the assemble routine, but with only one frame, the one in the picturebox.


Now. When you left-click and drag the mouse on pbSelect, you will have a rectangle drawn there. At mouseup this rectangle is selected and the areas in that position in all the images is selected. You can select on another frame if the first one is not suitable because has a title or is blank, and the effect will be the same. Now you could press "Crop" and a smaller size image will appear with a section of the original.

     Private Sub btnCrop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCrop.Click
        MemPic.Clear()
        If pbResult.Image IsNot Nothing Then
            Dim TempImage As Image

            For Each N As Object In ImageArray
                W = GetImageWidth(N)
                H = GetImageHeight(N)
                TempImage = CropImage(Image.FromFile(N))
                If WasAnError = True Then
                    MemPic.Clear()
                    WasAnError = False
                    Exit Sub
                Else
                    MemPic.Add(TempImage)
                End If

            Next
            Assemble(2)
        Else
            MessageBox.Show("Select Something, Please")
            Exit Sub
        End If
        pbResult.Image = Image.FromFile(NewName)

    End Sub




Assemble is called with option "2". The information appears on the label above the button.

Note that these animations are also saved to disk.

Most of these options are cummulative, what means that an image can be cropped and sepia, or reverted and double the size:

            Select Case But
                Case 1
                    'Animate
                    image = Bitmap.FromFile(ImageArray(picCount))
                    If ckDark.Checked = True Then image = Dark(image)
                    If rbSepiaTone.Checked = True Then image = Sepia(image)
                    If rbGrayScale.Checked = True Then image = Grayscale(image)
                    If ckGhost.Checked = True Then Ghost(image)
                    If ckFlip.Checked = True Then image.RotateFlip(RotateFlipType.RotateNoneFlipX)
                Case 2
                    If MemPic IsNot Nothing Then
                        image = MemPic(picCount)
                        If ckDark.Checked = True Then image = Dark(image)
                        If rbSepiaTone.Checked = True Then image = Sepia(image)
                        If rbGrayScale.Checked = True Then image = Grayscale(image)
                        If ckGhost.Checked = True Then Ghost(image)
                        If ckFlip.Checked = True Then image.RotateFlip(RotateFlipType.RotateNoneFlipX)
                    Else
                        MessageBox.Show("Please Select Something")
                    End If

            End Select



The "Normal, "Sepia" and "Gray" will exclude each other.

Now the resizing:

All is accomplished with a subroutine where a mathematical operation sets the size of the bitmap:

    Private Sub btnResize_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn50.Click, 

btn33.Click, btn25.Click, btn150.Click, btn75.Click, btn200.Click
        Dim Orig_bmp As Bitmap
        'following code resizes pictures
        MemPic.Clear()
        For NX As Integer = 0 To ImageArray.Count - 1
            W = GetImageWidth(ImageArray(0))
            H = GetImageHeight(ImageArray(0))

            If rbNormal.Checked = True Then
                Orig_bmp = New Bitmap(Image.FromFile(ImageArray(NX)))

            ElseIf rbGrayScale.Checked = True Then
                Orig_bmp = New Bitmap(Grayscale((Image.FromFile(ImageArray(NX)))))

            ElseIf rbSepiaTone.Checked = True Then
                Orig_bmp = New Bitmap(Sepia(Image.FromFile(ImageArray(NX))))
            Else
                ' Orig_bmp = New Bitmap(Dark(Image.FromFile(ImageArray(NX))))

            End If
            Dim x, y As Integer


            If sender Is btn50 Then
                x = W / 2
                y = H / 2
            End If

            If sender Is btn33 Then
                x = W / 3
                y = H / 3
            End If

            If sender Is btn25 Then
                x = W / 4
                y = H / 4
            End If

            If sender Is btn150 Then
                x = W * 3 / 2
                y = H * 3 / 2
            End If
            If sender Is btn75 Then
                x = W * 3 / 4
                y = H * 3 / 4
            End If
            If sender Is btn200 Then
                x = W * 2
                y = H * 2
            End If

            ' Dim Small As New Bitmap(Width, Height)
            Dim Small As New Bitmap(x, y)
            Dim G As Graphics = Graphics.FromImage(Small)

            G.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
            G.DrawImage(Orig_bmp, New Rectangle(0, 0, x, y), New Rectangle(0, 0, Orig_bmp.Width, Orig_bmp.Height), 

GraphicsUnit.Pixel)

            G.Dispose()
            MemPic.Add(Small)

        Next
        Assemble(2)
        pbResult.Image = Image.FromFile(NewName)
        Refresh()
    End Sub



The assemble operation is called with paremeter "2".

It is worth to mention that the numbers on filenames are treated like characters, so to keep the order of the frames you need to take provisions.

The numbers as characters will give this problem: 198,199,2,20,21,22... 29,200,201... For this I use:

    Private Sub Save(ByVal Pic As Image)
        Dim i As Integer
        Dim str As String = ""
        For i = 1 To 1000
            If i < 10 Then str = "Pic_00" & i.ToString & ".gif"
            If i > 9 And i < 100 Then str = "Pic_0" & i.ToString & ".gif"
            If i > 99 Then str = "Pic_" & i.ToString & ".gif"
            If i > 900 Then MsgBox("You Have Over 900 Pictures, Please Check And Delete Unnecesary Ones")
            If Not System.IO.File.Exists(str) Then
                Try
                    Pic.Save(str, System.Drawing.Imaging.ImageFormat.Gif)
                Catch Ex As Exception
                    MsgBox("Could Not Write To Location")
                End Try
                Exit For
            End If
        Next

    End Sub





And other similar subroutines.

If you select "Scan Folder" from the menu, the program will read the jpg files from the folder and put them in the arraylist. If you press animate, you will have a greek salad of images assembled. The result will have the dimension of the first image. The large ones will be truncated and the smaller ones padded with black pixels. You need to select frames if you want to get something usefull from it. You could assemble images obtained somewhere else, or created by you on a paint program. But be careful on the names. I use a renaming rousine to process them.

    Private Sub scan()
        ImageArray.Clear()
        Try
            Dim di As New IO.DirectoryInfo(Environment.CurrentDirectory)
            Dim aryFi As IO.FileInfo() = di.GetFiles("*.jpg")
            Pictures = aryFi.Count
            Dim fi As IO.FileInfo
            For Each fi In aryFi
                File_Rename(fi.Name)
            Next
            Dim aryFi2 As IO.FileInfo() = di.GetFiles("*.jpg")

            For Each fi In aryFi2
                ImageArray.Add(fi.Name) 'Original jpg Files 

            Next

            If ImageArray IsNot Nothing Then
                ImageArray.Sort()
                If ImageArray(0) IsNot Nothing Then
                    pbSelect.Image = Image.FromFile(ImageArray(0))
                    Im = pbSelect.Image.Clone
                    Label2.Text = ImageArray(0)
                End If
            End If
        Catch
            MessageBox.Show("Didn't Find Any Jpg Fles In Directory, Sorry")
        End Try
    End Sub

    Private Sub File_Rename(ByVal OldName As String)
        'Frame3.jpg
        If OldName.Length > 12 Then Exit Sub 'Already processed

        Dim NewFname As String = ""
        Dim Len As Integer
        Dim Ending = OldName.Substring(5)
        Len = Ending.Length
        Try
            Select Case Len

                Case 5
                    NewFname = "Frame000" & Ending
                Case 6
                    NewFname = "Frame00" & Ending
                Case 7
                    NewFname = "Frame0" & Ending
                Case 8
                    NewFname = "Frame" & Ending

            End Select

            My.Computer.FileSystem.RenameFile(OldName, NewFname)
        Catch
            MessageBox.Show("We Got An Error")
        End Try


    End Sub





If you check the option "Join" the arraylist is not cleared, so you will load another animation and the frames will be added. The dimensions of the first ones will determine the size of the frames. If the animations are of different sizes the smaller ones will be padded with black pixels, and the larger ones will be truncated.

One last note: The ConvertTORGB will convert the images to be able to manipulate them as I said before, due to the old history:

"Graphics Objects Can Not Be Created From Indexed..."

    Public Shared Function ConvertToRGB(ByVal original As Bitmap) As Bitmap
        Dim newImage As New Bitmap(original.Width, original.Height, 

System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        newImage.SetResolution(original.HorizontalResolution, original.VerticalResolution)
        Dim g As Graphics = Graphics.FromImage(newImage)
        g.DrawImageUnscaled(original, 0, 0)
        g.Dispose()
        Return newImage
    End Function
  



I think these cover almost everything.

This is the program. Check it and please, and if you can, report bugs.
See the zip included.

Thank you.

Attached File(s)



Is This A Good Question/Topic? 3
  • +

Replies To: Gif Animation Editing And Processing Program

#2 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 03 December 2011 - 05:31 AM

Hello all:
After I uploaded this tutorial I have played a lot with my program and found out that I needed to update the datagridview after deleting frames or selecting a subset of frames, so the datagridview navigation goes properly with the new set. You need to add these two lines of code at the end of "Delete This Frame" and "Finish Here" handlers:

        LoadImages()
        lblImages.Text = "Frames In Animation: " & ImageArray.Count.ToString




Anyway I have a new version that doesn't need to write the frames to disk, but can as an option. It does everything in memory and is notably faster. Also has a little bit different layout and has a "Dark" and "Watermark" subroutines added.

I have to watch my upload quota, so drop me a line to see how we can do.

Thank you.

This post has been edited by ricardosms: 03 December 2011 - 05:40 AM

Was This Post Helpful? 3
  • +
  • -

#3 walataza  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 24-May 07

Posted 04 December 2011 - 07:01 PM

Good to see this, very nnice..thumbs up& good luck..just downloaded..will try and give feed back..cheers
Was This Post Helpful? 0
  • +
  • -

#4 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 05 February 2012 - 03:05 PM

OK! Guys.

I have been fiddling with this program and I have fixed a bug with the frame size. Also I have added few things, like "Watermark", "Frame Delay", More Resize Options, Color Clearing, "Colored Edge" and "Mortar Effect" but....


Attached Image

The more important improvement is that you could process the animation without writting the intermediate files to disk. You can still do it if you desire so, but if you don't you will greatly improve your processing time.
The color clearing is to eliminate certain colors from the frames, the colored edge works with the ghost button, and the mortar effect resembles cast cement.
Check the new zip attached to see the code. A Windows image is used as watermark, but you coud set your own.

I have also moved some controls on the form, here is a view.


Attached Image


This program is not very intuitive, but by playing with it you will find your way.

regards,
ricardosms

Attached File(s)


Was This Post Helpful? 1
  • +
  • -

#5 eyestrain  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 15
  • Joined: 20-March 12

Posted 20 March 2012 - 02:19 AM

wow!
thats great
although some of the codes are hard for me to understand since im noob on this field but id like to try it...
Was This Post Helpful? 0
  • +
  • -

#6 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 20 March 2012 - 02:54 AM

Hello eyestrain,

Thanks for getting back to me. I don't get too much feedback from my posts.

If you indicate me what code needs some extra explanation I will try to clarify it. It is a complex program, but it can be broken down on smaller parts.

Regards,
ricardosms.
Was This Post Helpful? 0
  • +
  • -

#7 TechKid  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 3
  • View blog
  • Posts: 82
  • Joined: 04-September 10

Posted 25 November 2012 - 09:19 AM

This is awesome! Great job! :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1