├── FilmToLongPicture.py ├── README.md └── Your name.jpg /FilmToLongPicture.py: -------------------------------------------------------------------------------- 1 | # Film To Long Picture 2 | # by YYYeYing 3 | # weibo: @夜莺夜影XWB 4 | # twitter: 高坂琉璃 @Kousaka_Rurii 5 | 6 | import ffmpy3 7 | import os 8 | import sys 9 | import gc 10 | from PIL import Image 11 | from PIL import ImageFilter 12 | import numpy as np 13 | 14 | 15 | def KeyFrame(path, dest_path): 16 | # Get key frames of the film, using FFMPEG. 17 | # path: Path of the video 18 | # dest_path: The directory where you want to save the key frames. 19 | dest_path += '%04d.jpg' 20 | video = ffmpy3.FFmpeg(executable="ffmpeg.exe", 21 | inputs={path: '-hwaccel dxva2'}, 22 | outputs={dest_path: '-vf select=\'eq(pict_type\,I)\' -vsync 2 -b:v 5M -s 1920*1080 -f image2'} 23 | ) 24 | print(video.cmd) 25 | video.run() 26 | return 27 | 28 | 29 | def FrameConnection(source_path, dest_path, dest_name='num', split=100, width=5000, mode="col"): 30 | # connect the key frames to a long picture. 31 | # source_path: the directory of key frames. 32 | # dest_path: the directory of the long picture. 33 | # dest_name: the name of the long picture, an optional argument in main program. 34 | # spilt: frames per picture, an optional argument in main program. 35 | # width: the width of the long picture, an optional argument in main program. 36 | # mode: the treatment of frames. 37 | modes = {'norm': MedianFilter, 'row': PicAvgRow, 'col': PicAvgCol, 'max': PicMaxColor, 'non': DoNothing} 38 | # the options in argument [mode]: 'norm', 'row', 'col', 'max', 'non' 39 | images = [] 40 | for root, dirs, files in os.walk(source_path): 41 | for f in sorted(files): 42 | images.append(f) 43 | pic_num = len(images) 44 | print("There are "+str(pic_num)+" images.") 45 | example = Image.open(source_path + '\\' + images[0]) 46 | 47 | # unit_width = example.size[0] 48 | unit_width = int(width / split) 49 | target_height = example.size[1] 50 | target_width = split * unit_width 51 | seg_num = int(pic_num / split) 52 | print("Frames will be connect into "+str(seg_num)+" segments.") 53 | print("Target Segment Size: " + str(target_width) + " × " + str(target_height)+'\n') 54 | quality_value = 100 55 | for i in range(seg_num): 56 | new_left = 0 57 | print('connecting segment ' + str(i+1) + ' of ' + str(seg_num) + '...') 58 | target = Image.new(mode=example.mode, size=(target_width, target_height)) 59 | for j in range(0, split): 60 | print("Segment "+str(i+1)+" of "+str(seg_num)+", Picture "+str(j+1)+" of "+str(split)) 61 | # print(str('%.2f' % (((i*split + j) / pic_num)*100)) + ' % ...') 62 | this_image = Image.open(source_path + '\\' + images[i * split + j]) 63 | # this_image = modes.get(mode)(this_image) 64 | re_image = this_image.resize((unit_width, target_height), Image.ANTIALIAS) 65 | re_image = modes.get(mode)(re_image) 66 | target.paste(re_image, (new_left, 0)) # paste resized-image into target 67 | new_left += unit_width 68 | del this_image 69 | del re_image 70 | gc.collect() 71 | target = target.filter(ImageFilter.GaussianBlur(1)) 72 | # target = target.filter(ImageFilter.SMOOTH_MORE) 73 | target = MedianFilter(target) 74 | if dest_name == 'num': 75 | target.save(dest_path + str('%04d' % i) + '.jpg', quality=quality_value) 76 | else: 77 | target.save(dest_path + dest_name + '.jpg', quality=quality_value) 78 | del target 79 | gc.collect() 80 | print("picture connected.") 81 | return seg_num 82 | 83 | 84 | def PicMaxColor(image): 85 | # if you choose 'max' in the argument [mode] 86 | # set the picture to the most frequent color. 87 | image = image.convert('RGB') 88 | colors = image.getcolors(image.size[0]*image.size[1]) 89 | max_color = 0 90 | max_color_t = 0 91 | for i in range(len(colors)): 92 | if colors[i][0] > max_color_t: 93 | max_color_t = colors[i][0] 94 | max_color = colors[i][1] 95 | target = Image.new(mode="RGB", size=image.size, color=max_color) 96 | return target 97 | 98 | 99 | def PicAvgRow(image): 100 | # if you choose 'row' in the argument [mode] 101 | # set every row to the mean color of this row. 102 | image = image.convert('RGB') 103 | width = image.size[0] 104 | height = image.size[1] 105 | color = np.array(image) 106 | # print(color.flags) 107 | color.setflags(write=True) 108 | for i in range(height): 109 | r = color[i, :, 0] 110 | g = color[i, :, 1] 111 | b = color[i, :, 2] 112 | avg_r = [int(np.mean(r))]*width 113 | avg_g = [int(np.mean(g))]*width 114 | avg_b = [int(np.mean(b))]*width 115 | color[i, :, 0] = avg_r 116 | color[i, :, 1] = avg_g 117 | color[i, :, 2] = avg_b 118 | result = Image.fromarray(color) 119 | return result 120 | 121 | 122 | def PicAvgCol(image): 123 | # if you choose 'col' in the argument [mode] 124 | # set every column to the mean color of this column. 125 | image = image.convert('RGB') 126 | width = image.size[0] 127 | height = image.size[1] 128 | color = np.asarray(image) 129 | color.flags.writeable = True 130 | # print(color[:, 0, 0]) 131 | for j in range(width): 132 | r = color[:, j, 0] 133 | g = color[:, j, 1] 134 | b = color[:, j, 2] 135 | avg_r = [int(np.mean(r))]*height 136 | avg_g = [int(np.mean(g))]*height 137 | avg_b = [int(np.mean(b))]*height 138 | color[:, j, 0] = avg_r 139 | color[:, j, 1] = avg_g 140 | color[:, j, 2] = avg_b 141 | result = Image.fromarray(color) 142 | return result 143 | 144 | 145 | def MedianFilter(image): 146 | # if you choose 'norm' in the argument [mode] 147 | # use a median filter on the image. 148 | image = image.filter(ImageFilter.MedianFilter(5)) 149 | return image 150 | 151 | 152 | def DoNothing(image): 153 | # if you choose 'non' in the argument [mode] 154 | # do nothing with the picture. 155 | return image 156 | 157 | 158 | if __name__ == "__main__": 159 | sys_path = sys.path[0] 160 | key_frame_path = sys_path + '/KeyFrames/' 161 | key_frame_path = key_frame_path.replace('\\', '/') 162 | myVideo = KeyFrame("test.mkv", key_frame_path) 163 | # get key frames 164 | print("Key frames selected and saved in " + str(key_frame_path)) 165 | connection_path = sys_path + '\\connection\\' 166 | con_num = FrameConnection(source_path=key_frame_path, dest_path=connection_path, mode="row") 167 | # connect key frames to many segments, 100 frames in each segment. 168 | # Why not connect all the key frames in once? Due to the limitation of width in JPEG. 169 | # con_num = FrameConnection(source_path=key_frame_path, dest_path=connection_path, mode="col") 170 | result_path = sys_path + '\\result\\' 171 | FrameConnection(source_path=connection_path, dest_path=result_path, split=con_num, mode="norm", 172 | dest_name="Your Name") 173 | # connect the segments to a complete picture. 174 | print("Result picture is in " + result_path) 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FilmToLongPicture 2 | Select the key frames of a movie and connect them to a long picture. 3 | 4 | Using Python and FFmpeg to make a long picture, which shows the significant color in the movie. 5 | 6 | Like this 7 | 8 | ![image](https://github.com/yyyeying/FilmToLongPicture/blob/master/Your%20name.jpg) 9 | 10 | Before you start, make sure you have installed these tools in your computer: 11 | 12 | FFmpeg (http://ffmpeg.org/) 13 | 14 | ffmpy3 (pip install ffmpy3) 15 | 16 | pillow (pip install PIL) 17 | 18 | numpy (pip install numpy) 19 | 20 | -------------------------------------------------------------------------------- /Your name.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yyyeying/FilmToLongPicture/4ac0cba08ab47083bec96ce695058e52080611ea/Your name.jpg --------------------------------------------------------------------------------