pxlsrt 1.7.1 → 1.8.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.
- checksums.yaml +4 -4
- data/bin/pxlsrt +27 -1
- data/lib/pxlsrt/seed.rb +209 -0
- data/lib/pxlsrt/spiral.rb +84 -0
- data/lib/pxlsrt/version.rb +1 -1
- data/lib/pxlsrt.rb +3 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa77df27b6a2bcd94f0b1e23855baa5219e25f16
|
4
|
+
data.tar.gz: a3b71ee3d6f67fb348628bd917491db1193474bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6dd4f1a8187e9ccf3bab9e7735a190f53491404caa40bc3ae776b53077b4193cb820a62ce6973151aa613bf0beed0b3d76193c69bdd53b66644445155197a72f
|
7
|
+
data.tar.gz: 43d0246a46cebfce1a22e4e817a65c8ead7fb5efd40f8daa4a0a5850ba054030b3f485701cc1191ec42bb846baf8a849e85eb8af99ef190534383eb98fdfa0ac
|
data/bin/pxlsrt
CHANGED
@@ -59,7 +59,7 @@ class CLI < Thor
|
|
59
59
|
|
60
60
|
option :method, :type => :string, :default => "brightness", :aliases => "-m", :enum => ["brightness", "white", "black"]
|
61
61
|
option :value, :type => :numeric, :aliases => "-v"
|
62
|
-
desc "kim INPUT OUTPUT [
|
62
|
+
desc "kim INPUT OUTPUT [options]", "Uses Kim Asendorf's algorithm"
|
63
63
|
def kim(input, output)
|
64
64
|
k= {:trusted => true}
|
65
65
|
for o in options.keys
|
@@ -67,6 +67,32 @@ class CLI < Thor
|
|
67
67
|
end
|
68
68
|
Pxlsrt::Kim.suite(input, output, k)
|
69
69
|
end
|
70
|
+
|
71
|
+
option :reverse, :default => false, :aliases => "-r"
|
72
|
+
option :smooth, :type => :boolean, :default => false, :aliases => "-s"
|
73
|
+
option :method, :type => :string, :default => "sum-rgb", :banner => "[#{Pxlsrt::Colors::METHODS.join(" | ")}]", :aliases => "-m", :enum => Pxlsrt::Colors::METHODS
|
74
|
+
option :middle, :default => false, :aliases => "-M"
|
75
|
+
option :random, :default => false, :aliases => "-R"
|
76
|
+
option :distance, :default => 100, :aliases => "-d"
|
77
|
+
option :threshold, :default => 0.1, :aliases => "-t", :type => :numeric
|
78
|
+
desc "seed INPUT OUTPUT [options]", "Seed pixel sorting"
|
79
|
+
def seed(input, output)
|
80
|
+
k={:trusted=>true}
|
81
|
+
for o in options.keys
|
82
|
+
k[o.to_sym]=options[o]
|
83
|
+
end
|
84
|
+
if Pxlsrt::Helpers.isNumeric?(k[:random])
|
85
|
+
k[:random] = k[:random].to_i
|
86
|
+
else
|
87
|
+
k[:random] = false
|
88
|
+
end
|
89
|
+
if Pxlsrt::Helpers.isNumeric?(k[:distance])
|
90
|
+
k[:distance] = k[:distance].to_i
|
91
|
+
else
|
92
|
+
k[:distance] = false
|
93
|
+
end
|
94
|
+
Pxlsrt::Seed.suite(input, output, k)
|
95
|
+
end
|
70
96
|
end
|
71
97
|
|
72
98
|
CLI.start(ARGV)
|
data/lib/pxlsrt/seed.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
|
3
|
+
module Pxlsrt
|
4
|
+
##
|
5
|
+
# Plant seeds, have them spiral out and sort.
|
6
|
+
class Seed
|
7
|
+
##
|
8
|
+
# Uses Pxlsrt::Seed.seed to input and output from one method.
|
9
|
+
def self.suite(inputFileName, outputFileName, o={})
|
10
|
+
kml = Pxlsrt::Seed.seed(inputFileName, o)
|
11
|
+
if Pxlsrt::Helpers.contented(kml)
|
12
|
+
kml.save(outputFileName)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
##
|
16
|
+
# The main attraction of the Seed class. Returns a ChunkyPNG::Image that is sorted according to the options provided. Will raise any error that occurs.
|
17
|
+
def self.seed(input, o = {})
|
18
|
+
startTime = Time.now
|
19
|
+
defOptions = {
|
20
|
+
:reverse => false,
|
21
|
+
:smooth => false,
|
22
|
+
:method => "sum-rgb",
|
23
|
+
:verbose => false,
|
24
|
+
:trusted => false,
|
25
|
+
:middle => false,
|
26
|
+
:random => false,
|
27
|
+
:distance => 100,
|
28
|
+
:threshold => 0.1
|
29
|
+
}
|
30
|
+
defRules = {
|
31
|
+
:reverse => :anything,
|
32
|
+
:smooth => [false, true],
|
33
|
+
:method => Pxlsrt::Colors::METHODS,
|
34
|
+
:verbose => [false, true],
|
35
|
+
:trusted => [false, true],
|
36
|
+
:middle => :anything,
|
37
|
+
:random => [false, {:class => [Fixnum]}],
|
38
|
+
:distance => [false, {:class => [Fixnum]}],
|
39
|
+
:threshold => [{:class => [Float, Fixnum]}]
|
40
|
+
}
|
41
|
+
options = defOptions.merge(o)
|
42
|
+
if o.length == 0 or options[:trusted] == true or (options[:trusted] == false and o.length != 0 and Pxlsrt::Helpers.checkOptions(options, defRules) != false)
|
43
|
+
if input.class == String
|
44
|
+
Pxlsrt::Helpers.verbose("Getting image from file...") if options[:verbose]
|
45
|
+
if File.file?(input)
|
46
|
+
if Pxlsrt::Colors.isPNG?(input)
|
47
|
+
input = ChunkyPNG::Image.from_file(input)
|
48
|
+
else
|
49
|
+
Pxlsrt::Helpers.error("File #{input} is not a valid PNG.") if options[:verbose]
|
50
|
+
raise "Invalid PNG"
|
51
|
+
end
|
52
|
+
else
|
53
|
+
Pxlsrt::Helpers.error("File #{input} doesn't exist!") if options[:verbose]
|
54
|
+
raise "File doesn't exit"
|
55
|
+
end
|
56
|
+
elsif input.class != String and input.class != ChunkyPNG::Image
|
57
|
+
Pxlsrt::Helpers.error("Input is not a filename or ChunkyPNG::Image") if options[:verbose]
|
58
|
+
raise "Invalid input (must be filename or ChunkyPNG::Image)"
|
59
|
+
end
|
60
|
+
Pxlsrt::Helpers.verbose("Seed mode.") if options[:verbose]
|
61
|
+
Pxlsrt::Helpers.verbose("Creating Pxlsrt::Image object") if options[:verbose]
|
62
|
+
png = Pxlsrt::Image.new(input)
|
63
|
+
traversed = [false] * (png.getWidth * png.getHeight)
|
64
|
+
count = 0
|
65
|
+
seeds = []
|
66
|
+
if options[:random] != false
|
67
|
+
Pxlsrt::Helpers.progress("Planting seeds", 0, options[:random]) if options[:verbose]
|
68
|
+
for s in (0...options[:random])
|
69
|
+
x = (0...png.getWidth).to_a.sample
|
70
|
+
y = (0...png.getHeight).to_a.sample
|
71
|
+
seeds.push({
|
72
|
+
:spiral => Pxlsrt::Spiral.new(x, y),
|
73
|
+
:pixels => [png[x, y]],
|
74
|
+
:xy => [{:x => x, :y=>y}],
|
75
|
+
:placed => true,
|
76
|
+
:retired => false,
|
77
|
+
:anchor => {
|
78
|
+
:x => x,
|
79
|
+
:y => y
|
80
|
+
}
|
81
|
+
})
|
82
|
+
Pxlsrt::Helpers.progress("Planting seeds", s + 1, options[:random]) if options[:verbose]
|
83
|
+
end
|
84
|
+
else
|
85
|
+
Pxlsrt::Helpers.progress("Planting seeds", 0, png.getWidth * png.getHeight) if options[:verbose]
|
86
|
+
kernel = [[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]]
|
87
|
+
i = (png.getWidth + png.getHeight - 2) * 2
|
88
|
+
for y in (1...(png.getHeight - 1))
|
89
|
+
for x in (1...(png.getWidth - 1))
|
90
|
+
sum = 0
|
91
|
+
for ky in ((-1)..1)
|
92
|
+
for kx in ((-1)..1)
|
93
|
+
sum += kernel[ky + 1][kx + 1] * (ChunkyPNG::Color.r(png[x + kx, y + ky]))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
if sum < options[:threshold]
|
97
|
+
seeds.push({
|
98
|
+
:spiral => Pxlsrt::Spiral.new(x, y),
|
99
|
+
:pixels => [png[x, y]],
|
100
|
+
:xy => [{:x => x, :y=>y}],
|
101
|
+
:placed => true,
|
102
|
+
:retired => false,
|
103
|
+
:anchor => {
|
104
|
+
:x => x,
|
105
|
+
:y => y
|
106
|
+
}
|
107
|
+
})
|
108
|
+
end
|
109
|
+
i += 1
|
110
|
+
Pxlsrt::Helpers.progress("Planting seeds", i, png.getWidth * png.getHeight) if options[:verbose]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if options[:distance] != false
|
114
|
+
Pxlsrt::Helpers.progress("Removing seed clusters", 0, seeds.length) if options[:verbose]
|
115
|
+
results = []
|
116
|
+
i = 0
|
117
|
+
for current in seeds
|
118
|
+
add = true
|
119
|
+
for other in results
|
120
|
+
d = Math.sqrt((current[:anchor][:x] - other[:anchor][:x]) ** 2 + (current[:anchor][:y] - other[:anchor][:y]) ** 2)
|
121
|
+
add = false if d > 0 and d < options[:distance]
|
122
|
+
end
|
123
|
+
if add
|
124
|
+
results.push(current)
|
125
|
+
end
|
126
|
+
i += 1
|
127
|
+
Pxlsrt::Helpers.progress("Removing seed clusters", i, seeds.length) if options[:verbose]
|
128
|
+
end
|
129
|
+
seeds = results
|
130
|
+
end
|
131
|
+
end
|
132
|
+
for r in (0...seeds.length)
|
133
|
+
traversed[seeds[r][:anchor][:x] + seeds[r][:anchor][:y] * png.getWidth] = r
|
134
|
+
count += 1
|
135
|
+
end
|
136
|
+
Pxlsrt::Helpers.verbose("Planted #{seeds.length} seeds") if options[:verbose]
|
137
|
+
step = 0
|
138
|
+
Pxlsrt::Helpers.progress("Watch them grow!", count, traversed.length) if options[:verbose]
|
139
|
+
while count < traversed.length and seeds.length != 0
|
140
|
+
r = 0
|
141
|
+
retired = []
|
142
|
+
for seed in seeds
|
143
|
+
if !seed[:retired]
|
144
|
+
n = seed[:spiral].next
|
145
|
+
if n[:x] >= 0 and n[:y] >= 0 and n[:x] < png.getWidth and n[:y] < png.getHeight and !traversed[n[:x] + n[:y] * png.getWidth]
|
146
|
+
seed[:pixels].push(png[n[:x], n[:y]])
|
147
|
+
traversed[n[:x] + n[:y] * png.getWidth] = r
|
148
|
+
seed[:xy].push(n)
|
149
|
+
seed[:placed] = true
|
150
|
+
count += 1
|
151
|
+
else
|
152
|
+
if seed[:placed] == true
|
153
|
+
seed[:placed] = {
|
154
|
+
:count => 1,
|
155
|
+
:direction => seed[:spiral].direction,
|
156
|
+
:cycle => seed[:spiral].cycles
|
157
|
+
}
|
158
|
+
case seed[:placed][:direction]
|
159
|
+
when "up", "down"
|
160
|
+
seed[:placed][:value] = seed[:spiral].pos[:y]
|
161
|
+
seed[:placed][:valueS] = :y
|
162
|
+
when "left", "right"
|
163
|
+
seed[:placed][:value] = seed[:spiral].pos[:x]
|
164
|
+
seed[:placed][:valueS] = :x
|
165
|
+
end
|
166
|
+
else
|
167
|
+
seed[:placed][:count] += 1
|
168
|
+
if seed[:spiral].cycles != seed[:placed][:cycle] and seed[:placed][:direction] == seed[:spiral].direction and seed[:placed][:value] == seed[:spiral].pos[seed[:placed][:valueS]]
|
169
|
+
seed[:retired] = true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
r += 1
|
175
|
+
end
|
176
|
+
step += 1
|
177
|
+
Pxlsrt::Helpers.progress("Watch them grow!", count, traversed.length) if options[:verbose]
|
178
|
+
end
|
179
|
+
Pxlsrt::Helpers.progress("Sort seeds and place pixels", 0, seeds.length) if options[:verbose]
|
180
|
+
r = 0
|
181
|
+
for seed in seeds
|
182
|
+
spiral = Pxlsrt::Spiral.new(seed[:anchor][:x], seed[:anchor][:y])
|
183
|
+
band = Pxlsrt::Helpers.handlePixelSort(seed[:pixels], options)
|
184
|
+
i = 0
|
185
|
+
for k in seed[:xy]
|
186
|
+
png[k[:x], k[:y]] = band[i]
|
187
|
+
i += 1
|
188
|
+
end
|
189
|
+
r += 1
|
190
|
+
Pxlsrt::Helpers.progress("Sort seeds and place pixels", r, seeds.length) if options[:verbose]
|
191
|
+
end
|
192
|
+
endTime=Time.now
|
193
|
+
timeElapsed=endTime-startTime
|
194
|
+
if timeElapsed < 60
|
195
|
+
Pxlsrt::Helpers.verbose("Took #{timeElapsed.round(4)} second#{ timeElapsed!=1.0 ? "s" : "" }.") if options[:verbose]
|
196
|
+
else
|
197
|
+
minutes=(timeElapsed/60).floor
|
198
|
+
seconds=(timeElapsed % 60).round(4)
|
199
|
+
Pxlsrt::Helpers.verbose("Took #{minutes} minute#{ minutes!=1 ? "s" : "" } and #{seconds} second#{ seconds!=1.0 ? "s" : "" }.") if options[:verbose]
|
200
|
+
end
|
201
|
+
Pxlsrt::Helpers.verbose("Returning ChunkyPNG::Image...") if options[:verbose]
|
202
|
+
return png.returnModified
|
203
|
+
else
|
204
|
+
Pxlsrt::Helpers.error("Options specified do not follow the correct format.") if options[:verbose]
|
205
|
+
raise "Bad options"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Pxlsrt
|
2
|
+
##
|
3
|
+
# Spiral iteration.
|
4
|
+
class Spiral
|
5
|
+
def initialize(x, y)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
@direction = "up"
|
9
|
+
@step = 1
|
10
|
+
@at = 0
|
11
|
+
@count = 0
|
12
|
+
@cycles = -1
|
13
|
+
end
|
14
|
+
##
|
15
|
+
# Return current x value.
|
16
|
+
def x
|
17
|
+
return @x
|
18
|
+
end
|
19
|
+
##
|
20
|
+
# Return current y value.
|
21
|
+
def y
|
22
|
+
return @y
|
23
|
+
end
|
24
|
+
##
|
25
|
+
# Return current direction.
|
26
|
+
def direction
|
27
|
+
return @direction
|
28
|
+
end
|
29
|
+
##
|
30
|
+
# Return amount iterated.
|
31
|
+
def count
|
32
|
+
return @count
|
33
|
+
end
|
34
|
+
##
|
35
|
+
# Return cycles gone through completely.
|
36
|
+
def cycles
|
37
|
+
return @cycles
|
38
|
+
end
|
39
|
+
##
|
40
|
+
# Return current position.
|
41
|
+
def pos
|
42
|
+
return {:x => @x, :y => @y}
|
43
|
+
end
|
44
|
+
##
|
45
|
+
# Goes to next position. Returns position.
|
46
|
+
def next
|
47
|
+
case @direction
|
48
|
+
when "left"
|
49
|
+
@x -= 1
|
50
|
+
@at += 1
|
51
|
+
if @at == @step
|
52
|
+
@direction = "down"
|
53
|
+
@at = 0
|
54
|
+
@step += 1
|
55
|
+
end
|
56
|
+
when "down"
|
57
|
+
@y += 1
|
58
|
+
@at += 1
|
59
|
+
if @at == @step
|
60
|
+
@direction = "right"
|
61
|
+
@at = 0
|
62
|
+
end
|
63
|
+
when "right"
|
64
|
+
@x += 1
|
65
|
+
@at += 1
|
66
|
+
if @at == @step
|
67
|
+
@direction = "up"
|
68
|
+
@at = 0
|
69
|
+
@step += 1
|
70
|
+
end
|
71
|
+
when "up"
|
72
|
+
@cycles += 1 if @at == 0
|
73
|
+
@y -= 1
|
74
|
+
@at += 1
|
75
|
+
if @at == @step
|
76
|
+
@direction = "left"
|
77
|
+
@at = 0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@count += 1
|
81
|
+
return pos
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/pxlsrt/version.rb
CHANGED
data/lib/pxlsrt.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pxlsrt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- EVA-01
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -82,7 +82,9 @@ files:
|
|
82
82
|
- lib/pxlsrt/image.rb
|
83
83
|
- lib/pxlsrt/kim.rb
|
84
84
|
- lib/pxlsrt/lines.rb
|
85
|
+
- lib/pxlsrt/seed.rb
|
85
86
|
- lib/pxlsrt/smart.rb
|
87
|
+
- lib/pxlsrt/spiral.rb
|
86
88
|
- lib/pxlsrt/version.rb
|
87
89
|
homepage: https://github.com/EVA-01/pxlsrt
|
88
90
|
licenses:
|