imogen 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 09da39417c3f3659074517ded3cd333389d73294
4
- data.tar.gz: cf43db1635b701bf618aff5ea605b091aca53f01
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjdlYmVmMjIwZmFhMzNmYTdmMjNiOTFiYjM2ZjNmZTllMjYyZTRmMw==
5
+ data.tar.gz: !binary |-
6
+ YjU0NmU3NDdkZTE3YWI0NTdlMDUyMzhhYjk0OTI0ODI1YjIyYzAyYg==
5
7
  SHA512:
6
- metadata.gz: b792def58819afc01ba45716a2585ab967bbf622954167a1a46b39f1e899ad91b2ee1dfd257601db0fe5ffe1b5fe0b69014f6df5d9e8af099ed89600bdf1f9b4
7
- data.tar.gz: 9a7e6156211bf6fc74829bc0ab1f9fe040cce95a335d621eef3d39e39730aa90d4d4bc8897cdc37c86bf9bfc36df82c199c6ac22c06f38cdf5f7815086aace34
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
@@ -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[0] == p[2] or p[1] == p[3]
23
+ if p[2] == 0 or p[3] == 0
24
24
  raise BadRequest.new("Invalid region: #{region}")
25
25
  end
26
26
  e = [
@@ -2,7 +2,7 @@ module Imogen
2
2
  module Iiif
3
3
  class Size < Transform
4
4
  def get(scale=nil)
5
- if scale.nil? or scale.eql? "0"
5
+ if scale.nil? or scale.eql? "full"
6
6
  return nil
7
7
  end
8
8
  if md = /^(\d+)?,(\d+)?$/.match(scale)
@@ -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 = [:jpeg, :png]
46
- def self.convert(img, dest_path, format=:jpeg, opts={})
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
@@ -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 < 192
6
- max_tiles = (max.to_f / 96)
7
- Math.log2(max_tiles).floor
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,10,15")}.to raise_error Imogen::Iiif::BadRequest
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(90)
18
+ expect(subject.get("90")).to eql(270)
18
19
  expect(subject.get("180")).to eql(180)
19
- expect(subject.get("270")).to eql(270)
20
- expect(subject.get("450")).to eql(90)
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(270)
25
+ expect(subject.get("-90")).to eql(90)
24
26
  expect(subject.get("-180")).to eql(180)
25
- expect(subject.get("-270")).to eql(90)
26
- expect(subject.get("-450")).to eql(270)
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.9
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-19 00:00:00.000000000 Z
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: []