qttk 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +36 -0
  5. data/README.markdown +131 -0
  6. data/Rakefile +17 -0
  7. data/TODO.markdown +236 -0
  8. data/bin/qt +7 -0
  9. data/etc/gutenprint/gp-tool.rb +56 -0
  10. data/etc/gutenprint/gutenprint-filter.c +400 -0
  11. data/etc/gutenprint/gutenprint.rb +86 -0
  12. data/etc/gutenprint/stp-test +326 -0
  13. data/etc/images/3551599565_db282cf840_o.jpg +0 -0
  14. data/etc/images/4843122063_d582c569e9_o.jpg +0 -0
  15. data/etc/images/4843128953_83c1770907_o.jpg +0 -0
  16. data/lib/quadtone.rb +56 -0
  17. data/lib/quadtone/cgats.rb +137 -0
  18. data/lib/quadtone/cluster_calculator.rb +81 -0
  19. data/lib/quadtone/color.rb +83 -0
  20. data/lib/quadtone/color/cmyk.rb +112 -0
  21. data/lib/quadtone/color/device_n.rb +23 -0
  22. data/lib/quadtone/color/gray.rb +46 -0
  23. data/lib/quadtone/color/lab.rb +150 -0
  24. data/lib/quadtone/color/qtr.rb +71 -0
  25. data/lib/quadtone/color/rgb.rb +71 -0
  26. data/lib/quadtone/color/xyz.rb +80 -0
  27. data/lib/quadtone/curve.rb +138 -0
  28. data/lib/quadtone/curve_set.rb +196 -0
  29. data/lib/quadtone/descendants.rb +9 -0
  30. data/lib/quadtone/environment.rb +5 -0
  31. data/lib/quadtone/extensions/math.rb +11 -0
  32. data/lib/quadtone/extensions/pathname3.rb +11 -0
  33. data/lib/quadtone/printer.rb +106 -0
  34. data/lib/quadtone/profile.rb +217 -0
  35. data/lib/quadtone/quad_file.rb +59 -0
  36. data/lib/quadtone/renderer.rb +139 -0
  37. data/lib/quadtone/run.rb +10 -0
  38. data/lib/quadtone/sample.rb +32 -0
  39. data/lib/quadtone/separator.rb +36 -0
  40. data/lib/quadtone/target.rb +277 -0
  41. data/lib/quadtone/tool.rb +61 -0
  42. data/lib/quadtone/tools/add_printer.rb +73 -0
  43. data/lib/quadtone/tools/characterize.rb +43 -0
  44. data/lib/quadtone/tools/chart.rb +31 -0
  45. data/lib/quadtone/tools/check.rb +16 -0
  46. data/lib/quadtone/tools/dir.rb +15 -0
  47. data/lib/quadtone/tools/edit.rb +23 -0
  48. data/lib/quadtone/tools/init.rb +82 -0
  49. data/lib/quadtone/tools/install.rb +15 -0
  50. data/lib/quadtone/tools/linearize.rb +28 -0
  51. data/lib/quadtone/tools/list.rb +19 -0
  52. data/lib/quadtone/tools/print.rb +38 -0
  53. data/lib/quadtone/tools/printer_options.rb +40 -0
  54. data/lib/quadtone/tools/rename.rb +17 -0
  55. data/lib/quadtone/tools/render.rb +43 -0
  56. data/lib/quadtone/tools/rewrite.rb +15 -0
  57. data/lib/quadtone/tools/separate.rb +71 -0
  58. data/lib/quadtone/tools/show.rb +15 -0
  59. data/lib/quadtone/tools/test.rb +26 -0
  60. data/qttk.gemspec +34 -0
  61. metadata +215 -0
