2017-01-29 4 views
0

In diesem CodeWie bekomme ich HSV-Werte eines durchschnittlichen Pixels eines Bildes?

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] 

die letzte Zeile wirft

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

PS: temporäre nahm und die ChunkyPNG Umsetzung Refactoring:

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 

Antwort

1

Pixel Lungs wirklich in eine sein sollte, linearer Farbraum XYZ ist einfach, aber scRGB würde auch gut funktionieren. Sobald Sie ein 1x1 Pixel Bild haben, konvertieren Sie in HSV und lesen Sie den Wert aus.

#!/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}" 

Ich würde HSV selbst nicht verwenden, LCh ist im Allgemeinen viel besser.

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

Für LCH zu ändern, nur um das Ende zu:

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

Kühl. Sie können es wahrscheinlich als 'im.bandsplit.map (&: avg)' – Nakilon

+0

schreiben Ich werde Ergebnisse von mir und heute oder morgen vergleichen. – Nakilon

+0

Oh, guter Punkt auf der Karte (&: avg) '. Es ist unwahrscheinlich, dass genau dasselbe Ergebnis erzielt wird. – user894763

0

Ich erkannte, dass es offensichtlich falsch ist durchschnittlich Hue als arithmetischer Mittelwert zu berechnen, so dass ich es gelöst durch Vektoren der Länge Hinzufügen gleich der Sättigung. Aber ich habe nicht gefunden, wie über Pixel in vips laufen, damit ich eine Krücke von chunky_png verwendet:

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 

Für große Bilder, die ich .resize verwendet, die nur bis zu ~ 2% Fehler zu verursachen scheinen, wenn zu 10000 Ändern der Größe nach unten quadratischer Pixelbereich mit Standardkernel.

+0

@ user894763, bitte, werfen Sie einen Blick darauf. – Nakilon

+0

Es wurde nicht auf Bilder mit Alpha-Kanal getestet. – Nakilon

+0

Sie haben Recht, der Mittelungswert funktioniert nicht für Werte auf beiden Seiten der 0/360-Grenze. Wenn das Bild eine natürliche Szene ist, wollen Sie wirklich Photonen (oder etwas, das proportional zur Anzahl der Photonen ist), also XYZ, dann am Ende in HSV konvertieren. Ich werde meine Antwort aktualisieren. – user894763

Verwandte Themen