image_filter_dsl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []