class Quadtone::Target
Attributes
base_dir[RW]
channels[RW]
ink_limits[RW]
name[RW]
samples[RW]
type[RW]
Public Class Methods
new(params={})
click to toggle source
# File lib/quadtone/target.rb, line 12 def initialize(params={}) params.each { |k, v| send("#{k}=", v) } raise "Base directory must be specified" unless @base_dir raise "Channels must be specified" unless @channels raise "Type must be specified" unless @type raise "Name must be specified" unless @name end
Public Instance Methods
base_file(channel=nil, n=nil)
click to toggle source
private
# File lib/quadtone/target.rb, line 214 def base_file(channel=nil, n=nil) @base_dir / (@name.to_s + (channel ? "-#{channel}" : '') + (n ? "-#{n}" : '')) end
build()
click to toggle source
# File lib/quadtone/target.rb, line 20 def build ;;warn "Making target for channels #{@channels.inspect}" cleanup_files(:all) resolution = 360 page_size = HashStruct.new(width: 8, height: 10.5) # total_patches = 42 ;;total_patches = 14 patches_per_row = 14 total_rows = total_patches / patches_per_row row_height = 165 target_size = [ ((total_rows + 1) * row_height).to_f / resolution, page_size.height, ].map { |n| (n * 25.4).to_i }.join('x') image_list = Magick::ImageList.new @channels.each do |channel| sub_path = base_file(channel) sub_image_path = image_file(channel) ;;warn "Making target #{sub_path.inspect} at #{sub_image_path}" Quadtone.run('targen', # '-v', # Verbose mode [optional level 1..N] '-d', 0, # generate grayscale target '-e', 0, # White test patches (default 4) '-B', 0, # Black test patches (default 4 Grey/RGB, else 0) '-s', total_patches, # Single channel steps (default grey 50, color 0) sub_path) Quadtone.run('printtarg', # '-v', # Verbose mode [optional level 1..N] '-a', 1.45, # Scale patch size and spacers by factor (e.g. 0.857 or 1.5 etc.) '-r', # Don't randomize patch location '-i', 'i1', # set instrument to EyeOne (FIXME: make configurable) '-t', resolution, # generate 16-bit TIFF @ 360 ppi '-m', 0, # Set a page margin in mm (default 6.0 mm) '-L', # Suppress any left paper clip border '-p', target_size, # Select page size sub_path) image = Magick::Image.read(sub_image_path).first # image.background_color = 'transparent' # image = image.transparent('white') case @type when :characterization if @ink_limits && (limit = @ink_limits[channel]) && limit != 1 ;;warn "\t" + "#{channel.to_s.upcase}: Applying limit of #{limit}" levels = [1.0 - limit, 1.0].map { |n| n * Magick::QuantumRange } image = image.levelize_channel(*levels) end # calculate a black RGB pixel for this channel in QTR calibration mode black_qtr = Color::QTR.new(channel: channel, value: 0) black_rgb = black_qtr.to_rgb ;;warn "\t" + "#{channel.to_s.upcase}: Colorizing to #{black_rgb}" image = image.colorize(1, 0, 1, black_rgb.to_pixel) end image_list << image end if @type == :linearization || @type == :test width = (page_size.width * resolution).to_i - image_list.first.columns height = page_size.height * resolution ;;width = (page_size.width / 2) * resolution linear_scale_height = 1 * resolution radial_scale_height = (height - linear_scale_height) / 2 sample_image_height = (height - linear_scale_height) / 2 test_images = Magick::ImageList.new test_images << linear_gradation_scale_image([width, linear_scale_height]) test_images << radial_gradation_scale_image([width, radial_scale_height]) test_images << sample_image([width, sample_image_height]) image_list << test_images.append(true) end # ;;warn "montaging images" # image_list = image_list.montage do # self.geometry = Magick::Geometry.new(page_size.width * resolution, page_size.height * resolution) # self.tile = Magick::Geometry.new(image_list.length, 1) # end # ;;warn "writing target image" # image_list.write(image_file) do # self.depth = (@type == :characterization) ? 8 : 16 # self.compression = Magick::ZipCompression # end ;;warn "writing target image" image_list.append(false).write(image_file) do self.depth = (@type == :characterization) ? 8 : 16 self.compression = Magick::ZipCompression end end
cleanup_files(files)
click to toggle source
# File lib/quadtone/target.rb, line 254 def cleanup_files(files) ;;warn "deleting files: #{files.inspect}" files = [files].flatten until files.empty? file = files.shift case file when :all files << :ti files += image_files when :ti files += ti_files when Path if file.exist? # ;;warn "\t" + file file.unlink end else raise "Unknown file to cleanup: #{file}" end end end
image_file(channel=nil)
click to toggle source
# File lib/quadtone/target.rb, line 230 def image_file(channel=nil) base_file(channel).with_extname('.tif') end
image_files()
click to toggle source
# File lib/quadtone/target.rb, line 250 def image_files Path.glob(base_file.with_extname('*.tif')) end
linear_gradation_scale_image(size)
click to toggle source
# File lib/quadtone/target.rb, line 115 def linear_gradation_scale_image(size) ;;warn "\t" + "generating linear gradation scale of size #{size.inspect}" bounds = Magick::Rectangle.new(*size, 0, 0) image1 = Magick::Image.new(bounds.width, bounds.height/2, Magick::GradientFill.new(0, 0, 0, bounds.height/2, 'white', 'black')) image2 = image1.posterize(21) image3 = image1.posterize(256) ilist = Magick::ImageList.new ilist << image1 ilist << image2 ilist << image3 ilist.append(true) end
measure(options={})
click to toggle source
# File lib/quadtone/target.rb, line 147 def measure(options={}) options = HashStruct.new(options) channels_to_measure = options.channels || @channels channels_to_measure.each_with_index do |channel, i| measure_channel(channel, options.merge(disable_calibration: i > 0)) end end
measure_channel(channel, options=HashStruct.new)
click to toggle source
# File lib/quadtone/target.rb, line 155 def measure_channel(channel, options=HashStruct.new) options = HashStruct.new(options) if options.remeasure pass = options.remeasure else pass = ti2_files(channel).length end base = base_file(channel, pass) FileUtils.cp(ti2_file(channel), base.with_extname('.ti2')) unless options.remeasure ;;warn "Measuring target #{base.inspect}" Quadtone.run('chartread', # '-v', # Verbose mode [optional level 1..N] '-p', # Measure patch by patch rather than strip '-n', # Don't save spectral information (default saves spectral) '-l', # Save CIE as D50 L*a*b* rather than XYZ options.disable_calibration ? '-N' : nil, # Disable initial calibration of instrument if possible options.remeasure ? '-r' : nil, # Resume reading partly read chart base) end
radial_gradation_scale_image(size)
click to toggle source
# File lib/quadtone/target.rb, line 128 def radial_gradation_scale_image(size) ;;warn "\t" + "generating radial gradation scale of size #{size.inspect}" bounds = Magick::Rectangle.new(*size, 0, 0) 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')) image2 = image1.posterize(21).flip ilist = Magick::ImageList.new ilist << image1 ilist << image2 ilist.append(true) end
read()
click to toggle source
# File lib/quadtone/target.rb, line 175 def read ;;warn "reading samples for #{@channels.join(', ')} from CGATS files" @samples = {} @channels.each do |channel| samples = {} ti3_files(channel).map do |file| ;;warn "reading #{file}" cgats = CGATS.new_from_file(file) cgats.sections.first.data.each do |set| id = set['SAMPLE_LOC'].gsub(/"/, '') samples[id] ||= [] samples[id] << Sample.new(input: Color::Gray.from_cgats(set), output: Color::Lab.from_cgats(set)) end end @samples[channel] = samples.map do |id, samples| cc = ClusterCalculator.new(samples: samples, max_clusters: samples.length > 2 ? 2 : 1) cc.cluster! clusters = cc.clusters.sort_by(&:size).reverse # ;;warn "Clusters:" # clusters.each do |cluster| # warn "\t" + cluster.center.to_s # cluster.samples.each do |sample| # warn "\t\t" + "#{sample.to_s}" # end # end # ;; cluster = clusters.shift raise "Too much spread" if cluster.samples.length < 2 && samples.length > 2 unless clusters.empty? warn "Dropped #{clusters.length} out of range sample(s) at patch #{channel}-#{id}" end output = cluster.center Sample.new(input: samples.first.input, output: output, label: "#{channel}-#{id}") end end end
sample_image(size)
click to toggle source
# File lib/quadtone/target.rb, line 139 def sample_image(size) ;;warn "\t" + "generating sample image of size #{size.inspect}" bounds = Magick::Rectangle.new(*size, 0, 0) file = Path.new(ENV['HOME']) / 'Desktop' / '121213b.01.tif' #FIXME: parameterize ilist = Magick::ImageList.new(file.to_s) ilist.first.resize_to_fill(*size) end
ti1_file(channel)
click to toggle source
# File lib/quadtone/target.rb, line 218 def ti1_file(channel) base_file(channel).with_extname('.ti1') end
ti2_file(channel, n=nil)
click to toggle source
# File lib/quadtone/target.rb, line 222 def ti2_file(channel, n=nil) base_file(channel, n).with_extname('.ti2') end
ti2_files(channel)
click to toggle source
# File lib/quadtone/target.rb, line 242 def ti2_files(channel) Path.glob(base_file(channel).with_extname('*.ti2')) end
ti3_file(channel, n=nil)
click to toggle source
# File lib/quadtone/target.rb, line 226 def ti3_file(channel, n=nil) base_file(channel, n).with_extname('.ti3') end
ti3_files(channel)
click to toggle source
# File lib/quadtone/target.rb, line 246 def ti3_files(channel) Path.glob(base_file(channel).with_extname('*.ti3')) end
ti_files()
click to toggle source
# File lib/quadtone/target.rb, line 238 def ti_files Path.glob(base_file.with_extname('*.ti[123]')) end
values_file(channel=nil)
click to toggle source
# File lib/quadtone/target.rb, line 234 def values_file(channel=nil) base_file(channel).with_extname('.txt') end