Class: ImageSvd::Channel

Inherits:
Object
  • Object
show all
Defined in:
lib/image_svd/image_matrix.rb

Overview

This class is responsible for manipulating matricies that correspond to the color channels in images, which includes performing Singular Value Decomposition on a matrix

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(matrix, num_singular_values) ⇒ Channel

Returns a new instance of Channel.



16
17
18
19
20
# File 'lib/image_svd/image_matrix.rb', line 16

def initialize(matrix, num_singular_values)
  fail 'Channel initialized without a matrix' unless matrix.is_a? Matrix
  @matrix = matrix
  @num_singular_values = num_singular_values
end

Instance Attribute Details

#mObject

Returns the value of attribute m.



13
14
15
# File 'lib/image_svd/image_matrix.rb', line 13

def m
  @m
end

#nObject

Returns the value of attribute n.



13
14
15
# File 'lib/image_svd/image_matrix.rb', line 13

def n
  @n
end

#num_singular_valuesObject (readonly)

Returns the value of attribute num_singular_values.



14
15
16
# File 'lib/image_svd/image_matrix.rb', line 14

def num_singular_values
  @num_singular_values
end

#sigma_vTsObject

Returns the value of attribute sigma_vTs.



13
14
15
# File 'lib/image_svd/image_matrix.rb', line 13

def sigma_vTs
  @sigma_vTs
end

#usObject

Returns the value of attribute us.



13
14
15
# File 'lib/image_svd/image_matrix.rb', line 13

def us
  @us
end

Class Method Details

.apply_h(hash, num_singular_values) ⇒ Object

can initialize with the result of #to_h



70
71
72
73
74
75
76
77
78
# File 'lib/image_svd/image_matrix.rb', line 70

def self.apply_h(hash, num_singular_values)
  c = new(Matrix[], num_singular_values)
  c.sigma_vTs = hash['sigma_vTs']
    .map { |arr| Vector[*arr.flatten].covector }
  c.us = hash['us'].map { |arr| Vector[*arr.flatten] }
  c.m = hash['m']
  c.n = hash['n']
  c
end

Instance Method Details

#decompose(m_A = nil) ⇒ Object

The most time consuming method Launches the decomposition and saves the two lists of vectors needed to reconstruct the image rubocop:disable MethodLength



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/image_svd/image_matrix.rb', line 26

def decompose(m_A = nil)
  m_A ||= @matrix
  m_AT = m_A.transpose
  @m, @n = m_A.to_a.length, m_A.to_a.first.length
  m_ATA = m_AT * m_A
  # linear regression from several images over 200px wide
  eta = (0 < (t = (0.0003541 * (@m * @n) - 10.541)) ? t : 0)
  puts "Searching for eigenvalues... Estimated Time: #{eta.floor} seconds"
  dcmp = Matrix::EigenvalueDecomposition.new(m_ATA)
  evs = dcmp.eigenvalues
  # eigenvectors are already normalized and in same order as eigenvalues
  sorted_eigenvectors = dcmp.eigenvectors.each_with_index
    .sort_by { |_v, i| -evs[i] }
    .map { |v, _idx| v }
  both = (0...@num_singular_values).map do |idx|
    u = sorted_eigenvectors[idx]
    sigma_vT = (m_A * u).covector
    [u, sigma_vT]
  end
  @sigma_vTs = both.map { |p| p.last }
  @us = both.map { |p| p.first }
  self
end

#reconstruct_matrix(num_singular_values = nil) ⇒ Object

rubocop:enable MethodLength



51
52
53
54
55
56
57
# File 'lib/image_svd/image_matrix.rb', line 51

def reconstruct_matrix(num_singular_values = nil)
  num_singular_values ||= @num_singular_values
  zero_matrix = Matrix[*Array.new(@n) { Array.new(@m) { 0 } }]
  (0...num_singular_values).reduce(zero_matrix) do |acc, idx|
    acc + (@us[idx] * @sigma_vTs[idx])
  end.transpose
end

#to_hObject

returns all the information necessary to serialize this channel



60
61
62
63
64
65
66
67
# File 'lib/image_svd/image_matrix.rb', line 60

def to_h
  {
    'sigma_vTs' => @sigma_vTs.map(&:to_a),
    'us' => @us.map(&:to_a),
    'm' => @m,
    'n' => @n
  }
end