video_tesseract 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|