DreamDancer

Differential Image Generator

Dec 20th, 2014
218
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ' This is the Differential Image Generator.
  2. ' The purpose of the subroutine is to remove common pixels between the base image
  3. ' and any successively numbered images that follow it.
  4. '
  5. ' System was designed around the fact that when I make animation frames with Blender
  6. ' I generate them in a sequencial manner, eg: name-1001, name-1002, etc.
  7. '
  8. ' The end result is a generation of smaller memory model images that GIF animation
  9. ' can take advantage of because there's more "white space" for the RLL encoding to compress.
  10. '
  11. ' For example, taking the following base image:
  12. ' http://i19.photobucket.com/albums/b184/ddancer/renders/blink-000.gif 10,346 bytes
  13. ' and the following frame:
  14. ' http://i19.photobucket.com/albums/b184/ddancer/renders/blink-001.gif 11,899 bytes
  15. ' When run though this subroutine, generates the following image:
  16. ' http://i19.photobucket.com/albums/b184/ddancer/renders/blink-101.gif 3,699 bytes
  17. ' for a saving of about 75% on file size.
  18. '
  19. ' To fully appreciate the savings, consider that the following version:
  20. ' http://i19.photobucket.com/albums/b184/ddancer/renders/furre-crossing1.gif 353,472 bytes
  21. ' with 34 frames, when redone using this subroutine, helped generate:
  22. ' http://i19.photobucket.com/albums/b184/ddancer/renders/furre-crossing2.gif 178,699 bytes
  23. ' with 44 frames.
  24. '
  25. ' Further size reduction can be done by trimming each image and using the position setting
  26. ' for each frame of the animation to move the smaller image over the base image.
  27. '
  28. ' This subroutine, and another one that does trimming was coded for use in
  29. ' making animated images in Furcadia patch files, the Furcadia animation system
  30. ' permits one to designate one image as the background image and another one for the foregound.
  31. '
  32. ' Because the patch files can use PNG encoding for the images, the net result is the same,
  33. ' the animation takes up less space in the file than if you was to use full size frames
  34. ' for each image.
  35. '
  36. ' As an example, the above blink-001 as a PNG is 23,383 bytes in size, when I generate
  37. ' a differential image from it, the results is an image that's only 6,097 bytes in size.
  38. '
  39. ' The savings is about the same, though the file is bigger, but that's because in this case,
  40. ' the images are in 32bit color instead of being an 8bit palette image.
  41. '
  42. ' And again, more savings can be achieved by trimming the results and using the positioning
  43. ' settings for each frame to overlay the animation over the base image.
  44. '
  45. ' The above image, as a PNG, when trimmed, results in a size of 3,955 bytes.
  46.  
  47. ' Before I present the actual subroutine itself, I need to show two functions
  48. ' used by the subroutine to aid it in doing it's magic.
  49.  
  50. Function GetFileNameParts(BaseFileName As String, _
  51.     ByRef BaseFolder As String, ByRef BaseName As String, ByRef RootIndex As String) As String
  52. Dim FiDx As Long, SiDx As Long, fileExt As String
  53.  
  54. ' function walks backwards through the passed BaseFileName and extracts 4 parts from it
  55. ' because i wanted 4 items from the function, i needed to pass three parameters BYREF
  56. '
  57. ' function extracts the file extension, the numeric index, the base filename, and the path
  58. ' from the passed filename, it returns the extension
  59.  
  60.   ' extract the file extension from the name
  61.  For FiDx = Len(BaseFileName) To 1 Step -1
  62.     If (Mid$(BaseFileName, FiDx, 1) = ".") Then
  63.       SiDx = FiDx - 1
  64.       Exit For
  65.     End If
  66.     fileExt = Mid$(BaseFileName, FiDx, 1) & fileExt
  67.   Next FiDx
  68.   fileExt = "." & fileExt
  69.  
  70.   ' extract the numeric index from the filename
  71.  ' note we are starting at the first character past the "."
  72.  ' going backwarks because the above bumps it past the "."
  73.  For FiDx = SiDx To 1 Step -1
  74.     If (InStr(1, "1234567890", Mid$(BaseFileName, FiDx, 1), vbTextCompare) = 0) Then
  75.       SiDx = FiDx
  76.       Exit For
  77.     End If
  78.     RootIndex = Mid$(BaseFileName, FiDx, 1) & RootIndex
  79.   Next FiDx
  80.  
  81.   ' extract the remainder of the filename till the first backslash
  82.  ' here, we continue from the point we left off from above
  83.  For FiDx = SiDx To 1 Step -1
  84.     If (Mid$(BaseFileName, FiDx, 1) = "\") Then
  85.       Exit For
  86.     End If
  87.     BaseName = Mid$(BaseFileName, FiDx, 1) & BaseName
  88.   Next FiDx
  89.  
  90.   ' construct the BaseName and remove it from the passed filename to make BaseFolder
  91.  BaseName = BaseName & RootIndex & fileExt
  92.   BaseFolder = Replace$(BaseFileName, BaseName, "")
  93.  
  94.   ' return the extension
  95.  GetFileNameParts = fileExt
  96.  
  97. End Function
  98.  
  99.  
  100. Function GatherFileNames(BasePath As String, BaseName As String, _
  101.     fileExt As String, RootIndex As String) As String()
  102. Dim FSO As Object, FSI As Object, FSN As Object
  103. Dim FileNames() As String, FiDx As Long, ThisName As String
  104.  
  105. ' function is designed to return a filtered set of filenames based on a
  106. ' loose match system without going through wildcards
  107.  
  108. ' only return files of fileExt type
  109. '' only if they don't match BaseName
  110. ''' only if they are part of RootIndex group
  111. ' see below for explaination of RootIndex
  112.  
  113.   Set FSO = CreateObject("Scripting.FileSystemObject")
  114.   Set FSI = FSO.GetFolder(BasePath)
  115.  
  116.   FiDx = 0
  117.   ReDim FileNames(FiDx)
  118.   For Each FSN In FSI.Files
  119.     ThisName = FSN.Name
  120.     If (InStr(1, ThisName, fileExt, vbTextCompare) > 0) Then
  121.       If (InStr(1, ThisName, BaseName, vbTextCompare) = 0) Then
  122.         If (InStr(1, ThisName, RootIndex, vbTextCompare) > 0) Then
  123.           ReDim Preserve FileNames(FiDx)
  124.           FileNames(FiDx) = ThisName
  125.           FiDx = FiDx + 1
  126.         End If
  127.       End If
  128.     End If
  129.   Next
  130.  
  131.   ' cleanup on asile 6
  132.  Set FSN = Nothing
  133.   Set FSI = Nothing
  134.   Set FSO = Nothing
  135.  
  136.   GatherFileNames = FileNames()
  137. End Function
  138.  
  139. ' The Subroutine
  140. ' Function calls are based on using the FreeImage DLL image handling library.
  141. ' This can be adapted to using other library's with minimal adjustments.
  142.  
  143. Sub ExtractDifferences(BaseFileName As String)
  144. Dim baseDib As Long, compareDib As Long, targetDib As Long
  145. Dim fileFormat As Long
  146. Dim Results As Boolean, winBlt As Boolean
  147. Dim BaseFolder As String, BaseName As String, fileExt As String
  148. Dim RootIndex As String, NewIndex As String, NewName As String
  149. Dim FileNames() As String, FiDx As Long
  150. Dim iWidth As Long, iHeight As Long
  151. Dim hIndex As Long, wIndex As Long, uIndex As Long
  152. Dim sourcePixel As RGBQUAD, comparePixel As RGBQUAD, BackColor As RGBQUAD
  153.  
  154.   ' load the base image into memory
  155.  baseDib = fn_LoadImageFile(BaseFileName, False)
  156.  
  157.   ' fetch the file format based on the image file's data
  158.  ' this is used when saving to make the results the same type of image
  159.  fileFormat = dll_GetFIFFromFilename(BaseFileName)
  160.  
  161.   ' get the various parts of the filename that we need for generation
  162.  fileExt = GetFileNameParts(BaseFileName, BaseFolder, BaseName, RootIndex)
  163.  
  164.   ' recycle NewName for constructing our filename gathering filter
  165.  NewName = Replace$(BaseName, RootIndex, "")
  166.   NewName = Replace$(NewName, fileExt, "")
  167.  
  168.   ' RootIndex is the base image from which the differential images are generated from
  169.  RootIndex = NewName & Left$(RootIndex, 1)
  170.  
  171.   ' NewIndex is the offset for the differential images to be generated
  172.  NewIndex = NewName & Format$(Val(Left$(RootIndex, 1)) + 1)
  173.  
  174.   ' result examples:
  175.  '' if BaseFileName = "fireplace-1001.png"
  176.  '' RootIndex = "fireplace-1"
  177.  '' NewIndex = "fireplace-2"
  178.  
  179.   ' fetch our list of names, pass BaseName to exclude it from the returned list
  180.  FileNames = GatherFileNames(BaseFolder, BaseName, fileExt, RootIndex)
  181.  
  182.  
  183.   ' we need the width, height, and one pixel from base image for background
  184.  
  185.   ' *** FREEIMAGE ACCESS'S IMAGE PIXELS UPSIDE DOWN ***
  186.  ' means that iHeight-1 = Row Zero
  187.  ' WE ALSO ASSUME THAT THIS IS TO BE USED FOR BACKGROUND COLOR INCLUDING TRANSPARENCY IF ANY
  188.  
  189.   iWidth = dll_GetWidth(baseDib)
  190.   iHeight = dll_GetHeight(baseDib)
  191.   Results = fn_GetPixelColor(baseDib, iWidth - 1, iHeight - 1, BackColor)
  192.  
  193.   For FiDx = 0 To UBound(FileNames)
  194.  
  195.     ' sanity check, make sure we got back a flock of images
  196.    If (Len(FileNames(FiDx)) > 0) Then
  197.  
  198.       ' fetch our image to compare base with
  199.      compareDib = fn_LoadImageFile(BaseFolder & FileNames(FiDx), False)
  200.  
  201.       ' generate a copy
  202.      targetDib = dll_Clone(compareDib)
  203.  
  204.       ' loop through the images, mask out any matching pixels
  205.      ' optimization: convert or code in pixel fetch to a 32bit value
  206.      For hIndex = 0 To iWidth - 1
  207.         For wIndex = 0 To iHeight - 1
  208.           Results = fn_GetPixelColor(baseDib, hIndex, wIndex, sourcePixel)
  209.           Results = fn_GetPixelColor(compareDib, hIndex, wIndex, comparePixel)
  210.           If ((sourcePixel.rgbBlue = comparePixel.rgbBlue) _
  211.             And (sourcePixel.rgbGreen = comparePixel.rgbGreen) _
  212.             And (sourcePixel.rgbRed = comparePixel.rgbRed)) Then
  213.  
  214.             Results = fn_SetPixelColor(targetDib, hIndex, wIndex, BackColor)
  215.  
  216.           End If
  217.         Next wIndex
  218.       Next hIndex
  219.  
  220.       ' generate a new name for the results and save it
  221.      ' this is done to prevent trashing original source images
  222.      NewName = Replace$(FileNames(FiDx), RootIndex, NewIndex)
  223.       Results = fn_Save(fileFormat, targetDib, BaseFolder & NewName)
  224.  
  225.       ' prevent memory leaks, always unload the darn images
  226.      dll_Unload compareDib
  227.       dll_Unload targetDib
  228.     End If
  229.   Next FiDx
  230.  
  231.   dll_Unload baseDib
  232.  
  233. End Sub
Advertisement
Add Comment
Please, Sign In to add comment