imogen 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: []