Advertisement
Guest User

Untitled

a guest
Apr 30th, 2016
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.47 KB | None | 0 0
  1. # This routine needs the color_histogram method
  2. # from either ImageMagick 6.0.0 or GraphicsMagick 1.1
  3. # Specify an image filename as an argument.
  4.  
  5. require 'RMagick'
  6.  
  7. class PixelColumn < Array
  8. def initialize(size)
  9. super
  10. fill {Magick::Pixel.new}
  11. end
  12.  
  13. def reset(bg)
  14. each {|pixel| pixel.reset(bg)}
  15. end
  16. end
  17.  
  18. module Magick
  19.  
  20. class Pixel
  21. def reset(bg)
  22. self.red = bg.red
  23. self.green = bg.green
  24. self.blue = bg.blue
  25. self.opacity = bg.opacity
  26. end
  27. end
  28.  
  29. class Image
  30.  
  31. private
  32. HISTOGRAM_COLS = 256
  33. HISTOGRAM_ROWS = 200
  34. MAX_QUANTUM = 255
  35. AIR_FACTOR = 1.025
  36.  
  37. # The alpha frequencies are shown as white dots.
  38. def alpha_hist(freqs, scale, fg, bg)
  39. histogram = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
  40. self.background_color = bg
  41. self.border_color = fg
  42. }
  43.  
  44. gc = Draw.new
  45. gc.affine(1, 0, 0, -scale, 0, HISTOGRAM_ROWS)
  46. gc.fill('white')
  47.  
  48. HISTOGRAM_COLS.times do |x|
  49. gc.point(x, freqs[x])
  50. end
  51.  
  52. gc.draw(histogram)
  53. histogram['Label'] = 'Alpha'
  54.  
  55. return histogram
  56. end
  57.  
  58. def channel_histograms(red, green, blue, int, scale, fg, bg)
  59. rgb_histogram = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
  60. self.background_color = bg
  61. self.border_color = fg
  62. }
  63. rgb_histogram['Label'] = 'RGB'
  64. red_histogram = rgb_histogram.copy
  65. red_histogram['Label'] = 'Red'
  66. green_histogram = rgb_histogram.copy
  67. green_histogram['Label'] = 'Green'
  68. blue_histogram = rgb_histogram.copy
  69. blue_histogram['Label'] = 'Blue'
  70. int_histogram = rgb_histogram.copy
  71. int_histogram['Label'] = 'Intensity'
  72. int_histogram.matte = true
  73.  
  74. rgb_column = PixelColumn.new(HISTOGRAM_ROWS)
  75. red_column = PixelColumn.new(HISTOGRAM_ROWS)
  76. green_column = PixelColumn.new(HISTOGRAM_ROWS)
  77. blue_column = PixelColumn.new(HISTOGRAM_ROWS)
  78. int_column = PixelColumn.new(HISTOGRAM_ROWS)
  79.  
  80. HISTOGRAM_COLS.times do |x|
  81. HISTOGRAM_ROWS.times do |y|
  82.  
  83. yf = Float(y)
  84. if yf >= HISTOGRAM_ROWS - (red[x] * scale)
  85. red_column[y].red = MaxRGB
  86. red_column[y].green = 0
  87. red_column[y].blue = 0
  88. rgb_column[y].red = MaxRGB
  89. end
  90. if yf >= HISTOGRAM_ROWS - (green[x] * scale)
  91. green_column[y].green = MaxRGB
  92. green_column[y].red = 0
  93. green_column[y].blue = 0
  94. rgb_column[y].green = MaxRGB
  95. end
  96. if yf >= HISTOGRAM_ROWS - (blue[x] * scale)
  97. blue_column[y].blue = MaxRGB
  98. blue_column[y].red = 0
  99. blue_column[y].green = 0
  100. rgb_column[y].blue = MaxRGB
  101. end
  102. if yf >= HISTOGRAM_ROWS - (int[x] * scale)
  103. int_column[y].opacity = TransparentOpacity
  104. end
  105. end
  106.  
  107. rgb_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, rgb_column)
  108. red_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, red_column)
  109. green_histogram.store_pixels(x, 0, 1, HISTOGRAM_ROWS, green_column)
  110. blue_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, blue_column)
  111. int_histogram.store_pixels( x, 0, 1, HISTOGRAM_ROWS, int_column)
  112. rgb_column.reset(bg)
  113. red_column.reset(bg)
  114. green_column.reset(bg)
  115. blue_column.reset(bg)
  116. int_column.reset(bg)
  117. end
  118.  
  119. return red_histogram, green_histogram, blue_histogram, rgb_histogram, int_histogram
  120. end
  121.  
  122. # Make the color histogram. Quantize the image to 256 colors if necessary.
  123. def color_hist(fg, bg)
  124. img = number_colors > 256 ? quantize(256) : self
  125.  
  126. begin
  127. hist = img.color_histogram
  128. rescue NotImplementedError
  129. $stderr.puts "The color_histogram method is not supported by this version "+
  130. "of ImageMagick/GraphicsMagick"
  131.  
  132. else
  133. pixels = hist.keys.sort_by {|pixel| hist[pixel] }
  134. scale = HISTOGRAM_ROWS / (hist.values.max*AIR_FACTOR)
  135.  
  136. histogram = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
  137. self.background_color = bg
  138. self.border_color = fg
  139. }
  140.  
  141. x = 0
  142. pixels.each do |pixel|
  143. column = Array.new(HISTOGRAM_ROWS).fill {Pixel.new}
  144. HISTOGRAM_ROWS.times do |y|
  145. if y >= HISTOGRAM_ROWS - (hist[pixel] * scale)
  146. column[y] = pixel;
  147. end
  148. end
  149. histogram.store_pixels(x, 0, 1, HISTOGRAM_ROWS, column)
  150. x = x.succ
  151. end
  152.  
  153. histogram['Label'] = 'Color Frequency'
  154. return histogram
  155. end
  156. end
  157.  
  158. # Use AnnotateImage to write the stats.
  159. def info_text(fg, bg)
  160. klass = class_type == DirectClass ? "DirectClass" : "PsuedoClass"
  161.  
  162. text = <<-END_TEXT
  163. Format: #{format}
  164. Geometry: #{columns}x#{rows}
  165. Class: #{klass}
  166. Depth: #{depth} bits-per-pixel component
  167. Colors: #{number_colors}
  168. END_TEXT
  169.  
  170. info = Image.new(HISTOGRAM_COLS, HISTOGRAM_ROWS) {
  171. self.background_color = bg
  172. self.border_color = fg
  173. }
  174.  
  175. gc = Draw.new
  176.  
  177. gc.annotate(info, 0, 0, 0, 0, text) {
  178. self.stroke = 'transparent'
  179. self.fill = fg
  180. self.gravity = CenterGravity
  181. }
  182. info['Label'] = 'Info'
  183.  
  184. return info
  185. end
  186.  
  187. def intensity_hist(int_histogram)
  188.  
  189. gradient = (Image.read("gradient:#ffff80-#ff9000") { self.size="#{HISTOGRAM_COLS}x#{HISTOGRAM_ROWS}" }).first
  190. int_histogram = gradient.composite(int_histogram, CenterGravity, OverCompositeOp)
  191.  
  192. int_histogram['Label'] = 'Intensity'
  193.  
  194. return int_histogram
  195. end
  196.  
  197. # Returns a value between 0 and MAX_QUANTUM. Same as the PixelIntensity macro.
  198. def pixel_intensity(pixel)
  199. (306*(pixel.red & MAX_QUANTUM) + 601*(pixel.green & MAX_QUANTUM) + 117*(pixel.blue & MAX_QUANTUM))/1024
  200. end
  201.  
  202. public
  203. # Create the histogram montage.
  204. def histogram(fg='white', bg='black')
  205.  
  206. red = Array.new(HISTOGRAM_COLS, 0)
  207. green = Array.new(HISTOGRAM_COLS, 0)
  208. blue = Array.new(HISTOGRAM_COLS, 0)
  209. alpha = Array.new(HISTOGRAM_COLS, 0)
  210. int = Array.new(HISTOGRAM_COLS, 0)
  211.  
  212. rows.times do |row|
  213. pixels = get_pixels(0, row, columns, 1)
  214. pixels.each do |pixel|
  215. red[pixel.red & MAX_QUANTUM] += 1
  216. green[pixel.green & MAX_QUANTUM] += 1
  217. blue[pixel.blue & MAX_QUANTUM] += 1
  218.  
  219. # Only count opacity channel if some pixels are not opaque.
  220. if !opaque?
  221. alpha[pixel.opacity & MAX_QUANTUM] += 1
  222. end
  223. v = pixel_intensity(pixel)
  224. int[v] += 1
  225. end
  226. end
  227.  
  228. # Scale to chart size. When computing the scale, add some "air" between
  229. # the max frequency and the top of the histogram. This makes a prettier chart.
  230. # The RGBA and intensity histograms are all drawn to the same scale.
  231.  
  232. max = [red.max, green.max, blue.max, alpha.max, int.max].max
  233. scale = HISTOGRAM_ROWS / (max*AIR_FACTOR)
  234.  
  235. charts = ImageList.new
  236.  
  237. # Add the thumbnail.
  238. thumb = copy
  239. thumb['Label'] = File.basename(filename)
  240. charts << thumb
  241.  
  242. # Compute the channel and intensity histograms.
  243. channel_hists = channel_histograms(red, green, blue, int, scale, fg, bg)
  244.  
  245. # Add the red, green, and blue histograms to the list
  246. charts << channel_hists.shift
  247. charts << channel_hists.shift
  248. charts << channel_hists.shift
  249.  
  250. # Add Alpha channel or image stats
  251. if !opaque?
  252. charts << alpha_hist(alpha, scale, fg, bg)
  253. else
  254. charts << info_text(fg, bg)
  255. end
  256.  
  257. # Add the RGB histogram
  258. charts << channel_hists.shift
  259.  
  260. # Add the intensity histogram.
  261. charts << intensity_hist(channel_hists.shift)
  262.  
  263. # Add the color frequency histogram.
  264. charts << color_hist(fg, bg)
  265.  
  266. # Make a montage.
  267. histogram = charts.montage {
  268. self.background_color = bg
  269. self.stroke = 'transparent'
  270. self.fill = fg
  271. self.border_width = 1
  272. self.tile = "4x2"
  273. self.geometry = "#{HISTOGRAM_COLS}x#{HISTOGRAM_ROWS}+10+10"
  274. }
  275.  
  276. return histogram
  277. end
  278. end
  279. end
  280.  
  281. puts <<END_INFO
  282.  
  283. This example shows how to get pixel-level access to an image.
  284. Usage: histogram.rb <image-filename>
  285.  
  286. END_INFO
  287.  
  288. # Get filename from command line.
  289. if !ARGV[0] then
  290. puts "No filename argument. Defaulting to Flower_Hat.jpg"
  291. filename = '../doc/ex/images/Flower_Hat.jpg'
  292. else
  293. filename = ARGV[0]
  294. end
  295.  
  296. # Only process first frame if multi-frame image
  297. image = Magick::Image.read(filename)
  298. if image.length > 1
  299. puts "Charting 1st image"
  300. end
  301. image = image.first
  302.  
  303. # Give the user something to look at while we're working.
  304. name = File.basename(filename).sub(/\..*?$/,'')
  305. $defout.sync = true
  306. printf "Creating #{name}_Histogram.miff"
  307.  
  308. timer = Thread.new do
  309. loop do
  310. sleep(1)
  311. printf "."
  312. end
  313. end
  314.  
  315. # Generate the histograms
  316. histogram = image.histogram(Magick::Pixel.from_color('white'), Magick::Pixel.from_color('black'))
  317.  
  318. # Write output file
  319. histogram.compression = Magick::ZipCompression
  320. histogram.write("./#{name}_Histogram.miff")
  321.  
  322. Thread.kill(timer)
  323. puts "Done!"
  324. exit
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement