Class: ImageSvd::ImageMatrix

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

Overview

This class is responsible for: Reading an image or archive to a matrix Saving a matrix to an image

Constant Summary

Constants included from Util

Util::VALID_INPUT_EXT_REGEX

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

#extension_swap

Constructor Details

#initialize(singular_values, grayscale) ⇒ ImageMatrix

Returns a new instance of ImageMatrix.



90
91
92
93
94
95
96
# File 'lib/image_svd/image_matrix.rb', line 90

def initialize(singular_values, grayscale)
  fail 'not enough singular values' if singular_values.length.zero?
  @singular_values = singular_values
  @num_singular_values = singular_values.max
  @grayscale = grayscale
  @channels = []
end

Instance Attribute Details

#channelsObject

Returns the value of attribute channels.



88
89
90
# File 'lib/image_svd/image_matrix.rb', line 88

def channels
  @channels
end

#grayscaleObject (readonly)

Returns the value of attribute grayscale.



87
88
89
# File 'lib/image_svd/image_matrix.rb', line 87

def grayscale
  @grayscale
end

#singular_valuesObject (readonly)

Returns the value of attribute singular_values.



87
88
89
# File 'lib/image_svd/image_matrix.rb', line 87

def singular_values
  @singular_values
end

Class Method Details

.matrix_to_valid_pixels(matrix) ⇒ Object

conforms a matrix to pnm requirements for pixels: positive integers rubocop:disable MethodLength



178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/image_svd/image_matrix.rb', line 178

def self.matrix_to_valid_pixels(matrix)
  matrix.to_a.map do |row|
    row.map do |number|
      rounded = number.round
      if rounded > 255
        255
      elsif rounded < 0
        0
      else
        rounded
      end
    end
  end
end

.new_from_svd_savefile(opts) ⇒ Object

TODO:

error handling code here

TODO:

serialization is kind of silly as is



215
216
217
218
219
220
221
222
# File 'lib/image_svd/image_matrix.rb', line 215

def self.new_from_svd_savefile(opts)
  h = JSON.parse(File.open(opts[:input_file], &:readline))
  if h.length == 1 # grayscale
    new_saved_grayscale_svd(opts, h.first)
  else
    new_saved_color_svd(opts, h)
  end
end

.new_saved_color_svd(opts, hs) ⇒ Object



202
203
204
205
206
207
208
209
210
211
# File 'lib/image_svd/image_matrix.rb', line 202

def self.new_saved_color_svd(opts, hs)
  svals = [opts[:singular_values], hs.first['sigma_vTs'].size]
  valid_svals = ImageSvd::Options.num_sing_val_out_from_archive(*svals)
  instance = new(valid_svals, false)
  3.times do |i|
    chan = Channel.apply_h(hs[i], valid_svals)
    instance.channels << chan
  end
  instance
end

.new_saved_grayscale_svd(opts, h) ⇒ Object

rubocop:enable MethodLength



194
195
196
197
198
199
200
# File 'lib/image_svd/image_matrix.rb', line 194

def self.new_saved_grayscale_svd(opts, h)
  svals = [opts[:singular_values], h['sigma_vTs'].size]
  valid_svals = ImageSvd::Options.num_sing_val_out_from_archive(*svals)
  instance = new(valid_svals, true)
  instance.channels << Channel.apply_h(h, valid_svals)
  instance
end

.ppm_to_rgb(arr) ⇒ Object

breaks a ppm image into 3 separate channels



161
162
163
164
165
# File 'lib/image_svd/image_matrix.rb', line 161

def self.ppm_to_rgb(arr)
  (0..2).to_a.map do |i|
    arr.map { |row| row.map { |pix| pix[i] } }
  end
end

.rgb_to_ppm(r, g, b) ⇒ Object

combines 3 separate channels into the ppm scheme



168
169
170
171
172
173
174
# File 'lib/image_svd/image_matrix.rb', line 168

def self.rgb_to_ppm(r, g, b)
  r.each_with_index.map do |row, row_i|
    row.each_with_index.map do |_, pix_i|
      [r[row_i][pix_i], g[row_i][pix_i], b[row_i][pix_i]]
    end
  end
end

Instance Method Details

#get_image_channels(image_path) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/image_svd/image_matrix.rb', line 98

def get_image_channels(image_path)
  extension = @grayscale ? 'pgm' : 'ppm'
  intermediate = extension_swap(image_path.path, extension)
  %x(convert #{image_path.path} #{intermediate})
  if @grayscale
    channels = [PNM.read(intermediate).pixels]
  else
    channels = ImageMatrix.ppm_to_rgb(PNM.read(intermediate).pixels)
  end
  %x(rm #{intermediate})
  channels.map { |c| Matrix[*c] }
end

#read_image(image_path) ⇒ Object



111
112
113
114
115
# File 'lib/image_svd/image_matrix.rb', line 111

def read_image(image_path)
  channels = get_image_channels(image_path)
  @channels = channels.map { |m| Channel.new(m, @num_singular_values) }
  @channels.each(&:decompose)
end

#save_svd(path) ⇒ Object



152
153
154
155
156
157
158
# File 'lib/image_svd/image_matrix.rb', line 152

def save_svd(path)
  out_path = extension_swap(path, 'svdim')
  string = @channels.map(&:to_h).to_json
  File.open(out_path, 'w') do |f|
    f.puts string
  end
end

#to_color_image(path) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/image_svd/image_matrix.rb', line 138

def to_color_image(path)
  puts 'writing images...' if @singular_values.length > 1
  @singular_values.each do |sv|
    out_path = extension_swap(path, 'jpg', "_#{sv}_svs")
    intermediate = extension_swap(path, 'ppm', '_tmp_outfile')
    ms = @channels.map { |c| c.reconstruct_matrix(sv) }
    cleansed_mtrxs = ms.map { |m| ImageMatrix.matrix_to_valid_pixels(m) }
    ppm_matrix = ImageMatrix.rgb_to_ppm(*cleansed_mtrxs)
    PNM::Image.new(ppm_matrix).write(intermediate)
    %x(convert #{intermediate} #{out_path})
    %x(rm #{intermediate})
  end
end

#to_grayscale_image(path) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/image_svd/image_matrix.rb', line 125

def to_grayscale_image(path)
  puts 'writing images...' if @singular_values.length > 1
  @singular_values.each do |sv|
    out_path = extension_swap(path, 'jpg', "_#{sv}_svs")
    intermediate = extension_swap(path, 'pgm', '_tmp_outfile')
    reconstructed_mtrx = @channels.first.reconstruct_matrix(sv)
    cleansed_mtrx = ImageMatrix.matrix_to_valid_pixels(reconstructed_mtrx)
    PNM::Image.new(cleansed_mtrx).write(intermediate)
    %x(convert #{intermediate} #{out_path})
    %x(rm #{intermediate})
  end
end

#to_image(path) ⇒ Object



117
118
119
120
121
122
123
# File 'lib/image_svd/image_matrix.rb', line 117

def to_image(path)
  if @grayscale
    to_grayscale_image(path)
  else
    to_color_image(path)
  end
end