imogen 0.0.3

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzMwYzg4MjlkMjE0MDBmYjYzMWZlZTZiOGZkYzcyYmE3OGU0YmQ2Yg==
5
+ data.tar.gz: !binary |-
6
+ NTYzYjQ3ZDMyMzJjMjU1ODAxNmUyNDU4Mjc3YTFjZjY1NmIyYTM5Yw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MmMwZWM3MDE1ZDlmZTU5Y2U3YjQ4ZWE0ZDIxMzc2ODc3NGM0NmVhYzg2YTdk
10
+ MmFiY2I4OTA2MGJkNmJlYmUxNWVlMGQ4MzZmZTQ1Yjg0ZjIwNTc3MDgyNjVh
11
+ ZGVmZTMxOGMxODc1YzBjMmI4ODU1YTkzZjdhNmYwM2JlNzYxYjc=
12
+ data.tar.gz: !binary |-
13
+ OTU3MWQ4MjI3NWY1ODAzNjQzZTg5YmI4MGMyZTU5MDYxOWI2MDAzZTgxODAz
14
+ YjlhYjQwOWUwN2ZjZGFmYjcxM2U0ODdiOTM1Njg3OGYxYjNhNTc3OTg3MGMw
15
+ NmNhZjc3YTQ2NGIwOWM0ZmRlNDY4N2RiMGEzOWJhMGNlOGYyODE=
@@ -0,0 +1,79 @@
1
+ #!ruby
2
+ require 'opencv'
3
+ module Imogen::AutoCrop::Box
4
+ include OpenCV
5
+ class Best
6
+ def initialize(grayscale)
7
+ @corners = grayscale.good_features_to_track(0.3, 1.0, block_size: 3, max: 20)
8
+ if @corners.nil? or @corners.length == 0
9
+ @center = Center.new(grayscale)
10
+ else
11
+ @center = nil
12
+ end
13
+ end
14
+
15
+ def self.distance(p1, p2)
16
+ dx = p1.x.to_i - p2.x.to_i
17
+ dy = p1.y.to_i - p2.y.to_i
18
+ return Math.sqrt((dx * dx) + (dy * dy))
19
+ end
20
+
21
+ def box()
22
+ return @center.box unless @center.nil?
23
+ c = median()
24
+ cp = BoxInfo.new(c[0], c[1],0)
25
+ total_distance = 0;
26
+ features = @corners.collect {|corner| d = Best.distance(corner, cp); total_distance += d; {x: corner.x, y: corner.y, d: d}}
27
+ mean_distance = total_distance/features.length
28
+ sigma = features.inject(0) {|memo, feature| v = feature[:d] - mean_distance; memo += (v*v)}
29
+ sigma = Math.sqrt(sigma.to_f/features.length)
30
+ # 2 sigmas would capture > 95% of normally distributed features
31
+ cp.radius = 2*sigma
32
+ cp
33
+ end
34
+
35
+ def median()
36
+ @median ||= begin
37
+ xs = []
38
+ ys = []
39
+ @corners.each {|c| xs << c.x.to_i; ys << c.y.to_i}
40
+ xs.sort!
41
+ ys.sort!
42
+ ix = 0
43
+ if (@corners.length % 2 == 0)
44
+ l = (@corners.length == 2) ? 0 : (@corners.length/2)
45
+ x = ((xs[l] + xs[l+1]) /2).floor
46
+ y = ((ys[l] + ys[l+1]) /2).floor
47
+ [x,y]
48
+ else
49
+ r = (@corners.length/2).ceil
50
+ [xs[r], ys[r]]
51
+ end
52
+ end
53
+ end
54
+ end
55
+ class Center
56
+ def initialize(grayscale)
57
+ @center ||= [(grayscale.cols/2).floor, (grayscale.rows/2).floor]
58
+ @radius = @center.min
59
+ @ratio = @radius / @center.max
60
+ end
61
+ def box
62
+ return BoxInfo.new(@center[0],@center[1],@radius)
63
+ end
64
+ end
65
+ class BoxInfo
66
+ attr_reader :x, :y
67
+ attr_accessor :radius
68
+ def initialize(x,y,r)
69
+ @x = x
70
+ @y = y
71
+ @radius = r
72
+ end
73
+ end
74
+ def self.info(grayscale)
75
+ dims = [grayscale.cols, grayscale.rows]
76
+ ratio = dims.min / dims.max
77
+ ratio < 0.84 ? Best.new(grayscale).box() : Center.new(grayscale).box()
78
+ end
79
+ end
@@ -0,0 +1,63 @@
1
+ #!ruby
2
+ require 'opencv'
3
+ require 'free-image'
4
+ require 'tempfile'
5
+
6
+ module Imogen::AutoCrop
7
+ class Edges
8
+ include OpenCV
9
+ def initialize(src)
10
+ @xoffset = 0
11
+ @yoffset = 0
12
+ if src.is_a? FreeImage::Bitmap
13
+ img = src
14
+ @xoffset = img.width.to_f/6
15
+ @yoffset = img.height.to_f/6
16
+ @tempfile = Tempfile.new(['crop','.png'])
17
+
18
+ img.copy(@xoffset,@yoffset,img.width-@xoffset,img.height-@yoffset) do |crop|
19
+ crop.save(@tempfile.path, :png)
20
+ crop.free
21
+ end
22
+ else
23
+ raise src.class.name
24
+ end
25
+ # use bigger features on bigger images
26
+ @grayscale = CvMat.load(@tempfile.path, CV_LOAD_IMAGE_GRAYSCALE)
27
+ @xrange = (0..@grayscale.cols)
28
+ @yrange = (0..@grayscale.rows)
29
+ end
30
+
31
+ def bound_min(center)
32
+ [center.x - @xrange.min, @xrange.max - center.x, center.y - @yrange.min, @yrange.max - center.y].min
33
+ end
34
+
35
+ # returns leftX, topY, rightX, bottomY
36
+ def get(*args)
37
+ c = Imogen::AutoCrop::Box.info(@grayscale)
38
+ r = c.radius.floor
39
+ # adjust the box
40
+ coords = [c.x, c.y]
41
+ min_rad = args.max/2
42
+ unless r >= min_rad && r <= bound_min(c)
43
+ # first adjust to the lesser of max (half short dimension) and min (half requested length) radius
44
+ # this might require upscaling in rare situations to preserve bound safety
45
+ r = min_rad if r < min_rad
46
+ max_rad = [@xrange.max - @xrange.min, @yrange.max - @yrange.min].min / 2
47
+ r = max_rad if r > max_rad
48
+ # now move the center point minimally to accomodate the necessary radius
49
+ coords[0] = @xrange.max - r if (coords[0] + r) > @xrange.max
50
+ coords[0] = @xrange.min + r if (coords[0] - r) < @xrange.min
51
+ coords[1] = @yrange.max - r if (coords[1] + r) > @yrange.max
52
+ coords[1] = @yrange.min + r if (coords[1] - r) < @yrange.min
53
+ end
54
+ coords = [coords[0] + @xoffset, coords[1] + @yoffset].collect {|i| i.floor}
55
+ c = coords
56
+
57
+ return [c[0]-r, c[1]-r, c[0]+r, c[1] + r]
58
+ end
59
+ def unlink
60
+ @tempfile.unlink
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ module Imogen
2
+ module AutoCrop
3
+ autoload :Edges, 'imogen/auto_crop/edges'
4
+ autoload :Box, 'imogen/auto_crop/box'
5
+ def self.convert(img, dest_path, scale=768, format=:jpeg)
6
+ frame = Edges.new(img)
7
+ edges = frame.get(scale)
8
+ img.copy(*edges) do |crop|
9
+ crop.rescale(scale, scale) do |thumb|
10
+ t24 = thumb.convert_to_24bits
11
+ dst = FreeImage::File.new(dest_path)
12
+ dst.save(t24, format)
13
+ t24.free
14
+ thumb.free
15
+ end
16
+ end
17
+ frame.unlink
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ #require 'image_science'
2
+ module Imogen
3
+ module Zoomable
4
+ def self.levels_for(*dims)
5
+ max = dims[0..1].max || 0
6
+ return 0 if max < 192
7
+ max_tiles = (max.to_f / 96)
8
+ Math.log2(max_tiles).floor
9
+ end
10
+ def self.convert(img, dest_path)
11
+ dst = FreeImage::File.new(dest_path)
12
+ dst.save(img, :jp2, 8)
13
+ end
14
+ end
15
+ end
data/lib/imogen.rb ADDED
@@ -0,0 +1,106 @@
1
+ # encoding: UTF-8
2
+ require 'ffi'
3
+ require 'rbconfig'
4
+ require 'free-image'
5
+ module Imogen
6
+
7
+ def self.from(src_path)
8
+ FreeImage::Bitmap.open(src_path) do |img|
9
+ yield img
10
+ end
11
+ end
12
+ module Scaled
13
+ def self.convert(img, dest_path, scale=1500, format = :jpeg)
14
+ w = img.width
15
+ h = img.height
16
+ dims = (w > h) ? [1500, 1500*h/w] : [1500*w/h, 1500]
17
+ img.rescale(dims[0], dims[1]) do |scaled|
18
+ scaled = scaled.convert_to_24bits
19
+ dst = FreeImage::File.new(dest_path)
20
+ dst.save(scaled, format)
21
+ scaled.free
22
+ end
23
+ end
24
+ end
25
+ require 'imogen/auto_crop'
26
+ require 'imogen/zoomable'
27
+
28
+ def self.search_paths
29
+ @search_paths ||= begin
30
+ if ENV['FREE_IMAGE_LIBRARY_PATH']
31
+ [ ENV['FREE_IMAGE_LIBRARY_PATH'] ]
32
+ elsif FFI::Platform::IS_WINDOWS
33
+ ENV['PATH'].split(File::PATH_SEPARATOR)
34
+ else
35
+ [ '/usr/local/{lib64,lib32,lib}', '/opt/local/{lib64,lib32,lib}', '/usr/{lib64,lib32,lib}' ]
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.find_lib(lib)
41
+ files = search_paths.inject(Array.new) do |array, path|
42
+ file_name = File.expand_path(File.join(path, "#{lib}.#{FFI::Platform::LIBSUFFIX}"))
43
+ array << Dir.glob(file_name)
44
+ array
45
+ end
46
+ files.flatten.compact.first
47
+ end
48
+
49
+ def self.free_image_library_paths
50
+ @free_image_library_paths ||= begin
51
+ libs = %w{libfreeimage libfreeimage.3 FreeImage}
52
+
53
+ libs.map do |lib|
54
+ find_lib(lib)
55
+ end.compact
56
+ end
57
+ end
58
+
59
+ extend ::FFI::Library
60
+
61
+ if free_image_library_paths.any?
62
+ ffi_lib(*free_image_library_paths)
63
+ elsif FFI::Platform.windows?
64
+ ffi_lib("FreeImaged")
65
+ else
66
+ ffi_lib("freeimage")
67
+ end
68
+
69
+ ffi_convention :stdcall if FFI::Platform.windows?
70
+
71
+ def self.format_from(image_path)
72
+ result = FreeImage.FreeImage_GetFileType(image_path, 0)
73
+ FreeImage.check_last_error
74
+
75
+ if result == :unknown
76
+ # Try to guess the file format from the file extension
77
+ result = FreeImage.FreeImage_GetFIFFromFilename(image_path)
78
+ FreeImage.check_last_error
79
+ end
80
+ result
81
+ end
82
+
83
+ def self.image(src_path)
84
+
85
+ flags = 0
86
+
87
+ fif = format_from(src_path)
88
+ if ((fif != :unknown) and FreeImage.FreeImage_FIFSupportsReading(fif))
89
+ ptr = FreeImage.FreeImage_Load(fif, src_path, flags)
90
+ FreeImage.check_last_error
91
+ return FreeImage::Bitmap.new(ptr, nil)
92
+ end
93
+ return nil
94
+ end
95
+ def self.with_image(src_path, &block)
96
+
97
+ flags = 0
98
+
99
+ fif = format_from(src_path)
100
+ if ((fif != :unknown) and FreeImage.FreeImage_FIFSupportsReading(fif))
101
+ ptr = FreeImage.FreeImage_Load(fif, src_path, flags)
102
+ FreeImage.check_last_error
103
+ FreeImage::Bitmap.new(ptr, nil, &block)
104
+ end
105
+ end
106
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: imogen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Ben Armintor
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-opencv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email: armintor@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/imogen.rb
48
+ - lib/imogen/auto_crop.rb
49
+ - lib/imogen/auto_crop/box.rb
50
+ - lib/imogen/auto_crop/edges.rb
51
+ - lib/imogen/zoomable.rb
52
+ homepage: https://github.com/cul/imogen
53
+ licenses: []
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.2.2
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: derivative generation via FreeImage and smart square thumbnail via OpenCV
75
+ test_files: []