Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # This routine needs the color_histogram method
- # from either ImageMagick 6.0.0 or GraphicsMagick 1.1
- # Specify an image filename as an argument.
- require 'RMagick'
- class PixelColumn < Array
- def initialize(size)
- super
- fill {Magick::Pixel.new}
- end
- def reset(bg)
- each {|pixel| pixel.reset(bg)}
- end
- end
- module Magick
- class Pixel
- def reset(bg)
- self.red = bg.red
- self.green = bg.green
- self.blue = bg.blue
- self.opacity = bg.opacity
- end
- end
- class Image
- private
- HISTOGRAM_COLS = 256
- HISTOGRAM_ROWS = 200
- MAX_QUANTUM = 255
- AIR_FACTOR = 1.025
- # The alpha frequencies are shown as white dots.
- def alpha_hist(freqs, scale, fg, bg)
- histogram = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
- self.background_color = bg
- self.border_color = fg
- }
- gc = Draw.new
- gc.affine(1, 0, 0, -scale, 0, HISTOGRAM_ROWS)
- gc.fill('white')
- HISTOGRAM_COLS.times do |x|
- gc.point(x, freqs[x])
- end
- gc.draw(histogram)
- histogram['Label'] = 'Alpha'
- return histogram
- end
- def channel_histograms(red, green, blue, int, scale, fg, bg)
- rgb_histogram = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
- self.background_color = bg
- self.border_color = fg
- }
- rgb_histogram['Label'] = 'RGB'
- red_histogram = rgb_histogram.copy
- red_histogram['Label'] = 'Red'
- green_histogram = rgb_histogram.copy
- green_histogram['Label'] = 'Green'
- blue_histogram = rgb_histogram.copy
- blue_histogram['Label'] = 'Blue'
- int_histogram = rgb_histogram.copy
- int_histogram['Label'] = 'Intensity'
- int_histogram.matte = true
- rgb_column = PixelColumn.new(HISTOGRAM_ROWS)
- red_column = PixelColumn.new(HISTOGRAM_ROWS)
- green_column = PixelColumn.new(HISTOGRAM_ROWS)
- blue_column = PixelColumn.new(HISTOGRAM_ROWS)
- int_column = PixelColumn.new(HISTOGRAM_ROWS)
- HISTOGRAM_COLS.times do |x|
- HISTOGRAM_ROWS.times do |y|
- yf = Float(y)
- if yf >= HISTOGRAM_ROWS - (red[x] * scale)
- red_column[y].red = MaxRGB
- red_column[y].green = 0
- red_column[y].blue = 0
- rgb_column[y].red = MaxRGB
- end
- if yf >= HISTOGRAM_ROWS - (green[x] * scale)
- green_column[y].green = MaxRGB
- green_column[y].red = 0
- green_column[y].blue = 0
- rgb_column[y].green = MaxRGB
- end
- if yf >= HISTOGRAM_ROWS - (blue[x] * scale)
- blue_column[y].blue = MaxRGB
- blue_column[y].red = 0
- blue_column[y].green = 0
- rgb_column[y].blue = MaxRGB
- end
- if yf >= HISTOGRAM_ROWS - (int[x] * scale)
- int_column[y].opacity = TransparentOpacity
- end
- end
- rgb_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, rgb_column)
- red_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, red_column)
- green_histogram.store_pixels(x, 0, 1, HISTOGRAM_ROWS, green_column)
- blue_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, blue_column)
- int_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, int_column)
- rgb_column.reset(bg)
- red_column.reset(bg)
- green_column.reset(bg)
- blue_column.reset(bg)
- int_column.reset(bg)
- end
- return red_histogram, green_histogram, blue_histogram, rgb_histogram, int_histogram
- end
- # Make the color histogram. Quantize the image to 256 colors if necessary.
- def color_hist(fg, bg)
- img = number_colors > 256 ? quantize(256) : self
- begin
- hist = img.color_histogram
- rescue NotImplementedError
- $stderr.puts "The color_histogram method is not supported by this version "+
- "of ImageMagick/GraphicsMagick"
- else
- pixels = hist.keys.sort_by {|pixel| hist[pixel] }
- scale = HISTOGRAM_ROWS / (hist.values.max*AIR_FACTOR)
- histogram = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
- self.background_color = bg
- self.border_color = fg
- }
- x = 0
- pixels.each do |pixel|
- column = Array.new(HISTOGRAM_ROWS).fill {Pixel.new}
- HISTOGRAM_ROWS.times do |y|
- if y >= HISTOGRAM_ROWS - (hist[pixel] * scale)
- column[y] = pixel;
- end
- end
- histogram.store_pixels(x, 0, 1, HISTOGRAM_ROWS, column)
- x = x.succ
- end
- histogram['Label'] = 'Color Frequency'
- return histogram
- end
- end
- # Use AnnotateImage to write the stats.
- def info_text(fg, bg)
- klass = class_type == DirectClass ? "DirectClass" : "PsuedoClass"
- text = <<-END_TEXT
- Format: #{format}
- Geometry: #{columns}x#{rows}
- Class: #{klass}
- Depth: #{depth} bits-per-pixel component
- Colors: #{number_colors}
- END_TEXT
- info = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
- self.background_color = bg
- self.border_color = fg
- }
- gc = Draw.new
- gc.annotate(info, 0, 0, 0, 0, text) {
- self.stroke = 'transparent'
- self.fill = fg
- self.gravity = CenterGravity
- }
- info['Label'] = 'Info'
- return info
- end
- def intensity_hist(int_histogram)
- gradient = (Image.read("gradient:#ffff80-#ff9000") { self.size="#{HISTOGRAM_COLS}x#{HISTOGRAM_ROWS}" }).first
- int_histogram = gradient.composite(int_histogram, CenterGravity, OverCompositeOp)
- int_histogram['Label'] = 'Intensity'
- return int_histogram
- end
- # Returns a value between 0 and MAX_QUANTUM. Same as the PixelIntensity macro.
- def pixel_intensity(pixel)
- (306*(pixel.red & MAX_QUANTUM) + 601*(pixel.green & MAX_QUANTUM) + 117*(pixel.blue & MAX_QUANTUM))/1024
- end
- public
- # Create the histogram montage.
- def histogram(fg='white', bg='black')
- red = Array.new(HISTOGRAM_COLS, 0)
- green = Array.new(HISTOGRAM_COLS, 0)
- blue = Array.new(HISTOGRAM_COLS, 0)
- alpha = Array.new(HISTOGRAM_COLS, 0)
- int = Array.new(HISTOGRAM_COLS, 0)
- rows.times do |row|
- pixels = get_pixels(0, row, columns, 1)
- pixels.each do |pixel|
- red[pixel.red & MAX_QUANTUM] += 1
- green[pixel.green & MAX_QUANTUM] += 1
- blue[pixel.blue & MAX_QUANTUM] += 1
- # Only count opacity channel if some pixels are not opaque.
- if !opaque?
- alpha[pixel.opacity & MAX_QUANTUM] += 1
- end
- v = pixel_intensity(pixel)
- int[v] += 1
- end
- end
- # Scale to chart size. When computing the scale, add some "air" between
- # the max frequency and the top of the histogram. This makes a prettier chart.
- # The RGBA and intensity histograms are all drawn to the same scale.
- max = [red.max, green.max, blue.max, alpha.max, int.max].max
- scale = HISTOGRAM_ROWS / (max*AIR_FACTOR)
- charts = ImageList.new
- # Add the thumbnail.
- thumb = copy
- thumb['Label'] = File.basename(filename)
- charts << thumb
- # Compute the channel and intensity histograms.
- channel_hists = channel_histograms(red, green, blue, int, scale, fg, bg)
- # Add the red, green, and blue histograms to the list
- charts << channel_hists.shift
- charts << channel_hists.shift
- charts << channel_hists.shift
- # Add Alpha channel or image stats
- if !opaque?
- charts << alpha_hist(alpha, scale, fg, bg)
- else
- charts << info_text(fg, bg)
- end
- # Add the RGB histogram
- charts << channel_hists.shift
- # Add the intensity histogram.
- charts << intensity_hist(channel_hists.shift)
- # Add the color frequency histogram.
- charts << color_hist(fg, bg)
- # Make a montage.
- histogram = charts.montage {
- self.background_color = bg
- self.stroke = 'transparent'
- self.fill = fg
- self.border_width = 1
- self.tile = "4x2"
- self.geometry = "#{HISTOGRAM_COLS}x#{HISTOGRAM_ROWS}+10+10"
- }
- return histogram
- end
- end
- end
- puts <<END_INFO
- This example shows how to get pixel-level access to an image.
- Usage: histogram.rb <image-filename>
- END_INFO
- # Get filename from command line.
- if !ARGV[0] then
- puts "No filename argument. Defaulting to Flower_Hat.jpg"
- filename = '../doc/ex/images/Flower_Hat.jpg'
- else
- filename = ARGV[0]
- end
- # Only process first frame if multi-frame image
- image = Magick::Image.read(filename)
- if image.length > 1
- puts "Charting 1st image"
- end
- image = image.first
- # Give the user something to look at while we're working.
- name = File.basename(filename).sub(/\..*?$/,'')
- $defout.sync = true
- printf "Creating #{name}_Histogram.miff"
- timer = Thread.new do
- loop do
- sleep(1)
- printf "."
- end
- end
- # Generate the histograms
- histogram = image.histogram(Magick::Pixel.from_color('white'), Magick::Pixel.from_color('black'))
- # Write output file
- histogram.compression = Magick::ZipCompression
- histogram.write("./#{name}_Histogram.miff")
- Thread.kill(timer)
- puts "Done!"
- exit
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement