0

在这段代码中

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
4

2 回答 2

2

像素平均应该真正在线性色彩空间中。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
于 2017-01-30T09:14:02.223 回答
0

我意识到,将平均色调计算为算术平均值显然是错误的,所以我通过添加长度等于饱和度的向量来解决它。但是我没有找到如何迭代 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% 的错误。

于 2017-04-14T11:51:27.353 回答