@@ -0,0 +1,137 @@
1
+ module Quadtone
2
+
3
+ class CGATS
4
+
5
+ attr_accessor :sections
6
+
7
+ def self.new_from_file(file)
8
+ cgats = new
9
+ section_index = 0
10
+ state = :header
11
+ Pathname.new(file).readlines.each do |line|
12
+ line.chomp!
13
+ line.sub!(/#.*/, '')
14
+ line.strip!
15
+ next if line.empty?
16
+ section = cgats.sections[section_index] || cgats.add_section
17
+ case state
18
+ when :header
19
+ case line
20
+ when 'BEGIN_DATA_FORMAT'
21
+ state = :data_format
22
+ when 'BEGIN_DATA'
23
+ state = :data
24
+ else
25
+ key, value = line.split(/\s+/, 2)
26
+ if section.header[key]
27
+ if !section.header[key].kind_of?(Array)
28
+ section.header[key] = [section.header[key]]
29
+ end
30
+ section.header[key] << value
31
+ else
32
+ section.header[key] = value
33
+ end
34
+ end
35
+ when :data_format
36
+ case line
37
+ when 'END_DATA_FORMAT'
38
+ state = :header
39
+ else
40
+ line.split(/\s+/).each { |f| section.data_fields << f }
41
+ end
42
+ when :data
43
+ case line
44
+ when 'END_DATA'
45
+ # Emission data (BEGIN_DATA_EMISSION) may come after here, but we don't handle it.
46
+ section_index += 1
47
+ state = :header
48
+ else
49
+ values = line.split(/\s+/).map do |v|
50
+ case v
51
+ when /^-?\d+$/
52
+ v.to_i
53
+ when /^-?\d+\.\d+$/
54
+ v.to_f
55
+ else
56
+ v.to_s
57
+ end
58
+ end
59
+ set = {}
60
+ values.each_with_index do |value, i|
61
+ set[section.data_fields[i]] = value
62
+ end
63
+ section.data << set
64
+ end
65
+ end
66
+ end
67
+ cgats
68
+ end
69
+
70
+ def initialize
71
+ @sections = []
72
+ end
73
+
74
+ def add_section
75
+ @sections << Section.new
76
+ @sections[-1]
77
+ end
78
+
79
+ def write(io)
80
+ @sections.each do |section|
81
+ section.write(io)
82
+ io.puts
83
+ end
84
+ nil
85
+ end
86
+
87
+ class Section
88
+
89
+ attr_accessor :header
90
+ attr_accessor :data
91
+ attr_accessor :data_fields
92
+
93
+ def initialize
94
+ @header = {}
95
+ @data = []
96
+ @data_fields = []
97
+ end
98
+
99
+ def <<(set)
100
+ @data << set
101
+ end
102
+
103
+ def write(io)
104
+ # header
105
+ @header.each { |k, v| io.puts k.to_s + (v ? " \"#{v}\"" : '') }
106
+ # data format
107
+ io.puts
108
+ io.puts "NUMBER_OF_FIELDS #{@data_fields.length}"
109
+ io.puts 'BEGIN_DATA_FORMAT'
110
+ io.puts @data_fields.join(' ')
111
+ io.puts 'END_DATA_FORMAT'
112
+ # data
113
+ io.puts
114
+ io.puts "NUMBER_OF_SETS #{@data.length}"
115
+ io.puts 'BEGIN_DATA'
116
+ @data.each do |set|
117
+ fields = @data_fields.map do |f|
118
+ case (d = set[f])
119
+ when Float
120
+ '%.05f' % d
121
+ when String
122
+ '"' + d + '"'
123
+ else
124
+ d
125
+ end
126
+ end
127
+ io.puts fields.join(' ')
128
+ end
129
+ io.puts 'END_DATA'
130
+ nil
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+
137
+ end
@@ -0,0 +1,81 @@
1
+ # after:
2
+ # http://colinfdrake.com/2011/05/28/clustering-in-ruby.html
3
+ # http://m635j520.blogspot.com/2013/02/implementing-k-means-clustering-in-ruby.html
4
+
5
+ class ClusterCalculator
6
+
7
+ class Cluster
8
+
9
+ attr_accessor :center
10
+ attr_accessor :samples
11
+ attr_accessor :moved
12
+
13
+ def initialize(center)
14
+ @center = center
15
+ @samples = []
16
+ @moved = true
17
+ end
18
+
19
+ def add_sample(sample)
20
+ @samples << sample
21
+ end
22
+
23
+ def clear_samples
24
+ @samples = []
25
+ end
26
+
27
+ def distance_to(sample)
28
+ @center.delta_e(sample.output)
29
+ end
30
+
31
+ def update_center(delta=0.001)
32
+ @moved = false
33
+ average, error = Color::Lab.average(@samples.map(&:output))
34
+ unless average.delta_e(@center) < delta
35
+ @center = average
36
+ @moved = true
37
+ end
38
+ end
39
+
40
+ def size
41
+ @samples.length
42
+ end
43
+
44
+ end
45
+
46
+ attr_accessor :samples
47
+ attr_accessor :max_clusters
48
+ attr_accessor :delta
49
+ attr_accessor :clusters
50
+
51
+ def initialize(params={})
52
+ @delta = 0.001
53
+ params.each { |k, v| send("#{k}=", v) }
54
+ raise "Must specify samples" unless @samples
55
+ raise "Must specify max_clusters" unless @max_clusters
56
+ @max_clusters = @samples.length if @max_clusters > @samples.length
57
+ end
58
+
59
+ def cluster!
60
+ @clusters = @max_clusters.times.map { Cluster.new(@samples.sample.output) }
61
+ while @clusters.any?(&:moved)
62
+ @clusters.each(&:clear_samples)
63
+ @samples.each do |sample|
64
+ shortest = Float::INFINITY
65
+ cluster_found = nil
66
+ @clusters.each do |cluster|
67
+ distance = cluster.distance_to(sample)
68
+ if distance < shortest
69
+ cluster_found = cluster
70
+ shortest = distance
71
+ end
72
+ end
73
+ cluster_found.add_sample(sample) if cluster_found
74
+ end
75
+ @clusters.delete_if { |c| c.size == 0 }
76
+ @clusters.each { |c| c.update_center(@delta) }
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,83 @@
1
+ module Color
2
+
3
+ class Base
4
+
5
+ include Math
6
+
7
+ attr_accessor :components
8
+
9
+ def self.component_names
10
+ raise NotImplementedError, "\#component_names not implemented in #{self}"
11
+ end
12
+
13
+ def self.cgats_fields
14
+ raise NotImplementedError, "\#cgats_fields not implemented in #{self}"
15
+ end
16
+
17
+ def self.num_components
18
+ component_names.length
19
+ end
20
+
21
+ def self.colorspace_name
22
+ self.to_s.split('::').last.downcase
23
+ end
24
+
25
+ def self.from_cgats(set)
26
+ new(set.values_at(*cgats_fields))
27
+ end
28
+
29
+ def self.average(colors)
30
+ avg_components = []
31
+ errors = []
32
+ component_names.each_with_index do |comp, i|
33
+ avg_components << colors.map { |c| c.components[i] }.mean
34
+ errors << colors.map { |c| c.components[i] }.standard_deviation
35
+ end
36
+ [new(avg_components), errors.max]
37
+ end
38
+
39
+ def initialize(arg)
40
+ components = case arg
41
+ when String
42
+ arg =~ /^(\w+)\((.+)\)$/ or raise "Can't initialize #{self.class}: bad color string: #{arg.inspect}"
43
+ raise "Expected #{self.class.colorspace_name.inspect} but got #{$1.inspect}" if $1.downcase != self.class.colorspace_name
44
+ $2.split(/,\s+/).map(&:to_f)
45
+ when Hash
46
+ self.class.component_names.map { |n| arg[n] }
47
+ when Array
48
+ arg.map(&:to_f)
49
+ else
50
+ raise "Can't initialize #{self.class}: unknown object: #{arg.inspect}"
51
+ end
52
+ raise "Can't initialize #{self.class}: too many components specified: #{components.inspect}" if components.length > self.class.num_components
53
+ @components = [0] * self.class.num_components
54
+ components.each_with_index { |n, i| @components[i] = n if n }
55
+ end
56
+
57
+ def to_s
58
+ "#{self.class.colorspace_name}(#{@components.map { |n| '%3.1f' % n }.join(', ')})"
59
+ end
60
+
61
+ def to_str
62
+ to_s
63
+ end
64
+
65
+ def to_cgats
66
+ @components
67
+ end
68
+
69
+ def hash
70
+ @components.hash
71
+ end
72
+
73
+ def eql?(other)
74
+ @components == other.components
75
+ end
76
+
77
+ def <=>(other)
78
+ @components <=> other.components
79
+ end
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,112 @@
1
+ module Color
2
+
3
+ class CMYK < Base
4
+
5
+ def self.component_names
6
+ [:c, :m, :y, :k, :lc, :lm, :lk, :llk]
7
+ end
8
+
9
+ def self.cgats_fields
10
+ %w{CMYKcmk1k_C CMYKcmk1k_M CMYKcmk1k_Y CMYKcmk1k_K
11
+ CMYKcmk1k_c CMYKcmk1k_m CMYKcmk1k_k CMYKcmk1k_1k}
12
+ end
13
+
14
+ def self.cgats_color_rep
15
+ 'CMYKcmk1k'
16
+ end
17
+
18
+ def c
19
+ @components[0]
20
+ end
21
+
22
+ def m
23
+ @components[1]
24
+ end
25
+
26
+ def y
27
+ @components[2]
28
+ end
29
+
30
+ def k
31
+ @components[3]
32
+ end
33
+
34
+ def lc
35
+ @components[4]
36
+ end
37
+
38
+ def lm
39
+ @components[5]
40
+ end
41
+
42
+ def lk
43
+ @components[6]
44
+ end
45
+
46
+ def llk
47
+ @components[7]
48
+ end
49
+
50
+ def to_cgats
51
+ {
52
+ 'CMYKcmk1k_C' => c,
53
+ 'CMYKcmk1k_M' => m,
54
+ 'CMYKcmk1k_Y' => y,
55
+ 'CMYKcmk1k_K' => k,
56
+ 'CMYKcmk1k_c' => lc,
57
+ 'CMYKcmk1k_m' => lm,
58
+ 'CMYKcmk1k_k' => lk,
59
+ 'CMYKcmk1k_1k' => llk,
60
+ }
61
+ end
62
+
63
+ def to_cmyk
64
+ # estimates for light & light-light inks
65
+ l_factor = 0.5
66
+ ll_factor = 0.25
67
+
68
+ # first adjust for light inks
69
+ c0 = c + (lc * l_factor)
70
+ m0 = m + (lm * l_factor)
71
+ y0 = y
72
+ k0 = k + (lk * l_factor) + (llk * ll_factor)
73
+
74
+ Color::CMYK.new([c0, m0, y0, k0])
75
+ end
76
+
77
+ def to_cmy
78
+ # after http://www.easyrgb.com/index.php?X=MATH&H=14#text14
79
+
80
+ c0, m0, y0, k0 = *to_cmyk
81
+ c0 /= 100.0
82
+ m0 /= 100.0
83
+ y0 /= 100.0
84
+ k0 /= 100.0
85
+
86
+ c0 = (c0 * (1 - k0)) + k0
87
+ m0 = (m0 * (1 - k0)) + k0
88
+ y0 = (y0 * (1 - k0)) + k0
89
+
90
+ Color::CMYK.new([c0 * 100, m0 * 100, y0 * 100])
91
+ end
92
+
93
+ def to_rgb
94
+ cmy = to_cmy
95
+ Color::RGB.new([1 - (cmy.c / 100), 1 - (cmy.m / 100), 1 - (cmy.y / 100)])
96
+ end
97
+
98
+ def to_lab
99
+ to_xyz.to_lab
100
+ end
101
+
102
+ def to_xyz
103
+ to_rgb.to_xyz
104
+ end
105
+
106
+ def to_a
107
+ @components
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,23 @@
1
+ module Color
2
+
3
+ class DeviceN < Base
4
+
5
+ Components = (0..7).to_a
6
+
7
+ def self.component_names
8
+ Components
9
+ end
10
+
11
+ def self.cgats_fields
12
+ Components.map do |i|
13
+ "DEVICE_#{i}"
14
+ end
15
+ end
16
+
17
+ def to_gray
18
+ Color::Gray.new(@components.find { |c| c != 0 } || 0)
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,46 @@
1
+ module Color
2
+
3
+ class Gray < Base
4
+
5
+ def self.component_names
6
+ [:k]
7
+ end
8
+
9
+ def self.cgats_fields
10
+ %w{GRAY_K}
11
+ end
12
+
13
+ def self.cgats_color_rep
14
+ 'K'
15
+ end
16
+
17
+ def k
18
+ @components[0]
19
+ end
20
+
21
+ def value
22
+ k / 100.0
23
+ end
24
+
25
+ def to_cgats
26
+ {
27
+ 'GRAY_K' => k,
28
+ }
29
+ end
30
+
31
+ def to_rgb
32
+ n = 1 - value
33
+ Color::RGB.new([n, n, n])
34
+ end
35
+
36
+ def to_lab
37
+ Color::Lab.new(l: 100 - k, a: 0, b: 0)
38
+ end
39
+
40
+ def to_xyz
41
+ to_lab.to_xyz
42
+ end
43
+
44
+ end
45
+
46
+ end