image_filter_dsl 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dad7597cc47d2d15fbba4c15de8222bd7fd60599709c34801d909ba1b8c4e9e3
4
+ data.tar.gz: 53dc7843ef41747dc96baac57a440da580821c6b1ff18e3ecad479846343c51d
5
+ SHA512:
6
+ metadata.gz: 18274358415905e80d5969243832d30847496bc219510d3a11d5fd88be347c7a5de70eeb8415095413c684999beb24a269f5137b3a88c58bb223b6e78fb05a20
7
+ data.tar.gz: 61fcf052a2afb9f8445fcda605c6e94a84007680d55c5bda9bff4b995f5ff0854f3ba674b072a99da10f6ba037b5991b8308d563f270e73545d0ee95610908d1
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'image_filter_dsl'
4
+
5
+ printf "Image Filter DSL > Process Image\n"
6
+ if ARGV.length == 0
7
+ printf "Usage: image_filter_dsl <kernel_file> <image_in> <image_out>\n"
8
+ else
9
+ ImageFilterDsl.cli_process_image(ARGV)
10
+ end
@@ -0,0 +1,47 @@
1
+ require_relative './image_filter_dsl/dsl/filter.rb'
2
+ require_relative './image_filter_dsl/dsl/filter_instructions.rb'
3
+ require_relative './image_filter_dsl/dsl/kernel.rb'
4
+ require_relative './image_filter_dsl/binary/struct.rb'
5
+ require_relative './image_filter_dsl/binary/serialize.rb'
6
+ require_relative './image_filter_dsl/engine/io.rb'
7
+ require_relative './image_filter_dsl/engine/image_processor.rb'
8
+
9
+ ##
10
+ # Image Filter DSL Library
11
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
12
+ # @author Wade H. <vdtdev@gmail.com>
13
+ module ImageFilterDsl
14
+
15
+ ##
16
+ # Reference to Filter module
17
+ Filter = Dsl::Filter
18
+
19
+ ##
20
+ # Shortcut to ImageProcessor constructor
21
+ # @param [Dsl::Kernel::FilterKernel|String] kernel FilterKernel object or path to binary kernel file
22
+ # @param [Integer] threads How many threads for processor to use
23
+ # @return [Engine::ImageProcessor] new instance of image processor
24
+ def self.image_processor(kernel, threads=6)
25
+ Engine::ImageProcessor.new(kernel, threads)
26
+ end
27
+
28
+ ##
29
+ # Execute process image using kernel from CLI
30
+ # @param [Array] args Array of arguments (kernel file, img in, img out)
31
+ def self.cli_process_image(args)
32
+ kernel_file = args[0]
33
+ img_in = args[1]
34
+ img_out = args[2]
35
+ ip = image_processor(kernel_file)
36
+ ip.process_image(img_in, img_out)
37
+ end
38
+
39
+ ##
40
+ # Shorthand for Engine::IO.write
41
+ # @param [Dsl::Kernel::FilterKernel] filter Filter to write
42
+ # @param [String] filename Filename to write to
43
+ def self.save_binary_kernel(filter, filename)
44
+ Engine::IO.write(filename, filter)
45
+ end
46
+
47
+ end
@@ -0,0 +1,175 @@
1
+ ##
2
+ # Image Filter DSL Library
3
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
4
+ # @author Wade H. <vdtdev@gmail.com>
5
+ module ImageFilterDsl
6
+ module Binary
7
+ ##
8
+ # Module providing serialization functionality for converting between
9
+ # FilterKernel instance and binary IfdKernel record
10
+ module Serialize
11
+
12
+ # @!group Object -> Binary
13
+
14
+ ##
15
+ # Alias for Serialize::from_kernel
16
+ def self.to_record(filter)
17
+ from_kernel(filter)
18
+ end
19
+
20
+ ##
21
+ # Build binary IfdKernel record from Kernel object
22
+ # @param [Dsl::Kernel::FilterKernel] filter Source filter kernel
23
+ # @return [Binary::Struct::IfdKernel] Binary kernel record
24
+ def self.from_kernel(filter)
25
+ variables = {
26
+ inputs: nil,
27
+ outputs: nil,
28
+ pool: nil,
29
+ symbols: nil
30
+ }
31
+
32
+ parts = {}
33
+
34
+ # Collect variables
35
+ pool = (filter.inputs + filter.outputs).uniq
36
+ filter.instructions.each do |i|
37
+ pool += i.inputs
38
+ pool += [i.out]
39
+ pool.uniq!
40
+ end
41
+ variables[:pool] = pool
42
+
43
+ # Generate variable symbols
44
+ variables[:symbols] = {}
45
+ count = 1
46
+ variables[:pool].each do |v|
47
+ variables[:symbols][v] = count
48
+ count += 1
49
+ end
50
+
51
+ # Build variables record
52
+
53
+ vars_rec = Binary::Struct::IfdVariables.new(
54
+ field_count: variables[:pool].length,
55
+ input_count: filter.inputs.length,
56
+ output_count: filter.outputs.length,
57
+ input_fields: filter.inputs.map { |i| variables[:symbols][i] },
58
+ output_fields: filter.outputs.map { |i| variables[:symbols][i] }
59
+ )
60
+
61
+ fkinds = Binary::Struct::FIELD_KINDS
62
+
63
+ defs = variables[:symbols].keys.map do |k|
64
+ f_field = Binary::Struct::IfdField.new(
65
+ symbol: variables[:symbols][k]
66
+ )
67
+
68
+ if k.is_a?(Symbol)
69
+ f_field.kind = fkinds[:var]
70
+ f_field.name_length = k.to_s.length
71
+ f_field.variable.assign(k.to_s)
72
+ elsif k.is_a?(Integer)
73
+ f_field.kind = fkinds[:literal_int]
74
+ f_field.int_value = k
75
+ else
76
+ f_field.kind = fkinds[:literal_float]
77
+ f_field.float_value = k
78
+ end
79
+
80
+ f_field
81
+ end
82
+
83
+ vars_rec.definition = defs
84
+ parts[:variables] = vars_rec
85
+
86
+ # Build instructions records
87
+ parts[:instructions] = filter.instructions.map do |i|
88
+ inp_fields = i.inputs.map { |f| variables[:symbols][f] }
89
+ f_ins = Binary::Struct::IfdInstruction.new(
90
+ operation: Binary::Struct.instruction_symbol(i.op),
91
+ input_count: i.inputs.length,
92
+ input_fields: inp_fields,
93
+ output_field: variables[:symbols][i.out]
94
+ )
95
+ f_ins
96
+ end
97
+
98
+ # Build Kernel Record
99
+ kernel = Binary::Struct::IfdKernel.new(
100
+ header: Binary::Struct::IfdHeader.new,
101
+ variables: parts[:variables],
102
+ instruction_count: filter.instructions.length,
103
+ instructions: parts[:instructions]
104
+ )
105
+
106
+ return kernel
107
+ end
108
+
109
+ # @!endgroup
110
+
111
+ # @!group Binary -> Object
112
+
113
+ ##
114
+ # Alias for Serialize::from_record
115
+ def self.to_kernel(record)
116
+ from_record(record)
117
+ end
118
+
119
+ ##
120
+ # Convert IfdKernel record to FilterKernel object
121
+ # @param [Binary::Struct::IfdKernel] record Record to convert
122
+ # @return [Dsl::Kernel::FilterKernel] FilterKernel from record
123
+ def self.from_record(record)
124
+ ins = []
125
+ outs = []
126
+
127
+ vars = record.variables
128
+ vardefs = vars.definition
129
+
130
+ vars_val = Proc.new do |vs,force_string=false|
131
+ r = nil
132
+ i = vardefs.select{|v| v.symbol == vs}[0]
133
+ if i.kind == Binary::Struct::FIELD_KINDS[:var]
134
+ r = i.variable.strip.to_sym
135
+ r = ":#{r.to_s}" if force_string
136
+ elsif i.kind == Binary::Struct::FIELD_KINDS[:literal_int]
137
+ r = i.int_value
138
+ else
139
+ r = i.float_value
140
+ end
141
+ r
142
+ end
143
+
144
+ op_sym = Proc.new do |opc|
145
+ Dsl::FilterInstructions::OP_INS.select{|k,v| v==opc}.keys[0]
146
+ end
147
+
148
+ ins = vars.input_fields.map { |f|
149
+ vardefs.select{|v| v.symbol == f}[0].variable.strip.to_sym
150
+ }
151
+
152
+ outs = vars.output_fields.map { |f|
153
+ vardefs.select{|v| v.symbol == f}[0].variable.strip.to_sym
154
+ }
155
+
156
+ instructions = record.instructions.map do |i|
157
+ [
158
+ "#{op_sym.call(i.operation)} ",
159
+ i.input_fields.map{|f| vars_val.call(f) }.to_s,
160
+ " , ", vars_val.call(i.output_field, true).to_s
161
+ ].join("")
162
+ end
163
+
164
+ f = Dsl::Filter.define(ins, outs){
165
+ self.instance_eval(instructions.join("\n"))
166
+ }
167
+
168
+ return f
169
+ end
170
+
171
+ # @!endgroup
172
+
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,98 @@
1
+ require 'bindata'
2
+ ##
3
+ # Image Filter DSL Library
4
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
5
+ # @author Wade H. <vdtdev@gmail.com>
6
+ module ImageFilterDsl
7
+ module Binary
8
+ ##
9
+ # Data structures used for storing Filter Kernels
10
+ # in binary format
11
+ module Struct
12
+
13
+ ##
14
+ # Symbols used to indicate kind of a field
15
+ FIELD_KINDS = {
16
+ var: 0x1a,
17
+ literal_int: 0x12,
18
+ literal_float: 0x1f
19
+ }
20
+
21
+ ##
22
+ # Constants used in header
23
+ HEADER_VALUES = {
24
+ header: "ifdKernel",
25
+ version: 0.01
26
+ }
27
+
28
+ ##
29
+ # Look up hex symbol for instruction in FilterInstructions module
30
+ # @param [Symbol] ins Instruction to look up
31
+ # @return [Integer] Instruction hex symbol
32
+ def self.instruction_symbol(ins)
33
+ Dsl::FilterInstructions::OP_INS[ins]
34
+ end
35
+
36
+ ##
37
+ # Image Filter DSL Field record
38
+ class IfdField < BinData::Record
39
+ uint16le :symbol
40
+ uint16le :kind
41
+ uint8le :name_length
42
+ string :variable, :length => :name_length,
43
+ :onlyif => lambda { kind == FIELD_KINDS[:var] }
44
+ int32le :int_value,
45
+ :onlyif => lambda { kind == FIELD_KINDS[:literal_int] }
46
+ float_le :float_value,
47
+ :onlyif => lambda { kind == FIELD_KINDS[:literal_float] }
48
+ end
49
+
50
+ ##
51
+ # Image Filter DSL Instruction record
52
+ class IfdInstruction < BinData::Record
53
+ uint16le :operation
54
+ uint8le :input_count
55
+ array :input_fields, :initial_length => :input_count do
56
+ uint16le :symbol
57
+ end
58
+ uint16le :output_field
59
+ end
60
+
61
+ ##
62
+ # Image Filter DSL Variables definition record
63
+ class IfdVariables < BinData::Record
64
+ uint8le :field_count
65
+ uint8le :input_count
66
+ uint8le :output_count
67
+ array :definition, :initial_length => :field_count do
68
+ ifd_field :field
69
+ end
70
+ array :input_fields, :initial_length => :input_count do
71
+ uint16le :field
72
+ end
73
+ array :output_fields, :initial_length => :output_count do
74
+ uint16le :field
75
+ end
76
+ end
77
+
78
+ ##
79
+ # Image Filter DSL Header record
80
+ class IfdHeader < BinData::Record
81
+ string :header, read_length: HEADER_VALUES[:header].length,
82
+ value: HEADER_VALUES[:header]
83
+ float_le :version, value: HEADER_VALUES[:version]
84
+ end
85
+
86
+ ##
87
+ # Main Image Filter DSL Kernel record
88
+ class IfdKernel < BinData::Record
89
+ ifd_header :header
90
+ ifd_variables :variables
91
+ uint16le :instruction_count
92
+ array :instructions, :initial_length => :instruction_count do
93
+ ifd_instruction :instruction
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,47 @@
1
+ ##
2
+ # Image Filter DSL Library
3
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
4
+ # @author Wade H. <vdtdev@gmail.com>
5
+ module ImageFilterDsl
6
+ module Dsl
7
+ ##
8
+ # Module used for declaring Filter w/ DSL
9
+ module Filter
10
+
11
+ ##
12
+ # Define method
13
+ # @param [Array] ins Input symbols
14
+ # @param [Array] outs Output symbols
15
+ # @param [Proc] &block Filter instructions body
16
+ # @return [FilterKernel] new Filter Kernel
17
+ def self.define(ins,outs,&block)
18
+ kernel = Kernel::FilterKernel.new(ins,outs)
19
+ ip = processor(kernel)
20
+ ip.instance_eval &block
21
+ return kernel
22
+ end
23
+
24
+ private
25
+
26
+ ##
27
+ # Process filter, creating new Instruction Processing module
28
+ # @param [FilterKernel] kernel Target FilterKernel
29
+ # @return [Module] module wrapping instructions to target kernel
30
+ def self.processor(kernel)
31
+ instruction_proc = Module.new
32
+
33
+ FilterInstructions::OPS.each do |op|
34
+ p = Proc.new do |ins,outs|
35
+ kernel.store_instruction(
36
+ Kernel::KernelInstruction.new(op,ins,outs)
37
+ )
38
+ end
39
+ instruction_proc.class.send(:define_method, op, p)
40
+ end
41
+
42
+ instruction_proc
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,225 @@
1
+ ##
2
+ # Image Filter DSL Library
3
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
4
+ # @author Wade H. <vdtdev@gmail.com>
5
+ module ImageFilterDsl
6
+ module Dsl
7
+ ##
8
+ # Module defining filter kernel instruction logic
9
+ module FilterInstructions
10
+
11
+ ##
12
+ # Hash of binary instruction symbols
13
+ OP_INS = {
14
+ # math = 0x0X-0xAF
15
+ add: 0x01,
16
+ mult: 0x02,
17
+ div: 0x03,
18
+ mod: 0x04,
19
+ abs: 0x05,
20
+ # collection op = 0xB0-0xCF
21
+ min: 0xb0,
22
+ max: 0xb1,
23
+ avg: 0xb2,
24
+ # logic/mem/conv op = 0xD0-??
25
+ copy: 0xd0,
26
+ above: 0xd1,
27
+ below: 0xd2,
28
+ floor: 0xd3,
29
+ ceil: 0xd4,
30
+ float: 0xd5,
31
+ round: 0xd6,
32
+ switch: 0xd7,
33
+ eq: 0xd8,
34
+ bnot: 0xd9
35
+ }
36
+
37
+ ##
38
+ # Array of all valid filter instructions
39
+ OPS = OP_INS.keys
40
+
41
+ ##
42
+ # Add instruction
43
+ # @param [Array] input values
44
+ # @return [Integer|Float] output value
45
+ def self.add(i)
46
+ i.sum
47
+ end
48
+
49
+ ##
50
+ # Multiply instruction
51
+ # @param [Array] input values
52
+ # @return [Integer|Float] output value
53
+ def self.mult(i)
54
+ v=1;
55
+ i.each{|n|v=v*n}
56
+ v
57
+ end
58
+
59
+ ##
60
+ # Multiply instruction
61
+ # @param [Array] input values
62
+ # @return [Integer|Float] output value
63
+ def self.div(i)
64
+ i[0]/i[1]
65
+ end
66
+ ##
67
+ # Calculate modulo
68
+ # @param [Array] input values
69
+ # @return [Integer|] output value
70
+ def self.mod(i)
71
+ i[0] % i[1]
72
+ end
73
+
74
+ ##
75
+ # Absolute value
76
+ # @param [Array] input value
77
+ # @return [Integer|Float] output value
78
+ def self.abs(i)
79
+ i[0].abs
80
+ end
81
+
82
+ ##
83
+ # Minimum instruction
84
+ # @param [Array] input values
85
+ # @return [Integer|Float] output value
86
+ def self.min(i)
87
+ i.min
88
+ end
89
+
90
+ ##
91
+ # Maximum instruction
92
+ # @param [Array] input values
93
+ # @return [Integer|Float] output value
94
+ def self.max(i)
95
+ i.max
96
+ end
97
+
98
+ ##
99
+ # Average instruction
100
+ # @param [Array] input values
101
+ # @return [Integer|Float] output value
102
+ def self.avg(i)
103
+ i.sum / (1.0 * i.length)
104
+ end
105
+
106
+ ##
107
+ # Copy instruction
108
+ # @param [Array] input values (src)
109
+ # @return [Integer|Float] output value
110
+ def self.copy(i)
111
+ i[0]
112
+ end
113
+
114
+ ##
115
+ # Above instruction
116
+ # @param [Array] input values (a,b,trueVal,falseVal)
117
+ # @return [Integer|Float] output value
118
+ def self.above(i)
119
+ if(i[0]>i[1])
120
+ if i.length < 3
121
+ 1
122
+ else
123
+ i[2]
124
+ end
125
+ else
126
+ if i.length < 4
127
+ 0
128
+ else
129
+ i[3]
130
+ end
131
+ end
132
+ end
133
+
134
+ ##
135
+ # Below instruction
136
+ # @param [Array] input values (a,b,trueVal,falseVal)
137
+ # @return [Integer|Float] output value
138
+ def self.below(i)
139
+ if(i[0]<i[1])
140
+ if i.length < 3
141
+ 1
142
+ else
143
+ i[2]
144
+ end
145
+ else
146
+ if i.length < 4
147
+ 0
148
+ else
149
+ i[3]
150
+ end
151
+ end
152
+ end
153
+
154
+ ##
155
+ # Floor instruction
156
+ # @param [Array] input value (v)
157
+ # @return [Integer|Float] output value
158
+ def self.floor(i)
159
+ i[0].floor
160
+ end
161
+
162
+ ##
163
+ # Ceil instruction
164
+ # @param [Array] input value (v)
165
+ # @return [Integer|Float] output value
166
+ def self.ceil(i)
167
+ i[0].ceil
168
+ end
169
+
170
+ ##
171
+ # Float cast instruction
172
+ # @param [Array] input value (v)
173
+ # @return [Integer|Float] output value
174
+ def self.float(i)
175
+ i[0].to_f
176
+ end
177
+
178
+ ##
179
+ # Round instruction
180
+ # @param [Array] input value (val, decimal_places)
181
+ # @return [Integer|Float] output value
182
+ def self.round(i)
183
+ i[0].round(i[1])
184
+ end
185
+
186
+ ##
187
+ # 'Switch' instruction (basically if)
188
+ #
189
+ # switch [condition (0/1/0.0/1.0), trueval, falseval]
190
+ # @param [Array] input value (condition, true val, false val)
191
+ # @return [Integer|Float] output value
192
+ def self.switch(i)
193
+ if i[0].to_i == 1
194
+ i[1]
195
+ else
196
+ i[2]
197
+ end
198
+ end
199
+
200
+ ##
201
+ # Equal condition instruction
202
+ # @param [Array] input value (a, b)
203
+ # @return [Integer|Float] output value 1 true 0 falsew
204
+ def self.eq(i)
205
+ if i[0] == i[1]
206
+ 1
207
+ else
208
+ 0
209
+ end
210
+ end
211
+
212
+ ##
213
+ # Logic invert instruction
214
+ # @param [Array] input value (0 or 1)
215
+ # @return [Integer|Float] output value (1 if in 0, 0 if in 1)
216
+ def self.bnot(i)
217
+ if i[0].to_i == 1
218
+ 0
219
+ else
220
+ 1
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,85 @@
1
+ ##
2
+ # Image Filter DSL Library
3
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
4
+ # @author Wade H. <vdtdev@gmail.com>
5
+ module ImageFilterDsl
6
+ module Dsl
7
+ module Kernel
8
+ ##
9
+ # IFD Filter Kernel class
10
+ # @author vdtdev <vdtdev@gmail.com>
11
+ class FilterKernel
12
+ attr_accessor :instructions
13
+ attr_accessor :inputs
14
+ attr_accessor :outputs
15
+
16
+ ##
17
+ # Kernel constructor
18
+ # @param [Array] inputs Input symbols
19
+ # @param [Array] outputs Output symbols
20
+ def initialize(inputs=[],outputs=[])
21
+ @inputs = inputs
22
+ @outputs = outputs
23
+ @instructions = []
24
+ end
25
+
26
+ ##
27
+ # Store instruction in kernel
28
+ # @param [Kernel::KernelInstruction] instruction Instruction to add
29
+ # @return [Integer] total number of instructions
30
+ def store_instruction(instruction)
31
+ @instructions.append(instruction)
32
+ @instructions.length
33
+ end
34
+
35
+ ##
36
+ # Process filter kernel and produce output hash
37
+ # @param [Hash] initial_values Hash of values for inputs
38
+ # @return [Hash] hash of result values for output symbols
39
+ def process(initial_values)
40
+ outs = Hash[* @outputs.map{|o| [o,nil] }.flatten]
41
+ @instructions.each do |i|
42
+ v = i.calculate(initial_values)
43
+ if @outputs.include?(i.out)
44
+ outs[i.out] = v
45
+ else
46
+ initial_values[i.out] = v
47
+ end
48
+ end
49
+ outs
50
+ end
51
+ end
52
+
53
+ ##
54
+ # Class representing a FilterKernel instruction
55
+ class KernelInstruction
56
+ attr_accessor :op
57
+ attr_accessor :inputs
58
+ attr_accessor :out
59
+
60
+ ##
61
+ # Construct new Filter Kernel Instruction
62
+ # @param [Symbol] op Instruction operation
63
+ # @param [Array] ins Input symbols
64
+ # @param [Symbol] out Output symbol
65
+ def initialize(op,ins,out)
66
+ @op = op
67
+ @inputs = ins
68
+ unless @inputs.kind_of?(Array)
69
+ @inputs = [@inputs]
70
+ end
71
+ @out = out
72
+ end
73
+
74
+ ##
75
+ # Calculate instruction output
76
+ # @param [Hash] variables Hash of variable values
77
+ # @return [Integer|Float] output value
78
+ def calculate(variables)
79
+ vals = @inputs.map{|v| (v.kind_of?(Symbol))? variables[v] : v }
80
+ FilterInstructions.method(@op).call(vals)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,199 @@
1
+ require 'chunky_png'
2
+
3
+ ##
4
+ # Image Filter DSL Library
5
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
6
+ # @author Wade H. <vdtdev@gmail.com>
7
+ module ImageFilterDsl
8
+ module Engine
9
+ ##
10
+ # Image Processor class applies Filter Kernels to images
11
+ class ImageProcessor
12
+
13
+ attr_accessor :kernel, :config
14
+
15
+ ##
16
+ # Constructor
17
+ # @param [Dsl::Kernel::FilterKernel|String] filter_kernel FilterKernel object or path to binary kernel file
18
+ # @param [Integer] thread_count How many threads for processor to use
19
+ def initialize(filter_kernel, thread_count=6)
20
+ @config = {
21
+ filter_kernel_filename: nil,
22
+ threads: thread_count
23
+ }
24
+
25
+ if filter_kernel.is_a?(String)
26
+ @config[:filter_kernel_filename] = filter_kernel
27
+
28
+ @kernel = Engine::IO.read(
29
+ filter_kernel,
30
+ Engine::IO::DataFormat::KERNEL
31
+ )
32
+ else
33
+ @kernel = filter_kernel
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Process image
39
+ # @param [String] img_src_filename Source image filename
40
+ # @param [String] img_out_filename Output image filename
41
+ def process_image(img_src_filename, img_out_filename)
42
+ img = load_image(img_src_filename)
43
+
44
+ size = [img[:image].width, img[:image].height]
45
+ p_count = size[0] * size[1]
46
+
47
+ p_part = p_count / @config[:threads]
48
+ thread_loads = [].fill(p_part, (0..(@config[:threads])-1))
49
+
50
+ if (p_part * @config[:threads]) < p_count
51
+ thread_loads[-1] += (p_count - (p_part * @config[:threads]))
52
+ end
53
+
54
+ ranges = thread_loads.map.with_index do |l,i|
55
+ start = thread_loads[0..i][0..-2].sum
56
+ fin = start + (l - 1)
57
+ (start..fin)
58
+ end
59
+
60
+ ranges = ranges.map{|r| r.map{|i| i }}
61
+
62
+ threads = []
63
+
64
+ (0..@config[:threads]-1).each do |i|
65
+ threads << Thread.new {
66
+ run_process(img, ranges[i])
67
+ }
68
+ end
69
+
70
+ threads.each { |th| th.join }
71
+
72
+ save_image(img, img_out_filename)
73
+ end
74
+
75
+ private
76
+
77
+ ##
78
+ # Convert point index to x,y coord
79
+ # @param [Integer] p Point index
80
+ # @param [Integer] width Grid width
81
+ # @return [Hash] Coordinate {:x, :y}
82
+ def point_coord(p,width)
83
+ {
84
+ x: (p % width),
85
+ y: (p / width)
86
+ }
87
+ end
88
+
89
+ ##
90
+ # Execute processing for point range
91
+ # Called inside Thread.new
92
+ # @param [Hash] data Image data
93
+ # @param [Array] range Array of range points
94
+ def run_process(data, range)
95
+
96
+ range.each do |p|
97
+ c = point_coord(p, data[:image].width)
98
+ process_pixel(data, c[:x], c[:y])
99
+ end
100
+
101
+ end
102
+
103
+ ##
104
+ # Process pixel using filter
105
+ # @param [Hash] data Image data hash
106
+ # @param [Integer] x Source (possibly dest) X coordinate
107
+ # @param [Integer] y Source (possibly dest) Y coordinate
108
+ # @return [Hash] Copy of filter outputs
109
+ def process_pixel(data, x, y)
110
+ clr = ChunkyPNG::Color
111
+ pixel = data[:image].get_pixel(x,y)
112
+
113
+ # r = (clr.r(pixel)/255.0).round(2)
114
+ # g = (clr.g(pixel)/255.0).round(2)
115
+ # b = (clr.b(pixel)/255.0).round(2)
116
+ # a = (clr.a(pixel)/255.0).round(2)
117
+
118
+ r = clr.r(pixel)
119
+ g = clr.g(pixel)
120
+ b = clr.b(pixel)
121
+ a = clr.a(pixel)
122
+
123
+
124
+ inputs = Hash[*@kernel.inputs.map{|k|[k,0]}.flatten]
125
+ inputs[:x] = x if inputs.keys.include?(:x)
126
+ inputs[:y] = y if inputs.keys.include?(:y)
127
+ inputs[:r] = r if inputs.keys.include?(:r)
128
+ inputs[:g] = g if inputs.keys.include?(:g)
129
+ inputs[:b] = b if inputs.keys.include?(:b)
130
+ inputs[:a] = a if inputs.keys.include?(:a)
131
+ inputs[:width] = data[:image].width if inputs.keys.include?(:width)
132
+ inputs[:hght] = data[:image].height if inputs.keys.include?(:hght)
133
+
134
+
135
+ o = @kernel.process(inputs)
136
+
137
+ outputs = {
138
+ x: x, y: y, r: r, g: g, b: b, a: a
139
+ }
140
+
141
+ o.keys.each do |k|
142
+ if o[k] == Float::INFINITY
143
+ p "Infinity: #{k}"
144
+ end
145
+ outputs[k] = o[k].to_i
146
+ end
147
+
148
+ # o_color = clr.rgba(
149
+ # (255*outputs[:r]).to_i,
150
+ # (255*outputs[:g]).to_i,
151
+ # (255*outputs[:b]).to_i,
152
+ # (255*outputs[:a]).to_i
153
+ # )
154
+
155
+ o_color = clr.rgba(
156
+ outputs[:r].to_i,
157
+ outputs[:g].to_i,
158
+ outputs[:b].to_i,
159
+ outputs[:a].to_i
160
+ )
161
+
162
+ data[:image].set_pixel(
163
+ outputs[:x], outputs[:y],
164
+ o_color
165
+ )
166
+ end
167
+
168
+ ##
169
+ # Load PNG image using ChunkyPNG
170
+ # @param [String] filename Filename of image to load
171
+ # @return [Hash] Hash with :stream as ChunkyPNG::Datastream and
172
+ # :image as ChunkyPNG::Image and :filename with original filename
173
+ def load_image(filename)
174
+ stream = ChunkyPNG::Datastream.from_file(filename)
175
+ image = ChunkyPNG::Image.from_datastream(stream)
176
+
177
+ return {
178
+ filename: filename,
179
+ stream: stream,
180
+ image: image
181
+ }
182
+ end
183
+
184
+ ##
185
+ # Save image data to disk as PNG
186
+ # @param [Hash] data Image data as provided by load_image
187
+ # @param [String] filename Optional filename to save to; if none given,
188
+ # uses the original filename from data hash
189
+ # @return [File] file object written to
190
+ def save_image(data, filename = nil)
191
+ # If no filename given, use original
192
+ filename = data[:filename] if filename.nil?
193
+ stream = data[:image].to_datastream()
194
+ stream.save(filename)
195
+ end
196
+
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,54 @@
1
+ ##
2
+ # Image Filter DSL Library
3
+ # (c) 2018 VDTDEV/Wade H. ~ MIT License
4
+ # @author Wade H. <vdtdev@gmail.com>
5
+ module ImageFilterDsl
6
+ module Engine
7
+ ##
8
+ # I/O methods for reading/writing Filter Kernel data to files
9
+ module IO
10
+
11
+ ##
12
+ # Constants for format option used in read
13
+ module DataFormat
14
+ KERNEL = :kernel
15
+ RECORD = :record
16
+ end
17
+
18
+ ##
19
+ # Read Filter Kernel from file
20
+ # @param [String] filename Filename to read from
21
+ # @param [Symbol] format Format to return (constants from
22
+ # DataFormat module; KERNEL or RECORD)
23
+ # @return [Dsl::Kernel::FilterKernel|Binary::Struct::IfdKernel] loaded data,
24
+ # in specified format
25
+ def self.read(filename, format=DataFormat::KERNEL)
26
+ record = Binary::Struct::IfdKernel.new
27
+ File.open(filename, 'rb'){|f| record.read(f) }
28
+
29
+ if format == DataFormat::RECORD
30
+ return record
31
+ else
32
+ return Binary::Serialize.to_kernel(record)
33
+ end
34
+ end
35
+
36
+ ##
37
+ # Write filter kernel to file
38
+ # @param [String] filename File name to write to
39
+ # @param [Binary::Struct::IfdKernel|Dsl::Kernel::FilterKernel] filter Filter kernel or record to write
40
+ # @return [Binary::Struct::IfdKernel] IfdKernel record
41
+ def self.write(filename, filter)
42
+ rec = nil
43
+ if filter.is_a?(Dsl::Kernel::FilterKernel)
44
+ rec = Binary::Serialize.to_record(filter)
45
+ elsif filter.is_a?(Binary::Struct::IfdKernel)
46
+ rec = filter
47
+ end
48
+
49
+ File.open(filename, 'wb') { |f| rec.write(f) }
50
+ end
51
+
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_filter_dsl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Wade H.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Image Filter DSL with processing
14
+ email: vdtdev.prod@gmail.com
15
+ executables:
16
+ - image_filter_dsl
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/image_filter_dsl
21
+ - lib/image_filter_dsl.rb
22
+ - lib/image_filter_dsl/binary/serialize.rb
23
+ - lib/image_filter_dsl/binary/struct.rb
24
+ - lib/image_filter_dsl/dsl/filter.rb
25
+ - lib/image_filter_dsl/dsl/filter_instructions.rb
26
+ - lib/image_filter_dsl/dsl/kernel.rb
27
+ - lib/image_filter_dsl/engine/image_processor.rb
28
+ - lib/image_filter_dsl/engine/io.rb
29
+ homepage: https://bitbucket.org/WadeH/image_filter_dsl/src
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ source_code_uri: https://bitbucket.org/WadeH/image_filter_dsl/src
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 2.7.8
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Image Filter DSL
54
+ test_files: []