Last active
March 17, 2021 00:39
-
-
Save marcosgz/0d19ddf71b60d19ca1c954f2200c3e62 to your computer and use it in GitHub Desktop.
K-NN algorithm using RGB samples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# It's ODM (Object Document Mapper) to the RGB data | |
class RGB | |
# @overload initialize(r, g, b, value) | |
# @param r [Integer] Value for R | |
# @param g [Integer] Value for G | |
# @param b [Integer] Value for B | |
# @param value [Object] describe value param | |
# @overload initialize(r, g, b) | |
# @param r [Integer] Value for R | |
# @param g [Integer] Value for G | |
# @param b [Integer] Value for B | |
def initialize(*args) | |
@r, @g, @b, @value = args | |
end | |
# Calculate euclidian distance between two instances of RGB | |
# @param a [RBG] An instance of RGB | |
# @param b [RBG] An instance of RGB | |
# @return [Float] | |
def self.distance(a, b) | |
power = %i[r g b].map { |attribute| (a[attribute] - b[attribute]) ** 2 }.reduce(&:+) | |
Math.sqrt(power) | |
end | |
# @param variable_name [Symbol, String] The name of the instance variable | |
# @raise [KeyError] when instance variable is not defined | |
def [](variable_name) | |
var_name = :"@#{variable_name}" | |
raise KeyError unless instance_variable_defined?(var_name) | |
instance_variable_get(var_name) | |
end | |
# Calculate euclidian distance to the given RGB object | |
# @param rgb[RGB] An instance of RGB | |
# @raise [ArgumentError] When the given rgb is not an instance of RGB | |
def distance(rgb) | |
raise ArgumentError unless rgb.is_a?(RGB) | |
RGB.distance(self, rgb) | |
end | |
def inspect | |
"#<RGB #{ %i[r g b value].map { |k| self[k] }.compact.join(' ') }>" | |
end | |
end | |
# Initialize a collection of RGBs | |
samples = [ | |
[1, 10, 200, 1], | |
[2, 20, 230, 1], | |
[6, 25, 150, 1], | |
[7, 45, 100, 1], | |
[10, 50, 125, 1], | |
[3, 24, 111, 1], | |
[100, 4, 10, 2], | |
[250, 7, 50, 2], | |
[243, 5, 68, 2], | |
[210, 2, 90, 2], | |
[200, 1, 95, 2], | |
[215, 0, 68, 2], | |
[56, 200, 1, 3], | |
[79, 234, 3, 3], | |
[80, 210, 8, 3], | |
[95, 200, 10, 3], | |
[80, 210, 4, 3], | |
[49, 207, 1, 3], | |
].map do |sample| | |
RGB.new(*sample) | |
end | |
# Process RGB that does not have the "value" defined using 3, 5 and 7 as "K" | |
[ | |
RGB.new(1, 2, 100), | |
RGB.new(10, 20, 30), | |
RGB.new(8, 5, 20), | |
RGB.new(237, 45, 100), | |
RGB.new(1, 50, 101), | |
RGB.new(67, 121, 12), | |
].each do |rgb| | |
values = samples.sort_by { |sample| sample.distance(rgb) }.map { |sample| sample[:value] } | |
[3, 5, 7].each do |k| | |
value = values[0, k].inject(Hash.new(0)) { |h, v| h[v] += 1; h }.sort_by { |_, v| -v }.dig(0, 0) | |
puts format('%<rgb>p: K: %<k>s => %<value>s', rgb: rgb, k: k, value: value) | |
end | |
puts('-'* 50) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#<RGB 1 2 100>: K: 3 => 1 | |
#<RGB 1 2 100>: K: 5 => 1 | |
#<RGB 1 2 100>: K: 7 => 1 | |
-------------------------------------------------- | |
#<RGB 10 20 30>: K: 3 => 1 | |
#<RGB 10 20 30>: K: 5 => 1 | |
#<RGB 10 20 30>: K: 7 => 1 | |
-------------------------------------------------- | |
#<RGB 8 5 20>: K: 3 => 1 | |
#<RGB 8 5 20>: K: 5 => 1 | |
#<RGB 8 5 20>: K: 7 => 1 | |
-------------------------------------------------- | |
#<RGB 237 45 100>: K: 3 => 2 | |
#<RGB 237 45 100>: K: 5 => 2 | |
#<RGB 237 45 100>: K: 7 => 2 | |
-------------------------------------------------- | |
#<RGB 1 50 101>: K: 3 => 1 | |
#<RGB 1 50 101>: K: 5 => 1 | |
#<RGB 1 50 101>: K: 7 => 1 | |
-------------------------------------------------- | |
#<RGB 67 121 12>: K: 3 => 3 | |
#<RGB 67 121 12>: K: 5 => 3 | |
#<RGB 67 121 12>: K: 7 => 3 | |
-------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment