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.
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,59 @@
1
+ module Quadtone
2
+
3
+ class QuadFile
4
+
5
+ attr_accessor :curve_set
6
+
7
+ ChannelAliases = {
8
+ 'C' => 'c',
9
+ 'M' => 'm',
10
+ 'Y' => 'y',
11
+ 'K' => 'k',
12
+ 'c' => 'lc',
13
+ 'm' => 'lm',
14
+ 'k' => 'lk',
15
+ }
16
+
17
+ def initialize(profile)
18
+ @profile = profile
19
+ @curve_set = CurveSet.new(channels: [], profile: @profile, type: :separation)
20
+ load(@profile.quad_file_path)
21
+ end
22
+
23
+ # Read QTR quad (curve) file
24
+
25
+ def load(quad_file)
26
+ ;;warn "reading #{quad_file}"
27
+ lines = Pathname.new(quad_file).open.readlines.map { |line| line.chomp.force_encoding('ISO-8859-1') }
28
+ # process header
29
+ channels = parse_channel_list(lines.shift)
30
+ channels.each do |channel|
31
+ samples = (0..255).to_a.map do |input|
32
+ lines.shift while lines.first =~ /^#/
33
+ line = lines.shift
34
+ line =~ /^(\d+)$/ or raise "Unexpected value: #{line.inspect}"
35
+ output = $1.to_i
36
+ Sample.new(input: Color::Gray.new(k: 100 * (input / 255.0)), output: Color::Gray.new(k: 100 * (output / 65535.0)))
37
+ end
38
+ if @profile.inks.include?(channel)
39
+ @curve_set.curves << Curve.new(channel: channel, samples: samples)
40
+ end
41
+ end
42
+ end
43
+
44
+ def parse_channel_list(line)
45
+ # "## QuadToneRIP K,C,M,Y,LC,LM"
46
+ # "## QuadToneRIP KCMY"
47
+ line =~ /^##\s+QuadToneRIP\s+(.*)$/ or raise "Unexpected header line: #{line.inspect}"
48
+ channel_list = $1
49
+ case channel_list
50
+ when /,/
51
+ channel_list.split(',')
52
+ else
53
+ channel_list.chars.map { |c| ChannelAliases[c] }
54
+ end.map { |c| c.downcase.to_sym }
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,139 @@
1
+ module Quadtone
2
+
3
+ class Renderer
4
+
5
+ attr_accessor :gamma
6
+ attr_accessor :rotate
7
+ attr_accessor :compress
8
+ attr_accessor :page_size
9
+ attr_accessor :resolution
10
+ attr_accessor :desired_size
11
+
12
+ def initialize(params={})
13
+ @compress = true
14
+ @resolution = 720
15
+ params.each { |k, v| send("#{k}=", v) }
16
+ end
17
+
18
+ def render(input_path)
19
+ @input_path = input_path
20
+
21
+ raise "Page size required" unless @page_size
22
+
23
+ # Scale measurements to specified resolution
24
+
25
+ @page_size.width = (@page_size.width / 72.0 * @resolution).to_i
26
+ @page_size.height = (@page_size.height / 72.0 * @resolution).to_i
27
+ @page_size.imageable_width = (@page_size.imageable_width / 72.0 * @resolution).to_i
28
+ @page_size.imageable_height = (@page_size.imageable_height / 72.0 * @resolution).to_i
29
+ @page_size.margin.left = (@page_size.margin.left / 72.0 * @resolution).to_i
30
+ @page_size.margin.right = (@page_size.margin.right / 72.0 * @resolution).to_i
31
+ @page_size.margin.top = (@page_size.margin.top / 72.0 * @resolution).to_i
32
+ @page_size.margin.bottom = (@page_size.margin.bottom / 72.0 * @resolution).to_i
33
+
34
+ if @desired_size
35
+ @desired_size.width = (@desired_size.width * @resolution).to_i
36
+ @desired_size.height = (@desired_size.height * @resolution).to_i
37
+ if @desired_size.width > @page_size.imageable_width || @desired_size.height > @page_size.imageable_height
38
+ raise "Image too large for page size (#{@page_size.name})"
39
+ end
40
+ else
41
+ @desired_size = HashStruct.new(width: @page_size.imageable_width, height: @page_size.imageable_height)
42
+ end
43
+
44
+ ;;warn "Reading #{@input_path} @ #{@resolution}dpi"
45
+ r = @resolution # have to alias to avoid referring to ImageList object
46
+ image_list = Magick::ImageList.new(@input_path) {
47
+ self.density = r
48
+ }
49
+ output_paths = []
50
+ image_list.each_with_index do |image, image_index|
51
+ @current_image = image
52
+ @current_image_index = image_index
53
+ ;;warn "\t" + "Processing sub-image \##{@current_image_index}"
54
+ show_info
55
+ delete_profiles
56
+ convert_to_16bit
57
+ apply_gamma
58
+ rotate
59
+ resize
60
+ extend_to_page
61
+ crop_to_imageable_area
62
+ show_info
63
+ output_paths << write_to_file
64
+ end
65
+ output_paths
66
+ end
67
+
68
+ def output_path
69
+ params = []
70
+ params << "#{@desired_size.width}x#{@desired_size.height}"
71
+ params << @page_size.name
72
+ params << "@#{@resolution}"
73
+ params << "g#{@gamma}" if @gamma
74
+ @input_path.with_extname(".out-#{params.join('-')}.#{@current_image_index}.png")
75
+ end
76
+
77
+ def show_info
78
+ ;;warn "\t\t" + @current_image.inspect
79
+ end
80
+
81
+ def delete_profiles
82
+ ;;warn "\t\t" + "Deleting profiles"
83
+ @current_image.delete_profile('*')
84
+ end
85
+
86
+ def convert_to_16bit
87
+ ;;warn "\t\t" + "Changing to grayscale"
88
+ @current_image = @current_image.quantize(2 ** 16, Magick::GRAYColorspace)
89
+ end
90
+
91
+ def apply_gamma
92
+ if @gamma
93
+ ;;warn "\t\t" + "Applying gamma #{@gamma}"
94
+ @current_image = @current_image.gamma_correct(@gamma)
95
+ end
96
+ end
97
+
98
+ def rotate
99
+ if @rotate
100
+ ;;warn "\t\t" + "Rotating #{@rotate}°"
101
+ @current_image.rotate!(@rotate)
102
+ elsif (@current_image.columns.to_f / @current_image.rows) > (@page_size.width.to_f / @page_size.height)
103
+ ;;warn "\t\t" + "Auto-rotating 90°"
104
+ @current_image.rotate!(90)
105
+ end
106
+ end
107
+
108
+ def resize
109
+ ;;warn "\t\t" + "Resizing to desired size"
110
+ @current_image.resize_to_fit!(@desired_size.width, @desired_size.height)
111
+ end
112
+
113
+ def extend_to_page
114
+ ;;warn "\t\t" + "Extending canvas to page area"
115
+ @current_image = @current_image.extent(
116
+ @page_size.width,
117
+ @page_size.height,
118
+ -(@page_size.width - @current_image.columns) / 2,
119
+ -(@page_size.height - @current_image.rows) / 2)
120
+ end
121
+
122
+ def crop_to_imageable_area
123
+ x, y, w, h = @page_size.margin.left, @page_size.height - @page_size.margin.top, @page_size.imageable_width, @page_size.imageable_height
124
+ ;;warn "\t\t" + "Cropping to imageable area (x,y = #{x},#{y}, w,h = #{w},#{h})"
125
+ @current_image.crop!(x, y, w, h)
126
+ end
127
+
128
+ def write_to_file
129
+ path = output_path
130
+ ;;warn "\t\t" + "Writing image to #{path}"
131
+ @current_image.write(path) do
132
+ self.compression = @compress ? Magick::ZipCompression : Magick::NoCompression
133
+ end
134
+ path
135
+ end
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,10 @@
1
+ module Quadtone
2
+
3
+ def self.run(*args)
4
+ args = args.flatten.compact.map { |a| a.to_s }
5
+ warn "\t* #{args.join(' ')}"
6
+ system(*args)
7
+ raise "Error: #{$?}" unless $? == 0
8
+ end
9
+
10
+ end
@@ -0,0 +1,32 @@
1
+ module Quadtone
2
+
3
+ class Sample
4
+
5
+ attr_accessor :input
6
+ attr_accessor :output
7
+ attr_accessor :error
8
+ attr_accessor :label
9
+
10
+ def initialize(params={})
11
+ params.each { |key, value| send("#{key}=", value) }
12
+ end
13
+
14
+ def input_value
15
+ @input.value
16
+ end
17
+
18
+ def output_value
19
+ @output.value
20
+ end
21
+
22
+ def to_s
23
+ "%s / %s%s" % [
24
+ input,
25
+ output,
26
+ label ? " [#{label}]" : '',
27
+ ]
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,36 @@
1
+ module Quadtone
2
+
3
+ class Separator
4
+
5
+ attr_accessor :luts
6
+
7
+ def initialize(curve_set)
8
+ @luts = {}
9
+ curve_set.curves.each do |curve|
10
+ color_map = Magick::Image.new(curve.num_samples, 1) do
11
+ self.colorspace = Magick::GRAYColorspace
12
+ end
13
+ color_map.pixel_interpolation_method = Magick::IntegerInterpolatePixel
14
+ color_map.view(0, 0, curve.num_samples, 1) do |view|
15
+ curve.samples.each_with_index do |sample, i|
16
+ value = ((1 - sample.output.value) * 65535).to_i
17
+ view[0][curve.num_samples - 1 - i] = Magick::Pixel.new(value, value, value)
18
+ end
19
+ end
20
+ @luts[curve.channel] = color_map
21
+ end
22
+ end
23
+
24
+ def separate(image)
25
+ images = {}
26
+ @luts.each do |channel, lut|
27
+ image2 = image.copy.clut_channel(lut)
28
+ image2['Label'] = channel.to_s.upcase
29
+ images[channel] = image2
30
+ end
31
+ images
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,277 @@
1
+ module Quadtone
2
+
3
+ class Target
4
+
5
+ attr_accessor :base_dir
6
+ attr_accessor :channels
7
+ attr_accessor :type
8
+ attr_accessor :name
9
+ attr_accessor :ink_limits
10
+ attr_accessor :samples
11
+
12
+ def initialize(params={})
13
+ params.each { |k, v| send("#{k}=", v) }
14
+ raise "Base directory must be specified" unless @base_dir
15
+ raise "Channels must be specified" unless @channels
16
+ raise "Type must be specified" unless @type
17
+ raise "Name must be specified" unless @name
18
+ end
19
+
20
+ def build
21
+ ;;warn "Making target for channels #{@channels.inspect}"
22
+ cleanup_files(:all)
23
+
24
+ resolution = 360
25
+ page_size = HashStruct.new(width: 8, height: 10.5)
26
+ # total_patches = 42
27
+ ;;total_patches = 14
28
+ patches_per_row = 14
29
+ total_rows = total_patches / patches_per_row
30
+ row_height = 165
31
+ target_size = [
32
+ ((total_rows + 1) * row_height).to_f / resolution,
33
+ page_size.height,
34
+ ].map { |n| (n * 25.4).to_i }.join('x')
35
+
36
+ image_list = Magick::ImageList.new
37
+ @channels.each do |channel|
38
+ sub_path = base_file(channel)
39
+ sub_image_path = image_file(channel)
40
+ ;;warn "Making target #{sub_path.inspect} at #{sub_image_path}"
41
+ Quadtone.run('targen',
42
+ # '-v', # Verbose mode [optional level 1..N]
43
+ '-d', 0, # generate grayscale target
44
+ '-e', 0, # White test patches (default 4)
45
+ '-B', 0, # Black test patches (default 4 Grey/RGB, else 0)
46
+ '-s', total_patches, # Single channel steps (default grey 50, color 0)
47
+ sub_path)
48
+ Quadtone.run('printtarg',
49
+ # '-v', # Verbose mode [optional level 1..N]
50
+ '-a', 1.45, # Scale patch size and spacers by factor (e.g. 0.857 or 1.5 etc.)
51
+ '-r', # Don't randomize patch location
52
+ '-i', 'i1', # set instrument to EyeOne (FIXME: make configurable)
53
+ '-t', resolution, # generate 16-bit TIFF @ 360 ppi
54
+ '-m', 0, # Set a page margin in mm (default 6.0 mm)
55
+ '-L', # Suppress any left paper clip border
56
+ '-p', target_size, # Select page size
57
+ sub_path)
58
+ image = Magick::Image.read(sub_image_path).first
59
+ # image.background_color = 'transparent'
60
+ # image = image.transparent('white')
61
+ case @type
62
+ when :characterization
63
+ if @ink_limits && (limit = @ink_limits[channel]) && limit != 1
64
+ ;;warn "\t" + "#{channel.to_s.upcase}: Applying limit of #{limit}"
65
+ levels = [1.0 - limit, 1.0].map { |n| n * Magick::QuantumRange }
66
+ image = image.levelize_channel(*levels)
67
+ end
68
+ # calculate a black RGB pixel for this channel in QTR calibration mode
69
+ black_qtr = Color::QTR.new(channel: channel, value: 0)
70
+ black_rgb = black_qtr.to_rgb
71
+ ;;warn "\t" + "#{channel.to_s.upcase}: Colorizing to #{black_rgb}"
72
+ image = image.colorize(1, 0, 1, black_rgb.to_pixel)
73
+ end
74
+ image_list << image
75
+ end
76
+
77
+ if @type == :linearization || @type == :test
78
+
79
+ width = (page_size.width * resolution).to_i - image_list.first.columns
80
+ height = page_size.height * resolution
81
+
82
+ ;;width = (page_size.width / 2) * resolution
83
+
84
+ linear_scale_height = 1 * resolution
85
+ radial_scale_height = (height - linear_scale_height) / 2
86
+ sample_image_height = (height - linear_scale_height) / 2
87
+
88
+ test_images = Magick::ImageList.new
89
+ test_images << linear_gradation_scale_image([width, linear_scale_height])
90
+ test_images << radial_gradation_scale_image([width, radial_scale_height])
91
+ test_images << sample_image([width, sample_image_height])
92
+ image_list << test_images.append(true)
93
+ end
94
+
95
+ # ;;warn "montaging images"
96
+ # image_list = image_list.montage do
97
+ # self.geometry = Magick::Geometry.new(page_size.width * resolution, page_size.height * resolution)
98
+ # self.tile = Magick::Geometry.new(image_list.length, 1)
99
+ # end
100
+
101
+ # ;;warn "writing target image"
102
+ # image_list.write(image_file) do
103
+ # self.depth = (@type == :characterization) ? 8 : 16
104
+ # self.compression = Magick::ZipCompression
105
+ # end
106
+
107
+ ;;warn "writing target image"
108
+ image_list.append(false).write(image_file) do
109
+ self.depth = (@type == :characterization) ? 8 : 16
110
+ self.compression = Magick::ZipCompression
111
+ end
112
+
113
+ end
114
+
115
+ def linear_gradation_scale_image(size)
116
+ ;;warn "\t" + "generating linear gradation scale of size #{size.inspect}"
117
+ bounds = Magick::Rectangle.new(*size, 0, 0)
118
+ image1 = Magick::Image.new(bounds.width, bounds.height/2, Magick::GradientFill.new(0, 0, 0, bounds.height/2, 'white', 'black'))
119
+ image2 = image1.posterize(21)
120
+ image3 = image1.posterize(256)
121
+ ilist = Magick::ImageList.new
122
+ ilist << image1
123
+ ilist << image2
124
+ ilist << image3
125
+ ilist.append(true)
126
+ end
127
+
128
+ def radial_gradation_scale_image(size)
129
+ ;;warn "\t" + "generating radial gradation scale of size #{size.inspect}"
130
+ bounds = Magick::Rectangle.new(*size, 0, 0)
131
+ image1 = Magick::Image.new(bounds.width, bounds.height/2, Magick::GradientFill.new(bounds.width/2, bounds.height/2, bounds.width/2, bounds.height/2, 'black', 'white'))
132
+ image2 = image1.posterize(21).flip
133
+ ilist = Magick::ImageList.new
134
+ ilist << image1
135
+ ilist << image2
136
+ ilist.append(true)
137
+ end
138
+
139
+ def sample_image(size)
140
+ ;;warn "\t" + "generating sample image of size #{size.inspect}"
141
+ bounds = Magick::Rectangle.new(*size, 0, 0)
142
+ ilist = Magick::ImageList.new(Pathname.new(ENV['HOME']) + 'Desktop' + '121213b.01.tif')
143
+ ilist.first.resize_to_fill(*size)
144
+ end
145
+
146
+ def measure(options={})
147
+ options = HashStruct.new(options)
148
+ channels_to_measure = options.channels || @channels
149
+ channels_to_measure.each_with_index do |channel, i|
150
+ measure_channel(channel, options.merge(disable_calibration: i > 0))
151
+ end
152
+ end
153
+
154
+ def measure_channel(channel, options=HashStruct.new)
155
+ options = HashStruct.new(options)
156
+ if options.remeasure
157
+ pass = options.remeasure
158
+ else
159
+ pass = ti2_files(channel).length
160
+ end
161
+ base = base_file(channel, pass)
162
+ FileUtils.cp(ti2_file(channel), base.with_extname('.ti2')) unless options.remeasure
163
+ ;;warn "Measuring target #{base.inspect}"
164
+ Quadtone.run('chartread',
165
+ # '-v', # Verbose mode [optional level 1..N]
166
+ '-p', # Measure patch by patch rather than strip
167
+ '-n', # Don't save spectral information (default saves spectral)
168
+ '-l', # Save CIE as D50 L*a*b* rather than XYZ
169
+ options.disable_calibration ? '-N' : nil, # Disable initial calibration of instrument if possible
170
+ options.remeasure ? '-r' : nil, # Resume reading partly read chart
171
+ base)
172
+ end
173
+
174
+ def read
175
+ ;;warn "reading samples for #{@channels.join(', ')} from CGATS files"
176
+ @samples = {}
177
+ @channels.each do |channel|
178
+ samples = {}
179
+ ti3_files(channel).map do |file|
180
+ ;;warn "reading #{file}"
181
+ cgats = CGATS.new_from_file(file)
182
+ cgats.sections.first.data.each do |set|
183
+ id = set['SAMPLE_LOC'].gsub(/"/, '')
184
+ samples[id] ||= []
185
+ samples[id] << Sample.new(input: Color::Gray.from_cgats(set), output: Color::Lab.from_cgats(set))
186
+ end
187
+ end
188
+ @samples[channel] = samples.map do |id, samples|
189
+ cc = ClusterCalculator.new(samples: samples, max_clusters: samples.length > 2 ? 2 : 1)
190
+ cc.cluster!
191
+ clusters = cc.clusters.sort_by(&:size).reverse
192
+ # ;;warn "Clusters:"
193
+ # clusters.each do |cluster|
194
+ # warn "\t" + cluster.center.to_s
195
+ # cluster.samples.each do |sample|
196
+ # warn "\t\t" + "#{sample.to_s}"
197
+ # end
198
+ # end
199
+ # ;;
200
+ cluster = clusters.shift
201
+ raise "Too much spread" if cluster.samples.length < 2 && samples.length > 2
202
+ unless clusters.empty?
203
+ warn "Dropped #{clusters.length} out of range sample(s) at patch #{channel}-#{id}"
204
+ end
205
+ output = cluster.center
206
+ Sample.new(input: samples.first.input, output: output, label: "#{channel}-#{id}")
207
+ end
208
+ end
209
+ end
210
+
211
+ # private
212
+
213
+ def base_file(channel=nil, n=nil)
214
+ @base_dir + (@name.to_s + (channel ? "-#{channel}" : '') + (n ? "-#{n}" : ''))
215
+ end
216
+
217
+ def ti1_file(channel)
218
+ base_file(channel).with_extname('.ti1')
219
+ end
220
+
221
+ def ti2_file(channel, n=nil)
222
+ base_file(channel, n).with_extname('.ti2')
223
+ end
224
+
225
+ def ti3_file(channel, n=nil)
226
+ base_file(channel, n).with_extname('.ti3')
227
+ end
228
+
229
+ def image_file(channel=nil)
230
+ base_file(channel).with_extname('.tif')
231
+ end
232
+
233
+ def values_file(channel=nil)
234
+ base_file(channel).with_extname('.txt')
235
+ end
236
+
237
+ def ti_files
238
+ Pathname.glob(base_file.with_extname('*.ti[123]'))
239
+ end
240
+
241
+ def ti2_files(channel)
242
+ Pathname.glob(base_file(channel).with_extname('*.ti2'))
243
+ end
244
+
245
+ def ti3_files(channel)
246
+ Pathname.glob(base_file(channel).with_extname('*.ti3'))
247
+ end
248
+
249
+ def image_files
250
+ Pathname.glob(base_file.with_extname('*.tif'))
251
+ end
252
+
253
+ def cleanup_files(files)
254
+ ;;warn "deleting files: #{files.inspect}"
255
+ files = [files].flatten
256
+ until files.empty?
257
+ file = files.shift
258
+ case file
259
+ when :all
260
+ files << :ti
261
+ files += image_files
262
+ when :ti
263
+ files += ti_files
264
+ when Pathname
265
+ if file.exist?
266
+ # ;;warn "\t" + file
267
+ file.unlink
268
+ end
269
+ else
270
+ raise "Unknown file to cleanup: #{file}"
271
+ end
272
+ end
273
+ end
274
+
275
+ end
276
+
277
+ end