Line 3: Line 3:
 
Automatised post-processing of video using image compression algorithm glitches.
 
Automatised post-processing of video using image compression algorithm glitches.
  
== videos ==
+
== video ==
  
 
<html><iframe src="https://player.vimeo.com/video/272100688?title=0&byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></html>
 
<html><iframe src="https://player.vimeo.com/video/272100688?title=0&byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></html>
Line 9: Line 9:
 
<html><iframe src="https://player.vimeo.com/video/272102866?title=0&byline=0&portrait=0" width="640" height="338" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></html>
 
<html><iframe src="https://player.vimeo.com/video/272102866?title=0&byline=0&portrait=0" width="640" height="338" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></html>
  
== process ==
+
=== process ===
  
 
For '''jpg''' codec, the process is recompressing each frame with 1% less quality than previous compression. In the videos above, we start at 40% and gradually decrease the quality to 1%.
 
For '''jpg''' codec, the process is recompressing each frame with 1% less quality than previous compression. In the videos above, we start at 40% and gradually decrease the quality to 1%.
Line 149: Line 149:
 
  subprocess.call(['ffmpeg', '-f', 'image2', '-framerate', str(fps), '-r', str(fps), '-i', im_path, '-an', '-vcodec', 'mjpeg', '-q:v', '1', folder_compressed + '/' + movie_path ])
 
  subprocess.call(['ffmpeg', '-f', 'image2', '-framerate', str(fps), '-r', str(fps), '-i', im_path, '-an', '-vcodec', 'mjpeg', '-q:v', '1', folder_compressed + '/' + movie_path ])
  
 +
 +
== sound ==
 +
 +
First attempt using [http://lame.sourceforge.net/ lame].
 +
 +
<html><iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/450288369&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true"></iframe></html>
  
 
[[category:glitch]]  
 
[[category:glitch]]  

Revision as of 18:35, 28 May 2018

Igotit 1000-1500jpg movie.png

Automatised post-processing of video using image compression algorithm glitches.

video

process

For jpg codec, the process is recompressing each frame with 1% less quality than previous compression. In the videos above, we start at 40% and gradually decrease the quality to 1%.

For gif codec, it is much more straight-forward, as we do the process in one pass, specifying the number of colors in the palette.

Python script

  1. try:
  2. from cStringIO import StringIO as BytesIO
  3. except ImportError:
  4. from io import BytesIO
  5. from PIL import Image
  6. import os
  7. import shutil
  8. import subprocess
  9.  
  10.  
  11. ------------- GLOBAL -------------
  12.  
  13.  
  14. # set to true if the source video has not been extracted already
  15. regenarate_video_frames = False
  16. recompress_video_frames = True
  17. regenarate_output_video = True
  18. # source video path
  19. video = 'source.mv'
  20. # folder path to export source video frames
  21. folder_frames = 'frames'
  22. # folder path to store compressed video frames
  23. folder_compressed = 'compressed'
  24. # type of compressor - JPG or GIF
  25. compressor = 'JPG'
  26.  
  27. # jpg compressor settings
  28. jpg_from = 40
  29. jpg_to = 1
  30. jpg_steps = 1
  31.  
  32. # gif compressor settings
  33. gif_colors = 4
  34.  
  35. # output frames limits
  36. limit_from = 1800
  37. limit_to = 3125
  38.  
  39. # output frame rate
  40. fps = 25
  41.  
  42. fprefix = 'output_'
  43.  
  44.  
  45. ------------- FUNCTIONS -------------
  46.  
  47.  
  48. def create_folder(p):
  49. if not os.path.exists(p):
  50. os.makedirs(p)
  51. else:
  52. clear_folder(p)
  53. def clear_folder(p):
  54. for root, dirs, files in os.walk( p ):
  55. for f in files:
  56. os.unlink(os.path.join( root, f ))
  57. for d in dirs:
  58. shutil.rmtree(os.path.join( root, d ))
  59.  
  60. def jpg_compress( src_path, dst_path ):
  61. jpg_q = jpg_from
  62. while jpg_q >= jpg_to:
  63. src = Image.open( src_path )
  64. buffer = BytesIO()
  65. src.save( buffer, "JPEG", quality = jpg_q, optimize=True, progressive=True )
  66. jpg_q -= jpg_steps
  67. buffer.seek(0)
  68. with open( dst_path, "w") as handle:
  69. handle.write(buffer.read())
  70. src_path = dst_path
  71. def gif_compress( src_path, dst_path ):
  72. src = Image.open( src_path )
  73. buffer = BytesIO()
  74. src = src.convert('P', palette=Image.ADAPTIVE, colors=gif_colors)
  75. src.save( buffer, "PNG" )
  76. buffer.seek(0)
  77. with open( dst_path, "w") as handle:
  78. handle.write(buffer.read())
  79.  
  80.  
  81. ------------- PROCESS -------------
  82.  
  83.  
  84. if regenarate_video_frames == True:
  85. create_folder( folder_frames )
  86. subprocess.call(['ffmpeg', '-i', video, '-vcodec', 'png', folder_frames + '/' + fprefix + '%05d.png' ])
  87.  
  88. if recompress_video_frames == True:
  89. create_folder( folder_compressed )
  90. ext = '.jpg'
  91. if compressor == 'GIF':
  92. ext = '.png'
  93. for root, dirs, files in os.walk( folder_frames ):
  94. files.sort()
  95. i = 0
  96. outi = 0
  97. for f in files:
  98. if limit_from > -1 and i >= limit_from and i < limit_to:
  99. outf = folder_compressed + '/' + fprefix
  100. if outi < 10:
  101. outf += '0000'
  102. elif outi < 100:
  103. outf += '000'
  104. elif outi < 1000:
  105. outf += '00'
  106. elif outi < 10000:
  107. outf += '0'
  108. outf += str(outi) + ext
  109. outi += 1
  110. if compressor == 'JPG':
  111. jpg_compress( folder_frames + '/' + f, outf )
  112. elif compressor == 'GIF':
  113. gif_compress( folder_frames + '/' + f, outf )
  114. print( str( outi ) + '/' + str( ( limit_to - limit_from ) ) + ' frame (' + ext + ')' )
  115. i += 1
  116.  
  117. if regenarate_output_video == True:
  118. movie_path = 'movie_' + fps + 'fps_' + limit_from + '-' + limit_to
  119. im_path = folder_compressed + '/' + fprefix + '%5d'
  120. if compressor == 'JPG':
  121. movie_path += '_' + jpg_from + '-' + jpg_to + '-' + jpg_steps + 'jpg'
  122. im_path += '.jpg'
  123. elif compressor == 'GIF':
  124. movie_path += '_' + gif_colors + 'gif'
  125. im_path += '.png'
  126. else:
  127. pass
  128. movie_path += '.mkv'
  129. subprocess.call(['ffmpeg', '-f', 'image2', '-framerate', str(fps), '-r', str(fps), '-i', im_path, '-an', '-vcodec', 'mjpeg', '-q:v', '1', folder_compressed + '/' + movie_path ])


sound

First attempt using lame.

online identity ∋ [ social ∋ [mastodon♥, twitter®, facebook®, diaspora, linkedin®] ∥ repos ∋ [github®, gitlab♥, bitbucket®, sourceforge] ∥ media ∋ [itch.io®, vimeo®, peertube♥, twitch.tv®, tumblr®] ∥ communities ∋ [godotengine♥, openprocessing, stackoverflow, threejs]]