Frankiezafe (Talk | contribs) (→sound) |
Frankiezafe (Talk | contribs) (→screenshots) |
||
(10 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | [[File:Igotit 1000-1500jpg movie.png| | + | [[File:Igotit 1000-1500jpg movie.png|400px]] |
Automatised post-processing of video using image compression algorithm glitches. | Automatised post-processing of video using image compression algorithm glitches. | ||
+ | |||
+ | == screenshots == | ||
+ | |||
+ | '''Die Antwoord''', Banana brain | ||
+ | |||
+ | <script> { global $wgGallerier; $wgGallerier->addGallery( array( "path" => "compressions/die-antwoord-banana-brain/", "width" => 300, "height" => 126 )); } </script> | ||
+ | |||
+ | '''Lorn''', Acid Rain | ||
+ | |||
+ | <script> { global $wgGallerier; $wgGallerier->addGallery( array( "path" => "compressions/lorn-acid-rain/", "width" => 300, "height" => 126 )); } </script> | ||
+ | |||
+ | |||
+ | '''Bhad Bhabie''', I got it | ||
+ | |||
+ | <script> { global $wgGallerier; $wgGallerier->addGallery( array( "path" => "compressions/bhad-babie-i-got-it/", "width" => 300, "height" => 126 )); } </script> | ||
== video == | == video == | ||
Line 148: | Line 163: | ||
movie_path += '.mkv' | movie_path += '.mkv' | ||
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 == | == sound == | ||
− | First attempt using [http://lame.sourceforge.net/ lame]. | + | First attempt using [http://lame.sourceforge.net/ lame], [[File:I_got_it_3.mp3]] |
− | <html>< | + | |
+ | <html> | ||
+ | <audio controls style="width:100%"> | ||
+ | <source src="https://frankiezafe.org/images/0/00/I_got_it_3.mp3"> | ||
+ | </audio> | ||
+ | </html> | ||
=== script === | === script === | ||
Line 195: | Line 214: | ||
subprocess.call(['xplayer', swap_file1], stdout=FNULL, stderr=subprocess.STDOUT) | subprocess.call(['xplayer', swap_file1], stdout=FNULL, stderr=subprocess.STDOUT) | ||
+ | == References == | ||
+ | |||
+ | * https://arstechnica.com/features/2007/10/the-audiofile-understanding-mp3-compression/ | ||
+ | * https://en.wikipedia.org/wiki/MP3#Bit_rate | ||
+ | * http://sox.sourceforge.net/sox.html | ||
+ | * http://lame.sourceforge.net/ | ||
[[category:glitch]] | [[category:glitch]] |
Automatised post-processing of video using image compression algorithm glitches.
Die Antwoord, Banana brain
Lorn, Acid Rain
Bhad Bhabie, I got it
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
- try:
- from cStringIO import StringIO as BytesIO
- except ImportError:
- from io import BytesIO
- from PIL import Image
- import os
- import shutil
- import subprocess
- ------------- GLOBAL -------------
- # set to true if the source video has not been extracted already
- regenarate_video_frames = False
- recompress_video_frames = True
- regenarate_output_video = True
- # source video path
- video = 'source.mv'
- # folder path to export source video frames
- folder_frames = 'frames'
- # folder path to store compressed video frames
- folder_compressed = 'compressed'
- # type of compressor - JPG or GIF
- compressor = 'JPG'
- # jpg compressor settings
- jpg_from = 40
- jpg_to = 1
- jpg_steps = 1
- # gif compressor settings
- gif_colors = 4
- # output frames limits
- limit_from = 1800
- limit_to = 3125
- # output frame rate
- fps = 25
- fprefix = 'output_'
- ------------- FUNCTIONS -------------
- def create_folder(p):
- if not os.path.exists(p):
- os.makedirs(p)
- else:
- clear_folder(p)
- def clear_folder(p):
- for root, dirs, files in os.walk( p ):
- for f in files:
- os.unlink(os.path.join( root, f ))
- for d in dirs:
- shutil.rmtree(os.path.join( root, d ))
- def jpg_compress( src_path, dst_path ):
- jpg_q = jpg_from
- while jpg_q >= jpg_to:
- src = Image.open( src_path )
- buffer = BytesIO()
- src.save( buffer, "JPEG", quality = jpg_q, optimize=True, progressive=True )
- jpg_q -= jpg_steps
- buffer.seek(0)
- with open( dst_path, "w") as handle:
- handle.write(buffer.read())
- src_path = dst_path
- def gif_compress( src_path, dst_path ):
- src = Image.open( src_path )
- buffer = BytesIO()
- src = src.convert('P', palette=Image.ADAPTIVE, colors=gif_colors)
- src.save( buffer, "PNG" )
- buffer.seek(0)
- with open( dst_path, "w") as handle:
- handle.write(buffer.read())
- ------------- PROCESS -------------
- if regenarate_video_frames == True:
- create_folder( folder_frames )
- subprocess.call(['ffmpeg', '-i', video, '-vcodec', 'png', folder_frames + '/' + fprefix + '%05d.png' ])
- if recompress_video_frames == True:
- create_folder( folder_compressed )
- ext = '.jpg'
- if compressor == 'GIF':
- ext = '.png'
- for root, dirs, files in os.walk( folder_frames ):
- files.sort()
- i = 0
- outi = 0
- for f in files:
- if limit_from > -1 and i >= limit_from and i < limit_to:
- outf = folder_compressed + '/' + fprefix
- if outi < 10:
- outf += '0000'
- elif outi < 100:
- outf += '000'
- elif outi < 1000:
- outf += '00'
- elif outi < 10000:
- outf += '0'
- outf += str(outi) + ext
- outi += 1
- if compressor == 'JPG':
- jpg_compress( folder_frames + '/' + f, outf )
- elif compressor == 'GIF':
- gif_compress( folder_frames + '/' + f, outf )
- print( str( outi ) + '/' + str( ( limit_to - limit_from ) ) + ' frame (' + ext + ')' )
- i += 1
- if regenarate_output_video == True:
- movie_path = 'movie_' + fps + 'fps_' + limit_from + '-' + limit_to
- im_path = folder_compressed + '/' + fprefix + '%5d'
- if compressor == 'JPG':
- movie_path += '_' + jpg_from + '-' + jpg_to + '-' + jpg_steps + 'jpg'
- im_path += '.jpg'
- elif compressor == 'GIF':
- movie_path += '_' + gif_colors + 'gif'
- im_path += '.png'
- else:
- pass
- movie_path += '.mkv'
- subprocess.call(['ffmpeg', '-f', 'image2', '-framerate', str(fps), '-r', str(fps), '-i', im_path, '-an', '-vcodec', 'mjpeg', '-q:v', '1', folder_compressed + '/' + movie_path ])
First attempt using lame, File:I got it 3.mp3
- try:
- from cStringIO import StringIO as BytesIO
- except ImportError:
- from io import BytesIO
- from PIL import Image
- import os
- import shutil
- import subprocess
- source = 'source.wav'
- swap_file1 = 'output.mp3'
- swap_file2 = 'output2.wav'
- passes = 27
- freq = 20001
- freq_gap = 300
- scale = 1.03
- compression = 20
- compression_gap = 1.3
- FNULL = open(os.devnull, 'w')
- subprocess.call(['lame', '--quiet', '-q', '9', source, swap_file1 ])
- for i in range( 0, passes ):
- print( '>>>>>>>>>>> pass: ' + str( i ) + ', freq: ' + str(freq) + ', compression: ' + str(compression) )
- subprocess.call(['ffmpeg', '-i', swap_file1, '-ar', str(freq), '-loglevel', 'panic', '-y', swap_file2 ])
- subprocess.call(['lame', '--scale', str(scale), '--quiet', '-q', '9', '--comp', str(compression), swap_file2, swap_file1 ])
- if i%2 == 1:
- freq -= freq_gap
- else:
- freq += freq_gap
- compression += compression_gap
- # preview
- subprocess.call(['xplayer', swap_file1], stdout=FNULL, stderr=subprocess.STDOUT)
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]]