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::IMAGE_CREDIT, 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



188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/image_svd/image_matrix.rb', line 188

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



225
226
227
228
229
230
231
232
# File 'lib/image_svd/image_matrix.rb', line 225

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



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

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



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

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



171
172
173
174
175
# File 'lib/image_svd/image_matrix.rb', line 171

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



178
179
180
181
182
183
184
# File 'lib/image_svd/image_matrix.rb', line 178

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

#add_image_svd_credit!(path) ⇒ Object



155
156
157
158
159
160
# File 'lib/image_svd/image_matrix.rb', line 155

def add_image_svd_credit!(path)
  r = rand(99999)
  %x(echo '#{Util::IMAGE_CREDIT}' > iptcData#{r}.pro)
  %x(convert #{path} +profile 8BIM -profile 8BIMTEXT:iptcData#{r}.pro #{path})
  %x(rm iptcData#{r}.pro)
end

#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



162
163
164
165
166
167
168
# File 'lib/image_svd/image_matrix.rb', line 162

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



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

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})
    add_image_svd_credit!(out_path)
    %x(rm #{intermediate})
  end
end

#to_grayscale_image(path) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
# 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})
    add_image_svd_credit!(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