pano 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/pntool +6 -0
- data/lib/mask.png +0 -0
- data/lib/mask_last.png +0 -0
- data/lib/pano.rb +10 -0
- data/lib/pano/image.rb +58 -0
- data/lib/pano/image_file.rb +55 -0
- data/lib/pano/image_file_set.rb +99 -0
- metadata +71 -0
data/bin/pntool
ADDED
data/lib/mask.png
ADDED
Binary file
|
data/lib/mask_last.png
ADDED
Binary file
|
data/lib/pano.rb
ADDED
data/lib/pano/image.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Pano
|
4
|
+
module Image
|
5
|
+
|
6
|
+
def read_info file_path
|
7
|
+
json = `exiftool -d "%Y-%m-%d %H:%M:%S %z" -j '#{file_path}' 2>&1`
|
8
|
+
|
9
|
+
return {:error => "File not found"} if json =~ /^File not found/i
|
10
|
+
|
11
|
+
data = JSON.parse(json)[0]
|
12
|
+
data[:width] = data["ImageWidth"]
|
13
|
+
data[:height] = data["ImageHeight"]
|
14
|
+
data[:size] = data["ImageSize"]
|
15
|
+
data[:file_size] = data["FileSize"]
|
16
|
+
data[:camera_model] = data["Model"]
|
17
|
+
data[:keywords] = data["Keywords"] || ""
|
18
|
+
data[:owner_name] = data["OwnerName"] # camera owner.
|
19
|
+
data[:iptc_digest] = data["IPTCDigest"] # thinking this can be used to detect photographers if OwnerName undefined
|
20
|
+
data[:created_at] = try_parse_date data, "CreateDate"
|
21
|
+
data[:modified_at] = try_parse_date data, "ModifyDate"
|
22
|
+
data[:file_modified_at] = try_parse_date data, "FileModifyDate"
|
23
|
+
data[:orientation] = data[:width] > data[:height] ? 'h' : 'v'
|
24
|
+
data[:fov] = data["FOV"].to_f
|
25
|
+
|
26
|
+
if data["PhotoshopQuality"].present?
|
27
|
+
quality = data["PhotoshopQuality"]
|
28
|
+
data[:quality] = quality <= 9 ? quality * 10 : quality
|
29
|
+
end
|
30
|
+
|
31
|
+
data
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def try_parse_date data, key
|
37
|
+
s = data[key]
|
38
|
+
s.blank? ? nil : Time.parse(s)
|
39
|
+
end
|
40
|
+
|
41
|
+
def normalize_thumb_options options={}
|
42
|
+
options.reverse_merge(default_thumb_options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def suffix_based_on_options opts
|
46
|
+
suffix = "#{opts[:size]}q#{opts[:quality]}"
|
47
|
+
suffix << 'w' if opts[:watermarked]
|
48
|
+
suffix << 'f' if opts[:force]
|
49
|
+
suffix << 'p' if opts[:protected]
|
50
|
+
suffix << "cbt#{opts[:crop_before_to]}" if opts[:crop_before_to]
|
51
|
+
suffix << 'lo' if opts[:flop]
|
52
|
+
suffix << 'li' if opts[:flip]
|
53
|
+
suffix << 'dne' if opts[:do_not_enlarge]
|
54
|
+
suffix << 't' if opts[:top]
|
55
|
+
suffix
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Pano
|
2
|
+
class ImageFile
|
3
|
+
include Image
|
4
|
+
|
5
|
+
attr_reader :jpg_path, :raw_path, :info
|
6
|
+
|
7
|
+
def initialize path
|
8
|
+
if path =~ /\.jpg$/i
|
9
|
+
@jpg_path = path
|
10
|
+
@raw_path = path.sub(/\.jpg$/i, ".CR2")
|
11
|
+
else
|
12
|
+
@raw_path = path
|
13
|
+
@jpg_path = path.sub(/\.cr2$/i, ".JPG")
|
14
|
+
end
|
15
|
+
|
16
|
+
if File.exist?(@jpg_path)
|
17
|
+
@info = read_info(@jpg_path)
|
18
|
+
elsif File.exist?(raw_path)
|
19
|
+
@info = read_info(@raw_path)
|
20
|
+
else
|
21
|
+
raise "File not found '#{path}'"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def for_pano?
|
26
|
+
@for_pano ||= @info[:fov] > 95 && @info["SelfTimer"] == "2 s" && @info["Orientation"] == "Rotate 270 CW"
|
27
|
+
end
|
28
|
+
|
29
|
+
def bracketed?
|
30
|
+
@bracketed ||= @info["BracketMode"] == "AEB" && @info["BracketShotNumber"] == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def bracketing_number
|
34
|
+
b = @info["BracketValue"]
|
35
|
+
b == 0 ? 0 : b < 0 ? 1 : 2
|
36
|
+
end
|
37
|
+
|
38
|
+
def list_info
|
39
|
+
@info.each_pair do |k, v|
|
40
|
+
puts "#{k} - #{v}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def created_at
|
45
|
+
@created_at ||= @info[:created_at]
|
46
|
+
end
|
47
|
+
|
48
|
+
def copy_to dest_dir
|
49
|
+
FileUtils.mkpath dest_dir;
|
50
|
+
FileUtils.cp(@raw_path, dest_dir) if File.exist?(@raw_path)
|
51
|
+
FileUtils.cp(@jpg_path, dest_dir) if File.exist?(@jpg_path)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Pano
|
2
|
+
|
3
|
+
class ImageFileSet
|
4
|
+
|
5
|
+
attr_reader :base_path, :files
|
6
|
+
|
7
|
+
def initialize dir_path
|
8
|
+
@base_path = dir_path
|
9
|
+
paths = Dir[File.join(@base_path,"*.JPG")]
|
10
|
+
@files = paths.map {|path| ImageFile.new path }
|
11
|
+
@files.sort_by &:created_at
|
12
|
+
end
|
13
|
+
|
14
|
+
def split_images
|
15
|
+
pano = []
|
16
|
+
panos = []
|
17
|
+
misc = []
|
18
|
+
i = 0
|
19
|
+
while i < @files.length
|
20
|
+
file = @files[i]
|
21
|
+
if file.for_pano?
|
22
|
+
if pano.length < 13 * 3
|
23
|
+
pano << file
|
24
|
+
else
|
25
|
+
last = pano.last
|
26
|
+
if (file.created_at - last.created_at) > 1.5.minutes
|
27
|
+
panos << pano
|
28
|
+
pano = []
|
29
|
+
end
|
30
|
+
pano << file
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if pano.present?
|
34
|
+
panos << pano
|
35
|
+
pano = []
|
36
|
+
end
|
37
|
+
misc << file
|
38
|
+
end
|
39
|
+
i+=1
|
40
|
+
end
|
41
|
+
if pano.present?
|
42
|
+
panos << pano
|
43
|
+
end
|
44
|
+
[panos, misc]
|
45
|
+
end
|
46
|
+
|
47
|
+
def spit_and_copy_files
|
48
|
+
puts "analysing files..."
|
49
|
+
panos, misc = split_images
|
50
|
+
panos.each_with_index do |pano, index|
|
51
|
+
pano_dir = File.join(@base_path, "pano.#{index}")
|
52
|
+
puts "coping to #{pano_dir}"
|
53
|
+
pano.each do |file|
|
54
|
+
file.copy_to pano_dir
|
55
|
+
end
|
56
|
+
i = 0
|
57
|
+
pano.each_slice(3) do |files|
|
58
|
+
i+=1
|
59
|
+
fused = enfuse(File.join(pano_dir, "fused"), "%02d" % i, files)
|
60
|
+
if (1..6).include? i
|
61
|
+
system "convert #{fused} #{TOOL_ROOT}/lib/mask.png \
|
62
|
+
+matte -compose CopyOpacity -composite \
|
63
|
+
#{fused}"
|
64
|
+
elsif i == 13
|
65
|
+
system "convert #{fused} #{TOOL_ROOT}/lib/mask_last.png \
|
66
|
+
+matte -compose CopyOpacity -composite \
|
67
|
+
#{fused}"
|
68
|
+
end
|
69
|
+
system "mogrift -rotate -90 #{fused}"
|
70
|
+
system "exiftool -overwrite_original -TagsFromFile #{files.first.jpg_path} #{fused}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
misc_dir = File.join(@base_path, "misc")
|
75
|
+
puts "coping to #{misc_dir}"
|
76
|
+
misc.each do |file|
|
77
|
+
file.copy_to misc_dir
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def enfuse dir, name, files = []
|
83
|
+
return if files.blank?
|
84
|
+
FileUtils.mkpath dir
|
85
|
+
contrast_weight = 0.6
|
86
|
+
entropy_weight = 0.4
|
87
|
+
exposure_weight = 0.5
|
88
|
+
saturation_weight = 0.2
|
89
|
+
target = File.join(dir, name + ".tif")
|
90
|
+
input = files.map {|file| file.jpg_path }.join(" ")
|
91
|
+
options = "--contrast-weight=#{contrast_weight} --entropy-weight=#{entropy_weight} --exposure-weight=#{exposure_weight} --saturation-weight=#{saturation_weight} --compression=LZW"
|
92
|
+
system "enfuse #{options} -o #{target} #{input}"
|
93
|
+
|
94
|
+
target
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pano
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yury Korolev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-28 00:00:00 +03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.3.5
|
24
|
+
version:
|
25
|
+
description: Split and fuse images for pano
|
26
|
+
email:
|
27
|
+
- yury.korolev@gmail.com
|
28
|
+
executables:
|
29
|
+
- pntool
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- lib/pano/image.rb
|
36
|
+
- lib/pano/image_file.rb
|
37
|
+
- lib/pano/image_file_set.rb
|
38
|
+
- lib/pano.rb
|
39
|
+
- bin/pntool
|
40
|
+
- lib/mask.png
|
41
|
+
- lib/mask_last.png
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://anjlab.com
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.5
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: "\"Just prepare photos for pano\""
|
70
|
+
test_files: []
|
71
|
+
|