qttk 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 +7 -0
- data/.gitignore +2 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +36 -0
- data/README.markdown +131 -0
- data/Rakefile +17 -0
- data/TODO.markdown +236 -0
- data/bin/qt +7 -0
- data/etc/gutenprint/gp-tool.rb +56 -0
- data/etc/gutenprint/gutenprint-filter.c +400 -0
- data/etc/gutenprint/gutenprint.rb +86 -0
- data/etc/gutenprint/stp-test +326 -0
- data/etc/images/3551599565_db282cf840_o.jpg +0 -0
- data/etc/images/4843122063_d582c569e9_o.jpg +0 -0
- data/etc/images/4843128953_83c1770907_o.jpg +0 -0
- data/lib/quadtone.rb +56 -0
- data/lib/quadtone/cgats.rb +137 -0
- data/lib/quadtone/cluster_calculator.rb +81 -0
- data/lib/quadtone/color.rb +83 -0
- data/lib/quadtone/color/cmyk.rb +112 -0
- data/lib/quadtone/color/device_n.rb +23 -0
- data/lib/quadtone/color/gray.rb +46 -0
- data/lib/quadtone/color/lab.rb +150 -0
- data/lib/quadtone/color/qtr.rb +71 -0
- data/lib/quadtone/color/rgb.rb +71 -0
- data/lib/quadtone/color/xyz.rb +80 -0
- data/lib/quadtone/curve.rb +138 -0
- data/lib/quadtone/curve_set.rb +196 -0
- data/lib/quadtone/descendants.rb +9 -0
- data/lib/quadtone/environment.rb +5 -0
- data/lib/quadtone/extensions/math.rb +11 -0
- data/lib/quadtone/extensions/pathname3.rb +11 -0
- data/lib/quadtone/printer.rb +106 -0
- data/lib/quadtone/profile.rb +217 -0
- data/lib/quadtone/quad_file.rb +59 -0
- data/lib/quadtone/renderer.rb +139 -0
- data/lib/quadtone/run.rb +10 -0
- data/lib/quadtone/sample.rb +32 -0
- data/lib/quadtone/separator.rb +36 -0
- data/lib/quadtone/target.rb +277 -0
- data/lib/quadtone/tool.rb +61 -0
- data/lib/quadtone/tools/add_printer.rb +73 -0
- data/lib/quadtone/tools/characterize.rb +43 -0
- data/lib/quadtone/tools/chart.rb +31 -0
- data/lib/quadtone/tools/check.rb +16 -0
- data/lib/quadtone/tools/dir.rb +15 -0
- data/lib/quadtone/tools/edit.rb +23 -0
- data/lib/quadtone/tools/init.rb +82 -0
- data/lib/quadtone/tools/install.rb +15 -0
- data/lib/quadtone/tools/linearize.rb +28 -0
- data/lib/quadtone/tools/list.rb +19 -0
- data/lib/quadtone/tools/print.rb +38 -0
- data/lib/quadtone/tools/printer_options.rb +40 -0
- data/lib/quadtone/tools/rename.rb +17 -0
- data/lib/quadtone/tools/render.rb +43 -0
- data/lib/quadtone/tools/rewrite.rb +15 -0
- data/lib/quadtone/tools/separate.rb +71 -0
- data/lib/quadtone/tools/show.rb +15 -0
- data/lib/quadtone/tools/test.rb +26 -0
- data/qttk.gemspec +34 -0
- 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
|