【问题标题】:How do I get HSV values of an average pixel of an image?如何获得图像平均像素的 HSV 值?
【发布时间】:2017-01-29 22:52:02
【问题描述】:

在这段代码中

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)`

P.S.:临时采用并重构了 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

【问题讨论】:

    标签: ruby hsv vips


    【解决方案1】:

    像素平均应该真正在线性色彩空间中。 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

    对于 LCh,只需将结尾改为:

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

    【讨论】:

    • 酷。你可能可以写成im.bandsplit.map(&amp;:avg)
    • 今天或明天我会比较这个和我的结果。
    • 哦,map(&amp;:avg) 的好点子。不太可能给出完全相同的结果。
    • 另外,我不会使用 HSV,它的定义很差而且非常非线性 wrt。到人类的视觉。如果您尝试按色调(例如)选择对象,LCh 会好得多。 vips 只有 HSV 才能与非常旧的软件兼容。
    【解决方案2】:

    我意识到,将平均色调计算为算术平均值显然是错误的,所以我通过添加长度等于饱和度的向量来解决它。但是我没有找到如何在 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,当使用默认内核将区域缩小到 10000 平方像素区域时,它似乎只会造成大约 2% 的错误。

    【讨论】:

    • @user894763,请看一下这个。
    • 虽然它没有在带有 alpha 通道的图像上进行测试。
    • 你是对的,平均色调不适用于 0/360 边界两侧的值。如果图像是自然场景,您真的想要平均光子(或与光子数量成比例的东西),所以 XYZ,然后在最后转换为 HSV。我会更新我的答案。
    猜你喜欢
    • 2015-04-12
    • 2012-09-06
    • 2018-08-16
    • 1970-01-01
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多