imogen 0.0.9 → 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 +13 -5
- data/lib/imogen/dzi.rb +52 -0
- data/lib/imogen/iiif/region.rb +1 -1
- data/lib/imogen/iiif/size.rb +1 -1
- data/lib/imogen/iiif/tiles.rb +38 -0
- data/lib/imogen/iiif.rb +24 -3
- data/lib/imogen/zoomable.rb +8 -3
- data/lib/imogen.rb +2 -6
- data/spec/unit/imogen_iiif_region_spec.rb +2 -1
- data/spec/unit/imogen_iiif_rotate_spec.rb +8 -6
- data/spec/unit/imogen_iiif_tiles_spec.rb +78 -0
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YjdlYmVmMjIwZmFhMzNmYTdmMjNiOTFiYjM2ZjNmZTllMjYyZTRmMw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YjU0NmU3NDdkZTE3YWI0NTdlMDUyMzhhYjk0OTI0ODI1YjIyYzAyYg==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MWQ1ODU2NTE3OWRlN2MwZTNlOWYzYjQ0NmI0ZDhmNTBjZmMzMDdmZWYwNmMy
|
10
|
+
NDAwNzU4Y2RhMTE0MjBlYWIxMzg3ZGQxZWU0Yjc2MjU3ZjBhYzY5NjE2NGY4
|
11
|
+
YTAxNjAzZTBkZGNlZTYwYzZiM2Y0NzFlOWIxNTliOTYzZmEzN2Y=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZWE1ZjZiOGNjYzE3OWJlZjA5YWRlMWRlMDQzOWE2NTAxMzVmYjAzYmQ3M2Nm
|
14
|
+
NjVlZjY2NmY2YmE3NWU3MzE2NmQ2ZjhkNjYwMzY2ODA3MWQ3MzRkNTY5Mjg4
|
15
|
+
MDJjZDIxMzcyZjVmZTgwNDZiMTJiZjJhOTMzYjQ1MmU5YmRiNzY=
|
data/lib/imogen/dzi.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'imogen/zoomable'
|
2
|
+
require 'imogen/iiif'
|
3
|
+
module Imogen
|
4
|
+
module Dzi
|
5
|
+
def self.convert(img,dest_dir,format=:jpeg,opts={})
|
6
|
+
override = opts[:override]
|
7
|
+
|
8
|
+
end
|
9
|
+
def self.iiif_paths(img,tile_size=128,format=:jpeg)
|
10
|
+
width, height = img.width, img.height
|
11
|
+
max_levels = Imogen::Zoomable.max_levels_for(width, height)
|
12
|
+
results = {}
|
13
|
+
max_levels.downto(0) do |level|
|
14
|
+
c_ratio = 2**(max_levels-level)
|
15
|
+
|
16
|
+
tile_side = tile_size*c_ratio
|
17
|
+
x, col = 0, 0
|
18
|
+
while x < width
|
19
|
+
y, row = 0, 0
|
20
|
+
while y < height
|
21
|
+
results["#{level}/#{col_count}_#{row_count}"] = iiif_path_for_dzi(level,max_levels,col,row,tile_size,format)
|
22
|
+
y += tile_side
|
23
|
+
row += 1
|
24
|
+
end
|
25
|
+
x += tile_side
|
26
|
+
col += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
def self.iiif_opts_for_dzi(level,max_levels,col,row,tile_size=128,format=:jpeg)
|
32
|
+
level = level.to_i
|
33
|
+
max_levels = max_levels.to_i
|
34
|
+
c_ratio = 2**(max_levels-level)
|
35
|
+
|
36
|
+
tile_side = tile_size*c_ratio
|
37
|
+
x = col * c_ratio
|
38
|
+
y = row * c_ratio
|
39
|
+
{
|
40
|
+
region: "#{x},#{y},#{tile_side},#{tile_side}",
|
41
|
+
size: "!#{tile_size},#{tile_size}",
|
42
|
+
format: format,
|
43
|
+
rotation: 0,
|
44
|
+
quality: :native
|
45
|
+
}
|
46
|
+
end
|
47
|
+
def self.iiif_path_for_dzi(level,max_levels,col,row,tile_size=512,format=:jpeg)
|
48
|
+
opts = iiif_opts_for_dzi(level, max_levels,col,row,tile_size,format)
|
49
|
+
"#{opts[:region]}/#{opts[:size]}/#{opts[:rotation]}/#{opts[:quality]}.#{Imogen::Iiif::FORMATS[format]}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/imogen/iiif/region.rb
CHANGED
@@ -20,7 +20,7 @@ class Region < Transform
|
|
20
20
|
]
|
21
21
|
elsif md = /^(\d+),(\d+),(\d+),(\d+)$/.match(region)
|
22
22
|
p = [Integer(md[1]),Integer(md[2]),Integer(md[3]),Integer(md[4])]
|
23
|
-
if p[
|
23
|
+
if p[2] == 0 or p[3] == 0
|
24
24
|
raise BadRequest.new("Invalid region: #{region}")
|
25
25
|
end
|
26
26
|
e = [
|
data/lib/imogen/iiif/size.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'imogen/zoomable'
|
2
|
+
module Imogen
|
3
|
+
module Iiif
|
4
|
+
module Tiles
|
5
|
+
def self.scale_factor_for(*dims)
|
6
|
+
Imogen::Zoomable.levels_for(*dims)**2
|
7
|
+
end
|
8
|
+
def self.for(img,dest_dir,format=:jpeg,tile_size=128,override=false)
|
9
|
+
width, height = img.width, img.height
|
10
|
+
max_level = Imogen::Zoomable.levels_for(width,height,tile_size)
|
11
|
+
max_level.downto(0) do |level|
|
12
|
+
scale = 0.5**level
|
13
|
+
level_width = (img.width*scale).ceil
|
14
|
+
level_width = (img.height*scale).ceil
|
15
|
+
region_size = (tile_size / scale).ceil
|
16
|
+
region_width = (scale * img.width).ceil
|
17
|
+
region_height = (scale * img.height).ceil
|
18
|
+
x, col = 0, 0
|
19
|
+
while x < width
|
20
|
+
y, row = 0, 0
|
21
|
+
while y < height
|
22
|
+
region = "#{x},#{y},#{[width-x,region_size].min},#{[height-y,region_size].min}"
|
23
|
+
size = "full"
|
24
|
+
dest_path = File.join(dest_dir,region,size,'0',"native.#{Imogen::Iiif::FORMATS[format]}")
|
25
|
+
unless File.exists? dest_path or override
|
26
|
+
yield(img,dest_path,format,Imogen::Iiif.path_to_opts(dest_path,dest_dir))
|
27
|
+
end
|
28
|
+
y += region_size
|
29
|
+
row += 1
|
30
|
+
end
|
31
|
+
x += region_size
|
32
|
+
col += 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/imogen/iiif.rb
CHANGED
@@ -23,7 +23,7 @@ module Imogen
|
|
23
23
|
module Quality
|
24
24
|
VALUES = [:native, :color, :grey, :bitonal]
|
25
25
|
def self.get(quality=:native)
|
26
|
-
q = quality.to_sym
|
26
|
+
q = (quality || :native).to_sym
|
27
27
|
raise BadRequest.new("bad quality #{quality}") unless VALUES.include? q
|
28
28
|
return (q == :native or q == :color) ? nil : q
|
29
29
|
end
|
@@ -41,15 +41,20 @@ module Imogen
|
|
41
41
|
autoload :Region, 'imogen/iiif/region'
|
42
42
|
autoload :Size, 'imogen/iiif/size'
|
43
43
|
autoload :Rotation, 'imogen/iiif/rotation'
|
44
|
+
autoload :Tiles, 'imogen/iiif/tiles'
|
44
45
|
|
45
|
-
FORMATS =
|
46
|
-
|
46
|
+
FORMATS = {jpeg: 'jpg', jpg: 'jpg', png: 'png'}
|
47
|
+
EXTENSIONS = {'jpg' => :jpeg, 'png' => :png}
|
48
|
+
def self.convert(img, dest_path, format=nil, opts={})
|
49
|
+
format ||= opts.fetch(:format,:jpeg)
|
50
|
+
format = format.to_sym
|
47
51
|
raise BadRequest.new("bad format #{format}") unless FORMATS.include? format
|
48
52
|
Region.convert(img, opts[:region]) do |region|
|
49
53
|
Size.convert(region, opts[:size]) do |size|
|
50
54
|
Rotation.convert(size, opts[:rotation]) do |rotation|
|
51
55
|
Quality.convert(rotation, opts[:quality]) do |quality|
|
52
56
|
dst = FreeImage::File.new(dest_path)
|
57
|
+
format = :jpeg if format == :jpg
|
53
58
|
if (img.color_type == :rgb)
|
54
59
|
quality.convert_to_24bits {|result| dst.save(result, format)}
|
55
60
|
else
|
@@ -60,5 +65,21 @@ module Imogen
|
|
60
65
|
end
|
61
66
|
end
|
62
67
|
end
|
68
|
+
def self.path_to_opts(path,parent_dir)
|
69
|
+
if parent_dir and path.start_with? parent_dir
|
70
|
+
path = path.sub(parent_dir,'')
|
71
|
+
end
|
72
|
+
path = path[1..-1] if path =~ /^\//
|
73
|
+
parts = path.split('/')
|
74
|
+
quality = parts[-1].split('.')[0].to_sym
|
75
|
+
format = EXTENSIONS[parts[-1].split('.')[1]]
|
76
|
+
{
|
77
|
+
region: parts[0],
|
78
|
+
size: parts[1],
|
79
|
+
rotation: parts[2],
|
80
|
+
quality: quality,
|
81
|
+
format: format
|
82
|
+
}
|
83
|
+
end
|
63
84
|
end
|
64
85
|
end
|
data/lib/imogen/zoomable.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
module Imogen
|
2
2
|
module Zoomable
|
3
|
+
# levels for hypothetical 1x1 tiles
|
4
|
+
def self.max_levels_for(*dims)
|
5
|
+
return Math.log2(dims[0..1].max).ceil
|
6
|
+
end
|
7
|
+
# levels for width,height, tile_size=128
|
3
8
|
def self.levels_for(*dims)
|
4
9
|
max = dims[0..1].max || 0
|
5
|
-
return 0 if max
|
6
|
-
|
7
|
-
Math.log2(
|
10
|
+
return 0 if max == 0
|
11
|
+
tile_size = dims[2] || 128
|
12
|
+
return Math.log2(dims[0..1].max.to_f / tile_size).ceil
|
8
13
|
end
|
9
14
|
def self.convert(img, dest_path)
|
10
15
|
dst = FreeImage::File.new(dest_path)
|
data/lib/imogen.rb
CHANGED
@@ -85,9 +85,7 @@ module Imogen
|
|
85
85
|
result
|
86
86
|
end
|
87
87
|
|
88
|
-
def self.image(src_path)
|
89
|
-
|
90
|
-
flags = 0
|
88
|
+
def self.image(src_path, flags=0)
|
91
89
|
|
92
90
|
fif = format_from(src_path)
|
93
91
|
if ((fif != :unknown) and FreeImage.FreeImage_FIFSupportsReading(fif))
|
@@ -97,9 +95,7 @@ module Imogen
|
|
97
95
|
end
|
98
96
|
return nil
|
99
97
|
end
|
100
|
-
def self.with_image(src_path, &block)
|
101
|
-
|
102
|
-
flags = 0
|
98
|
+
def self.with_image(src_path, flags = 0, &block)
|
103
99
|
|
104
100
|
fif = format_from(src_path)
|
105
101
|
if ((fif != :unknown) and FreeImage.FreeImage_FIFSupportsReading(fif))
|
@@ -17,7 +17,8 @@ describe Imogen::Iiif::Region, type: :unit do
|
|
17
17
|
expect(subject.get("80,15,60,75")).to eql([80,15,140,90])
|
18
18
|
end
|
19
19
|
it "should reject zero-dimension boxes" do
|
20
|
-
expect{subject.get("101,15,
|
20
|
+
expect{subject.get("101,15,0,15")}.to raise_error Imogen::Iiif::BadRequest
|
21
|
+
expect{subject.get("101,15,10,0")}.to raise_error Imogen::Iiif::BadRequest
|
21
22
|
end
|
22
23
|
describe "that exceeds the bounds" do
|
23
24
|
it "should calculate an overlapping region" do
|
@@ -13,17 +13,19 @@ describe Imogen::Iiif::Rotation, type: :unit do
|
|
13
13
|
expect(subject.get("0")).to be_nil
|
14
14
|
expect(subject.get(nil)).to be_nil
|
15
15
|
end
|
16
|
+
# IIIF rotation is opposite FreeImage
|
16
17
|
it "should calculate for positive values" do
|
17
|
-
expect(subject.get("90")).to eql(
|
18
|
+
expect(subject.get("90")).to eql(270)
|
18
19
|
expect(subject.get("180")).to eql(180)
|
19
|
-
expect(subject.get("270")).to eql(
|
20
|
-
expect(subject.get("450")).to eql(
|
20
|
+
expect(subject.get("270")).to eql(90)
|
21
|
+
expect(subject.get("450")).to eql(270)
|
21
22
|
end
|
23
|
+
# IIIF rotation is opposite FreeImage
|
22
24
|
it "should calculate for negative values" do
|
23
|
-
expect(subject.get("-90")).to eql(
|
25
|
+
expect(subject.get("-90")).to eql(90)
|
24
26
|
expect(subject.get("-180")).to eql(180)
|
25
|
-
expect(subject.get("-270")).to eql(
|
26
|
-
expect(subject.get("-450")).to eql(
|
27
|
+
expect(subject.get("-270")).to eql(270)
|
28
|
+
expect(subject.get("-450")).to eql(90)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
it "should reject arbitrary integer and float values" do
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'imogen/iiif'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
3
|
+
describe Imogen::Iiif::Tiles, type: :unit do
|
4
|
+
before(:all) do
|
5
|
+
@test_image = ImageStub.new(175,131)
|
6
|
+
end
|
7
|
+
let(:fullSize) { ["/0,0,175,131/full/0/native.jpg"]}
|
8
|
+
let(:tile128) {
|
9
|
+
[
|
10
|
+
"/0,0,128,128/full/0/native.jpg",
|
11
|
+
"/128,0,47,128/full/0/native.jpg",
|
12
|
+
"/0,128,128,3/full/0/native.jpg",
|
13
|
+
"/128,128,47,3/full/0/native.jpg"
|
14
|
+
]
|
15
|
+
}
|
16
|
+
let(:tile64) {
|
17
|
+
[
|
18
|
+
"/0,0,64,64/full/0/native.jpg",
|
19
|
+
"/64,0,64,64/full/0/native.jpg",
|
20
|
+
"/128,0,47,64/full/0/native.jpg",
|
21
|
+
"/0,64,64,64/full/0/native.jpg",
|
22
|
+
"/64,64,64,64/full/0/native.jpg",
|
23
|
+
"/128,64,47,64/full/0/native.jpg",
|
24
|
+
"/0,128,64,3/full/0/native.jpg",
|
25
|
+
"/64,128,64,3/full/0/native.jpg",
|
26
|
+
"/128,128,47,3/full/0/native.jpg",
|
27
|
+
]
|
28
|
+
}
|
29
|
+
describe '#scale_factor_for' do
|
30
|
+
it 'should calculate for different tile sizes' do
|
31
|
+
width, height = @test_image.width, @test_image.height
|
32
|
+
(0..8).each do |exp|
|
33
|
+
tile_size = 2**exp
|
34
|
+
expected = (8 - exp)**2
|
35
|
+
expect(subject.scale_factor_for(width, height, tile_size)).to eql(expected)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
describe '#for' do
|
40
|
+
it 'should produce a single tile when contained' do
|
41
|
+
expected = fullSize
|
42
|
+
actual = []
|
43
|
+
subject.for(@test_image,'',:jpeg,256) do |img,dest_path,format,opts|
|
44
|
+
actual << dest_path
|
45
|
+
end
|
46
|
+
expect(actual).to eql(expected)
|
47
|
+
end
|
48
|
+
it 'should produce a tileset when half' do
|
49
|
+
expected = fullSize + tile128
|
50
|
+
expected.uniq!
|
51
|
+
actual = []
|
52
|
+
subject.for(@test_image,'',:jpeg,128) do |img,dest_path,format,opts|
|
53
|
+
actual << dest_path
|
54
|
+
end
|
55
|
+
actual.uniq!
|
56
|
+
expect(actual.sort).to eql(expected.sort)
|
57
|
+
end
|
58
|
+
it 'should produce a single tileset' do
|
59
|
+
expected = fullSize + tile128 + tile64
|
60
|
+
expected.uniq!
|
61
|
+
actual = []
|
62
|
+
subject.for(@test_image,'',:jpeg,64) do |img,dest_path,format,opts|
|
63
|
+
actual << dest_path
|
64
|
+
end
|
65
|
+
actual.uniq!
|
66
|
+
expect(actual.sort).to eql(expected.sort)
|
67
|
+
end
|
68
|
+
it 'should produce png when requested' do
|
69
|
+
expected = fullSize.collect {|x| x.sub(/jpg$/,'png')}
|
70
|
+
expected.uniq!
|
71
|
+
actual = []
|
72
|
+
subject.for(@test_image,'',:png,256) do |img,dest_path,format,opts|
|
73
|
+
actual << dest_path
|
74
|
+
end
|
75
|
+
expect(actual).to eql(expected)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imogen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Armintor
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-opencv
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ! '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ! '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ! '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
description:
|
@@ -48,15 +48,18 @@ files:
|
|
48
48
|
- lib/imogen/auto_crop.rb
|
49
49
|
- lib/imogen/auto_crop/box.rb
|
50
50
|
- lib/imogen/auto_crop/edges.rb
|
51
|
+
- lib/imogen/dzi.rb
|
51
52
|
- lib/imogen/iiif.rb
|
52
53
|
- lib/imogen/iiif/region.rb
|
53
54
|
- lib/imogen/iiif/rotation.rb
|
54
55
|
- lib/imogen/iiif/size.rb
|
56
|
+
- lib/imogen/iiif/tiles.rb
|
55
57
|
- lib/imogen/zoomable.rb
|
56
58
|
- spec/spec_helper.rb
|
57
59
|
- spec/unit/imogen_iiif_region_spec.rb
|
58
60
|
- spec/unit/imogen_iiif_rotate_spec.rb
|
59
61
|
- spec/unit/imogen_iiif_size_spec.rb
|
62
|
+
- spec/unit/imogen_iiif_tiles_spec.rb
|
60
63
|
homepage: https://github.com/cul/imogen
|
61
64
|
licenses: []
|
62
65
|
metadata: {}
|
@@ -66,12 +69,12 @@ require_paths:
|
|
66
69
|
- lib
|
67
70
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
71
|
requirements:
|
69
|
-
- -
|
72
|
+
- - ! '>='
|
70
73
|
- !ruby/object:Gem::Version
|
71
74
|
version: '0'
|
72
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
76
|
requirements:
|
74
|
-
- -
|
77
|
+
- - ! '>='
|
75
78
|
- !ruby/object:Gem::Version
|
76
79
|
version: '0'
|
77
80
|
requirements: []
|