pnm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e0626fcd5c88b2477eb1f76891857c83fb10d881
4
+ data.tar.gz: 29724fe5ca19969bb9bf66358272222189b17f33
5
+ SHA512:
6
+ metadata.gz: 9e1f19e832e56b253d9db10794d4b414fc0211222c8e24ac572ffa93399e505bab04648b51ad1598916959480e2769fbac6f74253557f8a2a2ec81fca06aab8f
7
+ data.tar.gz: de73725df2c7703abd73e105ea770c4efe89d5db7caa01da45a747335a3d819003a946a8aaa31aa08869e0a8a6662e578cd23227d31daab00eabc686ac86e0ef
@@ -0,0 +1,93 @@
1
+ PNM - A Ruby library for PNM image files (PBM, PGM, PPM)
2
+ ========================================================
3
+
4
+ PNM is a pure [Ruby][Ruby] library for creating, reading,
5
+ and writing of `PNM` image files (Portable Anymap):
6
+
7
+ - `PBM` (Portable Bitmap),
8
+ - `PGM` (Portable Graymap), and
9
+ - `PPM` (Portable Pixmap).
10
+
11
+ Examples
12
+ --------
13
+
14
+ Create a PGM grayscale image from a two-dimensional array of gray values:
15
+
16
+ require 'pnm'
17
+
18
+ # pixel data
19
+ pixels = [[ 0, 10, 20],
20
+ [10, 20, 30]]
21
+
22
+ # optional settings
23
+ options = {:maxgray => 30, :comment => 'Test Image'}
24
+
25
+ # create the image object
26
+ image = PNM::Image.new(:pgm, pixels, options)
27
+
28
+ # retrieve some image properties
29
+ image.info # => "PGM 3x2 Grayscale"
30
+ image.width # => 3
31
+ image.height # => 2
32
+
33
+ See PNM::Image.new for a more detailed description of pixel data formats
34
+ and available options.
35
+
36
+ Write an image to a file:
37
+
38
+ image.write('test.pgm')
39
+
40
+ # use ASCII or "plain" format (default is binary)
41
+ image.write('test.pgm', :ascii)
42
+
43
+ # write to an I/O stream
44
+ File.open('test.pgm', 'w') {|f| image.write(f) }
45
+
46
+ Read an image from a file (returns a PNM::Image object):
47
+
48
+ image = PNM.read('test.pgm')
49
+ image.comment # => "Test Image"
50
+ image.maxgray # => 30
51
+ image.pixels # => [[0, 10, 20], [10, 20, 30]]
52
+
53
+
54
+ Installation
55
+ ------------
56
+
57
+ To install PNM, you can either
58
+
59
+ - use `gem install pnm`, or
60
+
61
+ - clone or download the repository and use
62
+ `rake build` and `[sudo] gem install pnm`.
63
+
64
+ Requirements
65
+ ------------
66
+
67
+ - No additional Ruby gems or native libraries are needed.
68
+
69
+ - PNM has been tested with Ruby 1.9.3 and Ruby 2.0.0
70
+ on Linux and on Windows.
71
+
72
+ Documentation
73
+ -------------
74
+
75
+ Documentation should be available via `ri PNM`.
76
+
77
+ Reporting bugs
78
+ --------------
79
+
80
+ Report bugs on the PNM home page: <https://github.com/stomar/pnm/>
81
+
82
+ License
83
+ -------
84
+
85
+ Copyright &copy; 2013 Marcus Stollsteimer
86
+
87
+ `PNM` is free software: you can redistribute it and/or modify
88
+ it under the terms of the GNU General Public License version 3 or later (GPLv3+),
89
+ see [www.gnu.org/licenses/gpl.html](http://www.gnu.org/licenses/gpl.html).
90
+ There is NO WARRANTY, to the extent permitted by law.
91
+
92
+
93
+ [Ruby]: http://www.ruby-lang.org/
@@ -0,0 +1,34 @@
1
+ # rakefile for the PNM library.
2
+ #
3
+ # Copyright (C) 2013 Marcus Stollsteimer
4
+
5
+ require 'rake/testtask'
6
+
7
+ require_relative 'lib/pnm'
8
+
9
+
10
+ def gemspec_file
11
+ 'pnm.gemspec'
12
+ end
13
+
14
+
15
+ task :default => [:test]
16
+
17
+ Rake::TestTask.new do |t|
18
+ t.pattern = 'test/**/test_*.rb'
19
+ t.ruby_opts << '-rubygems'
20
+ t.verbose = true
21
+ t.warning = true
22
+ end
23
+
24
+
25
+ desc 'Run benchmarks'
26
+ task :benchmark do
27
+ Dir['benchmark/**/bm_*.rb'].each {|f| require_relative f }
28
+ end
29
+
30
+
31
+ desc 'Build gem'
32
+ task :build do
33
+ sh "gem build #{gemspec_file}"
34
+ end
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+ # bm_converter.rb: Benchmarks for the PNM library.
3
+ #
4
+ # Copyright (C) 2013 Marcus Stollsteimer
5
+
6
+ require 'benchmark'
7
+ require_relative '../lib/pnm'
8
+ require_relative '../lib/pnm/converter'
9
+
10
+ class ConverterBenchmark
11
+
12
+ def initialize
13
+ @repetitions = ARGV[0].to_i if ARGV[0] =~ /[0-9]+/
14
+ @repetitions ||= 10
15
+
16
+ @srcpath = File.dirname(__FILE__)
17
+
18
+ print 'Initializing test data...'
19
+ @pbm_image = PNM.read(File.expand_path("#{@srcpath}/random_image.pbm"))
20
+ @pgm_image = PNM.read(File.expand_path("#{@srcpath}/random_image.pgm"))
21
+ @ppm_image = PNM.read(File.expand_path("#{@srcpath}/random_image.ppm"))
22
+ puts " done\n"
23
+
24
+ @user = 0.0
25
+ @system = 0.0
26
+ @total = 0.0
27
+ end
28
+
29
+ def run
30
+ puts "Running benchmarks (#{@repetitions} repetitions)..."
31
+
32
+ run_benchmark(@pbm_image)
33
+ run_benchmark(@pgm_image)
34
+ run_benchmark(@ppm_image)
35
+
36
+ puts "\nTotal: " <<
37
+ @user.round(2).to_s.ljust(11) <<
38
+ @system.round(2).to_s.ljust(11) <<
39
+ @total.round(2).to_s
40
+ end
41
+
42
+ def run_benchmark(image)
43
+ type = image.type
44
+ type_string = type.upcase
45
+ width = image.width
46
+ height = image.height
47
+ array = image.pixels
48
+ ascii = PNM::Converter.array2ascii(array)
49
+ binary = PNM::Converter.array2binary(type, array)
50
+
51
+ puts
52
+
53
+ Benchmark.bm(18) do |bm|
54
+ bm.report("#{type_string} / ascii2array") {
55
+ @repetitions.times do
56
+ PNM::Converter.ascii2array(type, ascii)
57
+ end
58
+ }
59
+
60
+ bm.report("#{type_string} / array2ascii") {
61
+ @repetitions.times do
62
+ PNM::Converter.array2ascii(array)
63
+ end
64
+ }
65
+
66
+ bm.report("#{type_string} / binary2array") {
67
+ @repetitions.times do
68
+ PNM::Converter.binary2array(type, width, height, binary)
69
+ end
70
+ }
71
+
72
+ bm.report("#{type_string} / array2binary") {
73
+ @repetitions.times do
74
+ PNM::Converter.array2binary(type, array)
75
+ end
76
+ }
77
+
78
+ @user += bm.list.map {|tms| tms.utime }.reduce(:+)
79
+ @system += bm.list.map {|tms| tms.stime }.reduce(:+)
80
+ @total += bm.list.map {|tms| tms.total }.reduce(:+)
81
+ end
82
+ end
83
+ end
84
+
85
+ ConverterBenchmark.new.run
@@ -0,0 +1,155 @@
1
+ # = pnm.rb - create/read/write PNM image files (PBM, PGM, PPM)
2
+ #
3
+ # See PNM module for documentation.
4
+
5
+ require_relative 'pnm/version'
6
+ require_relative 'pnm/image'
7
+ require_relative 'pnm/parser'
8
+ require_relative 'pnm/converter'
9
+
10
+
11
+ # PNM is a pure Ruby library for creating, reading,
12
+ # and writing of +PNM+ image files (Portable Anymap):
13
+ #
14
+ # - +PBM+ (Portable Bitmap),
15
+ # - +PGM+ (Portable Graymap), and
16
+ # - +PPM+ (Portable Pixmap).
17
+ #
18
+ # == Examples
19
+ #
20
+ # Create a PGM grayscale image from a two-dimensional array of gray values:
21
+ #
22
+ # require 'pnm'
23
+ #
24
+ # # pixel data
25
+ # pixels = [[ 0, 10, 20],
26
+ # [10, 20, 30]]
27
+ #
28
+ # # optional settings
29
+ # options = {:maxgray => 30, :comment => 'Test Image'}
30
+ #
31
+ # # create the image object
32
+ # image = PNM::Image.new(:pgm, pixels, options)
33
+ #
34
+ # # retrieve some image properties
35
+ # image.info # => "PGM 3x2 Grayscale"
36
+ # image.width # => 3
37
+ # image.height # => 2
38
+ #
39
+ # See PNM::Image.new for a more detailed description of pixel data formats
40
+ # and available options.
41
+ #
42
+ # Write an image to a file:
43
+ #
44
+ # image.write('test.pgm')
45
+ #
46
+ # # use ASCII or "plain" format (default is binary)
47
+ # image.write('test.pgm', :ascii)
48
+ #
49
+ # # write to an I/O stream
50
+ # File.open('test.pgm', 'w') {|f| image.write(f) }
51
+ #
52
+ # Read an image from a file (returns a PNM::Image object):
53
+ #
54
+ # image = PNM.read('test.pgm')
55
+ # image.comment # => "Test Image"
56
+ # image.maxgray # => 30
57
+ # image.pixels # => [[0, 10, 20], [10, 20, 30]]
58
+ #
59
+ # == See also
60
+ #
61
+ # Further information on the PNM library is available on the
62
+ # project home page: <https://github.com/stomar/pnm/>.
63
+ #
64
+ # == Author
65
+ #
66
+ # Copyright (C) 2013 Marcus Stollsteimer
67
+ #
68
+ # License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
69
+ #
70
+ #--
71
+ #
72
+ # == PNM magic numbers
73
+ #
74
+ # Magic Number Type Encoding
75
+ # ------------ ---------------- -------
76
+ # P1 Portable bitmap ASCII
77
+ # P2 Portable graymap ASCII
78
+ # P3 Portable pixmap ASCII
79
+ # P4 Portable bitmap Binary
80
+ # P5 Portable graymap Binary
81
+ # P6 Portable pixmap Binary
82
+ #
83
+ #++
84
+ #
85
+ module PNM
86
+
87
+ LIBNAME = 'pnm' # :nodoc:
88
+ HOMEPAGE = 'https://github.com/stomar/pnm' # :nodoc:
89
+ TAGLINE = 'create/read/write PNM image files (PBM, PGM, PPM)' # :nodoc:
90
+
91
+ COPYRIGHT = <<-copyright.gsub(/^ +/, '') # :nodoc:
92
+ Copyright (C) 2012-2013 Marcus Stollsteimer.
93
+ License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
94
+ This is free software: you are free to change and redistribute it.
95
+ There is NO WARRANTY, to the extent permitted by law.
96
+ copyright
97
+
98
+ # Reads an image from +file+ (a filename or an IO object).
99
+ #
100
+ # Returns a PNM::Image object.
101
+ def self.read(file)
102
+ raw_data = nil
103
+ if file.kind_of?(String)
104
+ raw_data = File.binread(file)
105
+ else
106
+ file.binmode
107
+ raw_data = file.read
108
+ end
109
+
110
+ content = Parser.parse(raw_data)
111
+
112
+ case content[:magic_number]
113
+ when 'P1'
114
+ type = :pbm
115
+ encoding = :ascii
116
+ when 'P2'
117
+ type = :pgm
118
+ encoding = :ascii
119
+ when 'P3'
120
+ type = :ppm
121
+ encoding = :ascii
122
+ when 'P4'
123
+ type = :pbm
124
+ encoding = :binary
125
+ when 'P5'
126
+ type = :pgm
127
+ encoding = :binary
128
+ when 'P6'
129
+ type = :ppm
130
+ encoding = :binary
131
+ end
132
+
133
+ width = content[:width].to_i
134
+ height = content[:height].to_i
135
+ maxgray = content[:maxgray].to_i
136
+ pixels = if encoding == :ascii
137
+ Converter.ascii2array(type, content[:data])
138
+ else
139
+ Converter.binary2array(type, width, height, content[:data])
140
+ end
141
+
142
+ options = {:maxgray => maxgray}
143
+ options[:comment] = content[:comments].join("\n") if content[:comments]
144
+
145
+ Image.new(type, pixels, options)
146
+ end
147
+
148
+ def self.magic_number # :nodoc:
149
+ {
150
+ :pbm => {:ascii => 'P1', :binary => 'P4'},
151
+ :pgm => {:ascii => 'P2', :binary => 'P5'},
152
+ :ppm => {:ascii => 'P3', :binary => 'P6'}
153
+ }
154
+ end
155
+ end
@@ -0,0 +1,101 @@
1
+ module PNM
2
+
3
+ # Converter for pixel data. Only for internal usage.
4
+ class Converter
5
+
6
+ # Returns the number of bytes needed for one row of pixels
7
+ # (in binary encoding).
8
+ def self.byte_width(type, width)
9
+ case type
10
+ when :pbm
11
+ (width - 1) / 8 + 1
12
+ when :pgm
13
+ width
14
+ when :ppm
15
+ 3 * width
16
+ end
17
+ end
18
+
19
+ # Converts from ASCII format to an array of pixel values.
20
+ #
21
+ # +type+:: +:pbm+, +:pgm+, or +:ppm+.
22
+ # +data+:: A string containing the raw pixel data in ASCII format.
23
+ #
24
+ # Returns a two-dimensional array of bilevel, gray, or RGB values.
25
+ def self.ascii2array(type, data)
26
+ pixels = data.split("\n").map do |row|
27
+ row.split(/ +/).map {|value| value.to_i }
28
+ end
29
+
30
+ pixels.map! {|row| row.each_slice(3).to_a } if type == :ppm
31
+
32
+ pixels
33
+ end
34
+
35
+ # Converts from binary format to an array of pixel values.
36
+ #
37
+ # +type+:: +:pbm+, +:pgm+, or +:ppm+.
38
+ # +width+, +height+:: The image dimensions in pixels.
39
+ # +data+:: A string containing the raw pixel data in binary format.
40
+ #
41
+ # Returns a two-dimensional array of bilevel, gray, or RGB values.
42
+ def self.binary2array(type, width, height, data)
43
+ bytes_per_row = byte_width(type, width)
44
+
45
+ if data.size == bytes_per_row * height + 1 && data[-1] =~ /[ \n\t\r]/
46
+ data.slice!(-1)
47
+ end
48
+
49
+ if data.size != bytes_per_row * height
50
+ raise 'data size does not match expected size'
51
+ end
52
+
53
+ case type
54
+ when :pbm
55
+ pixels = data.scan(/.{#{bytes_per_row}}/m)
56
+ pixels.map! {|row| row.unpack('B*').first[0, width].each_char.map {|char| char.to_i } }
57
+ when :pgm
58
+ pixels = data.each_byte.each_slice(bytes_per_row).to_a
59
+ when :ppm
60
+ pixels = data.each_byte.each_slice(bytes_per_row).map {|row| row.each_slice(3).to_a }
61
+ end
62
+
63
+ pixels
64
+ end
65
+
66
+ # Converts a two-dimensional array of pixel values to an ASCII format string.
67
+ #
68
+ # +data+:: A two-dimensional array of bilevel, gray, or RGB values.
69
+ #
70
+ # Returns a string containing the pixel data in ASCII format.
71
+ def self.array2ascii(data)
72
+ case data.first.first
73
+ when Array
74
+ output = data.map {|row| row.flatten.join(' ') }.join("\n")
75
+ else
76
+ output = data.map {|row| row.join(' ') }.join("\n")
77
+ end
78
+
79
+ output << "\n"
80
+ end
81
+
82
+ # Converts a two-dimensional array of pixel values to a binary format string.
83
+ #
84
+ # +type+:: +:pbm+, +:pgm+, or +:ppm+.
85
+ # +data+:: A two-dimensional array of bilevel, gray, or RGB values.
86
+ #
87
+ # Returns a string containing the pixel data in binary format.
88
+ def self.array2binary(type, data)
89
+ height = data.size
90
+
91
+ if type == :pbm
92
+ binary_rows = data.map {|row| row.join }
93
+ data_string = binary_rows.pack('B*' * height)
94
+ else
95
+ data_string = data.flatten.pack('C*')
96
+ end
97
+
98
+ data_string
99
+ end
100
+ end
101
+ end