├── LICENSE ├── README.md ├── images ├── centered.jpg ├── output.gif ├── output.png └── output_post.jpg ├── slitscan-gif.py └── slitscan-image.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 KP Kaiser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slit Scan Images in Python with MoviePy and Pillow 2 | 3 | This is the companion repository for the blog post at [makeartwithpython.com](https://www.makeartwithpython.com/blog/creating-slit-scan-images-in-python-and-moviepy/). 4 | 5 | ## Usage 6 | 7 | ```bash 8 | $ python3 slitscan-gif.py INPUT.MP4 9 | ``` 10 | To generate a gif like this: 11 | 12 | ![Generate gifs like this](https://github.com/burningion/slitscan-images-python/raw/master/images/output.gif) 13 | 14 | Or, generate a single image using: 15 | 16 | ```bash 17 | $ python3 slitscan-image.py INPUT.MP4 18 | ``` 19 | ...and you'll get an image like this: 20 | 21 | ![Image like this](https://github.com/burningion/slitscan-images-python/raw/master/images/output_post.jpg) 22 | 23 | See the full post at https://www.makeartwithpython.com/blog/creating-slit-scan-images-in-python-and-moviepy/ 24 | -------------------------------------------------------------------------------- /images/centered.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burningion/slitscan-images-python/4559f5c5ab908a7ab92d84c17cc6e491e459b539/images/centered.jpg -------------------------------------------------------------------------------- /images/output.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burningion/slitscan-images-python/4559f5c5ab908a7ab92d84c17cc6e491e459b539/images/output.gif -------------------------------------------------------------------------------- /images/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burningion/slitscan-images-python/4559f5c5ab908a7ab92d84c17cc6e491e459b539/images/output.png -------------------------------------------------------------------------------- /images/output_post.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burningion/slitscan-images-python/4559f5c5ab908a7ab92d84c17cc6e491e459b539/images/output_post.jpg -------------------------------------------------------------------------------- /slitscan-gif.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | parser = argparse.ArgumentParser() 4 | parser.add_argument("filename", help="filename to generate slit scan image from") 5 | args = parser.parse_args() 6 | 7 | from moviepy.editor import VideoFileClip, VideoClip 8 | from PIL import Image 9 | 10 | import numpy as np 11 | 12 | clip = VideoFileClip(args.filename).resize(0.2) 13 | 14 | print('%s is %i fps, for % seconds at %s' % (args.filename, clip.fps, clip.duration, clip.size)) 15 | 16 | img = np.zeros((clip.size[1], clip.size[0], 3), dtype='uint8') 17 | 18 | currentX = 0 19 | slitwidth = 1 20 | 21 | slitpoint = clip.size[1] // 2 22 | 23 | frame_generator = clip.iter_frames(fps=clip.fps, dtype='uint8') 24 | 25 | def make_frame(t): 26 | global img, currentX 27 | next_frame = next(frame_generator) 28 | img = np.roll(img, -1, axis=0) 29 | img[slitpoint,:,:] = next_frame[slitpoint,:,:] 30 | next_frame[max(slitpoint - currentX, 0):slitpoint,:,:] = img[max(0, slitpoint - currentX):slitpoint,:,:] 31 | 32 | currentX += 1 33 | return next_frame 34 | 35 | output = VideoClip(make_frame=make_frame, duration=10.5) 36 | output.write_gif('output1.gif', fps=12) 37 | -------------------------------------------------------------------------------- /slitscan-image.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | parser = argparse.ArgumentParser() 4 | parser.add_argument("filename", help="filename to generate slit scan from") 5 | args = parser.parse_args() 6 | 7 | print('opening file %s for slit scanning' % args.filename) 8 | 9 | from moviepy.editor import VideoFileClip 10 | import numpy as np 11 | 12 | from PIL import Image 13 | 14 | clip = VideoFileClip(args.filename) 15 | 16 | print('%s is %i fps, for %i seconds at %s' % (args.filename, clip.fps, clip.duration, clip.size)) 17 | 18 | # np.zeros is how we generate an empty ndarray 19 | img = np.zeros((clip.size[1], clip.size[0], 3), dtype='uint8') 20 | 21 | currentX = 0 22 | slitwidth = 1 23 | 24 | slitpoint = clip.size[0] // 2 25 | 26 | # generate our target fps with width / duration 27 | target_fps = clip.size[0] / clip.duration 28 | 29 | for i in clip.iter_frames(fps=target_fps, dtype='uint8'): 30 | if currentX < (clip.size[0] - slitwidth): 31 | img[:,currentX:currentX + slitwidth,:] = i[:,slitpoint:slitpoint+slitwidth,:] 32 | currentX += slitwidth 33 | 34 | output = Image.fromarray(img) 35 | output.save('output_post.jpg') 36 | --------------------------------------------------------------------------------