video_tesseract 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/video-tesseract +155 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0afb26ca6638e1ab64bbd929df6661238d07d3ef
|
4
|
+
data.tar.gz: a6b7ba869db9390130aa5b9455f2c93187713336
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9b0dfab4fb0f34a20938edc50bdb306eaadb5a185b5a73f74f57b2a9a61ef4876e5e2241c4c517aa2abfed8e85caeed04c3e5ae7c831ac1f08499fe2b9f96575
|
7
|
+
data.tar.gz: c8f751daf2035c22419ab4f2ff2ee9f8e9d7698adb2a911b994b0b48ef3c57f5029975d6b805fccd7d25be240f206f6d2f282af745137db9851c8f9861b31f19
|
data/bin/video-tesseract
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'mkmf'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
@tile_width = 320
|
8
|
+
@tile_height = 180
|
9
|
+
|
10
|
+
@frame_rate = 23.976
|
11
|
+
|
12
|
+
@cols = 5
|
13
|
+
@rows = 5
|
14
|
+
|
15
|
+
@input = nil
|
16
|
+
@output = 'tesseract.mp4'
|
17
|
+
|
18
|
+
@verbose = false
|
19
|
+
|
20
|
+
optparse = OptionParser.new do |opts|
|
21
|
+
opts.banner = "Usage: tesseract [options]"
|
22
|
+
|
23
|
+
opts.on('-i', '--input [input.mp4]', 'Path to input video (Required)') do |i|
|
24
|
+
@input = i
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-o', '--output [tesseract.mp4]', 'Path to write output tesseracted video (Defaults to tesseract.mp4)') do |o|
|
28
|
+
@output = o
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-s', '--size [<width>x<height>]', 'Tile size in pixels (Defaults to 320x180)') do |s|
|
32
|
+
@tile_width, @tile_height = s.split('x').map(&:to_i)
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on('-g', '--grid [<columns>x<rows>]', 'Tesseract grid layout (Defaults to 5x5)') do |g|
|
36
|
+
@cols, @rows = g.split('x').map(&:to_i)
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('-r', '--frame-rate [23.976]', 'Frame rate of the tesseracted video (Defaults to 23.976)') do |r|
|
40
|
+
@frame_rate = r.to_f
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
44
|
+
@verbose = v
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-h', '--help', 'Prints help') do
|
48
|
+
puts opts
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
optparse.parse!
|
54
|
+
|
55
|
+
|
56
|
+
# Make sure we have @input
|
57
|
+
if @input.nil? || !File.exist?(@input)
|
58
|
+
puts 'Missing or invalid input video file (--input)'
|
59
|
+
puts ''
|
60
|
+
puts optparse
|
61
|
+
abort
|
62
|
+
end
|
63
|
+
|
64
|
+
# Make sure we have ffmpeg and imagemagick installed
|
65
|
+
unless find_executable('ffmpeg')
|
66
|
+
puts 'Cannot find ffmpeg executable. Make sure you have FFmpeg installed.'
|
67
|
+
abort
|
68
|
+
end
|
69
|
+
|
70
|
+
unless find_executable('convert')
|
71
|
+
puts 'Cannot find convert executable. Make sure you have ImageMagick installed.'
|
72
|
+
abort
|
73
|
+
end
|
74
|
+
|
75
|
+
Dir.mktmpdir do |data_dir|
|
76
|
+
|
77
|
+
# get the frame count for the video
|
78
|
+
frames = `ffmpeg -i #{@input} -vcodec copy -acodec copy -f null /dev/null 2>&1 | grep 'frame=' | cut -f 2 -d ' '`.to_i
|
79
|
+
|
80
|
+
tiles = @rows * @cols
|
81
|
+
frames_per_tile = frames / tiles
|
82
|
+
tesseract_width = @tile_width * @cols
|
83
|
+
tesseract_height = @tile_height * @rows
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
puts ''
|
88
|
+
puts "Generating #{@cols}x#{@rows} tesseract with tile size #{@tile_width}x#{@tile_height}"
|
89
|
+
puts "Tesseract video will be #{tesseract_width}x#{tesseract_height} with a duration of #{frames_per_tile} frames."
|
90
|
+
puts ''
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
#
|
95
|
+
# Step 1: Extract all frames from the video
|
96
|
+
#
|
97
|
+
# Note: Skip this step if the we already have the frames extracted.
|
98
|
+
|
99
|
+
print "Extracting all frames of the video ... "
|
100
|
+
`ffmpeg -y -i #{@input} -vcodec png -s #{@tile_width}x#{@tile_height} -an -y #{data_dir}/frame_%d.png #{'2>&1' unless @verbose}`
|
101
|
+
puts 'done'
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
#
|
106
|
+
# Step 2: Creating the montages from the frames
|
107
|
+
#
|
108
|
+
(1..frames_per_tile).each do |offset|
|
109
|
+
|
110
|
+
images = (offset..frames).step(frames_per_tile).map { |idx| "#{data_dir}/frame_#{idx}.png" }[0...tiles]
|
111
|
+
|
112
|
+
# how much to move each frame to the left as time goes on
|
113
|
+
x_shift = (offset / frames_per_tile.to_f) * @tile_width
|
114
|
+
|
115
|
+
cmd = "convert -size #{tesseract_width}x#{tesseract_height} xc:black "
|
116
|
+
images.each_with_index do |image, idx|
|
117
|
+
x_offset = (idx % @cols) * @tile_width + x_shift
|
118
|
+
y_offset = (idx / @cols) * @tile_height
|
119
|
+
cmd << "#{image} -geometry +#{x_offset}+#{y_offset} -composite "
|
120
|
+
|
121
|
+
# if it's the last image in the row, repeat it in the row below to the left
|
122
|
+
# of the first image for that row
|
123
|
+
if idx % @cols == @cols - 1
|
124
|
+
x_offset = ((idx + 1) % @cols) * @tile_width + x_shift - @tile_width
|
125
|
+
y_offset = ((idx + 1) / @cols) * @tile_height
|
126
|
+
cmd << "#{image} -geometry +#{x_offset}+#{y_offset} -composite "
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
cmd << "#{data_dir}/montage_#{offset}.png"
|
131
|
+
|
132
|
+
if @verbose
|
133
|
+
puts cmd
|
134
|
+
puts ''
|
135
|
+
end
|
136
|
+
|
137
|
+
print "Generating montage #{offset}/#{frames_per_tile}\r"
|
138
|
+
$stdout.flush
|
139
|
+
|
140
|
+
system(cmd)
|
141
|
+
|
142
|
+
end
|
143
|
+
puts "Generated all #{frames_per_tile} montages."
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
#
|
148
|
+
# Step 3: Creating the tesseracted video from the montages
|
149
|
+
#
|
150
|
+
|
151
|
+
print "Generating final tesseracted video: #{@output} ... "
|
152
|
+
`ffmpeg -y -framerate #{@frame_rate} -i #{data_dir}/montage_%d.png -c:v libx264 -r #{@frame_rate} -pix_fmt yuv420p #{@output} #{'2>&1' unless @verbose}`
|
153
|
+
puts 'done'
|
154
|
+
|
155
|
+
end
|
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: video_tesseract
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brendan Schwartz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-16 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: 'Video tesseract lays '
|
14
|
+
email: brendan@wistia.com
|
15
|
+
executables:
|
16
|
+
- video-tesseract
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/video-tesseract
|
21
|
+
homepage: https://github.com/wistia/video-tesseract
|
22
|
+
licenses:
|
23
|
+
- MIT
|
24
|
+
metadata: {}
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
requirements: []
|
40
|
+
rubyforge_project:
|
41
|
+
rubygems_version: 2.2.2
|
42
|
+
signing_key:
|
43
|
+
specification_version: 4
|
44
|
+
summary: Tesseract your videos
|
45
|
+
test_files: []
|