2017-01-29 16 views
0

В этом кодеКак получить значения HSV для среднего пикселя изображения?

im = Vips::Image.new_from_file "some.jpg" 
r = (im * [1,0,0]).avg 
g = (im * [0,1,0]).avg 
b = (im * [0,0,1]).avg 

p [r,g,b]      # => [57.1024, 53.818933333333334, 51.9258] 

p Vips::Image.sRGB2HSV [r,g,b] 

последняя линия бросает

/ruby-vips-1.0.3/lib/vips/argument.rb:154:in `set_property': invalid argument Array (expect #<Class:0x007fbd7c923600>) (ArgumentError)` 

PS: временно взял и переработан реализацию ChunkyPNG:

def to_hsv r, g, b 
    r, g, b = [r, g, b].map{ |component| component.fdiv 255 } 
    min, max = [r, g, b].minmax 
    chroma = max - min 
    [ 
    60.0 * (chroma.zero? ? 0 : case max 
     when r ; (g - b)/chroma 
     when g ; (b - r)/chroma + 2 
     when b ; (r - g)/chroma + 4 
     else 0 
    end % 6), 
    chroma/max, 
    max, 
    ] 
end 

ответ

1

Pixel усреднение должно быть действительно в линейное цветовое пространство. XYZ прост, но scRGB тоже будет работать. Как только вы получите изображение размером 1x1 пиксель, конвертируйте в HSV и запишите значение.

#!/usr/bin/ruby 

require 'vips' 

im = Vips::Image.new_from_file ARGV[0] 

# xyz colourspace is linear, ie. the value is each channel is proportional to 
# the number of photons of that frequency 
im = im.colourspace "xyz" 

# 'shrink' is a fast box filter, so each output pixel is the simple average of 
# the corresponding input pixels ... this will shrink the whole image to a 
# single pixel 
im = im.shrink im.width, im.height 

# now convert the one pixel image to hsv and read out the values 
im = im.colourspace "hsv" 
h, s, v = im.getpoint 0, 0 

puts "h = #{h}" 
puts "s = #{s}" 
puts "v = #{v}" 

Я бы не использовал HSV сам, LCh, как правило, намного лучше.

https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC

Для ЛЧ, просто изменить конец:

im = im.colourspace "lch" 
l, c, h = im.getpoint 0, 0 
+0

Прохладный. Вероятно, вы можете написать его как 'im.bandsplit.map (&: avg)' – Nakilon

+0

Я сравню результаты этого и моего сегодня или завтра. – Nakilon

+0

О, хорошая точка на карте (&: avg) '. Это вряд ли даст точно такой же результат. – user894763

0

я понял, что это, очевидно, неправильно рассчитать среднюю Hue, как среднее арифметическое, так что я решил ее путем добавления векторов длины равным насыщенности. Но я не нашел, как перебрать пикселей в VIPs, так что я использовал костыль chunky_png:

require "vips" 
require "chunky_png" 

def get_average_hsv_by_filename filename 
    im = Vips::Image.new filename 
    im.write_to_file "temp.png" 
    y, x = 0, 0 
    ChunkyPNG::Canvas.from_file("temp.png").to_rgba_stream.unpack("N*").each do |rgba| 
    h, s, v = ChunkyPNG::Color.to_hsv(rgba) 
    a = h * Math::PI/180 
    y += Math::sin(a) * s 
    x += Math::cos(a) * s 
    end 
    h = Math::atan2(y, x)/Math::PI * 180 
    _, s, v = im.colourspace("hsv").bandsplit.map(&:avg) 
    [h, s, v] 
end 

Для больших изображений, которые я использовал .resize, что, кажется, чтобы причинить только до ~ 2% погрешности при изменении размера до 10000 квадратная площадь пикселей с ядром по умолчанию.

+0

@ user894763, пожалуйста, взгляните на это. – Nakilon

+0

Он не был протестирован на изображениях с альфа-каналом. – Nakilon

+0

Вы правы, усредненный оттенок не будет работать для значений с обеих сторон границы 0/360. Если изображение является естественной сценой, вы действительно хотите усреднить фотоны (или что-то пропорциональное числу фотонов), поэтому XYZ, а затем конвертировать в HSV в конце. Я уточню свой ответ. – user894763

 Смежные вопросы

  • Нет связанных вопросов^_^