pxlsrt 1.6.3 → 1.7.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 +23 -7
- data/lib/pxlsrt.rb +2 -1
- data/lib/pxlsrt/brute.rb +7 -8
- data/lib/pxlsrt/colors.rb +23 -52
- data/lib/pxlsrt/helpers.rb +21 -0
- data/lib/pxlsrt/image.rb +16 -0
- data/lib/pxlsrt/kim.rb +263 -0
- data/lib/pxlsrt/smart.rb +7 -8
- data/lib/pxlsrt/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a70f5d32502428d68f198d43c0ee703814b0827
|
4
|
+
data.tar.gz: 213ac35f136cf0de3cc07329e7ced721a47df470
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b492703dd354aead349520473721db2a8ef810e9495fa7b8e4b6e2d362a4083496b563be6c8204d5cee8c6cb915a08ac933bbe77253a9b767ea2e95d407a9af
|
7
|
+
data.tar.gz: 9f2c0daebb6336b7dd56222c8eb04b03957d0cc3b5e9735a5e77593b56de6bb71e1de6e3309638390eaa49109f28ae7ddd743c30f6391b6e9398c4adfbf8d5f8
|
data/bin/pxlsrt
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
3
|
require 'pxlsrt'
|
5
4
|
require 'thor'
|
6
5
|
|
@@ -14,14 +13,14 @@ require 'thor'
|
|
14
13
|
# * verbose - Have pxlsrt tell you what it's working on.
|
15
14
|
# * help - More in depth commands.
|
16
15
|
class CLI < Thor
|
17
|
-
class_option :reverse, :default => false, :aliases => "-r"
|
18
|
-
class_option :vertical, :type => :boolean, :default => false, :aliases => "-v"
|
19
|
-
class_option :diagonal, :type => :boolean, :default => false, :aliases => "-d"
|
20
|
-
class_option :smooth, :type => :boolean, :default => false, :aliases => "-s"
|
21
|
-
class_option :method, :type => :string, :default => "sum-rgb", :banner => "[sum-rgb | red | green | blue | sum-hsb | hue | saturation | brightness | uniqueness | luma | random | cyan | magenta | yellow | alpha | sum-rgba | sum-hsba]", :aliases => "-m", :enum => ["sum-rgb", "red", "green", "blue", "sum-hsb", "hue", "saturation", "brightness", "uniqueness", "luma", "random", "cyan", "magenta", "yellow", "alpha", "sum-rgba", "sum-hsba"]
|
22
16
|
class_option :verbose, :type => :boolean, :default => false, :aliases => "-V"
|
23
|
-
class_option :middle, :default => false, :aliases => "-M"
|
24
17
|
|
18
|
+
option :reverse, :default => false, :aliases => "-r"
|
19
|
+
option :vertical, :type => :boolean, :default => false, :aliases => "-v"
|
20
|
+
option :diagonal, :type => :boolean, :default => false, :aliases => "-d"
|
21
|
+
option :smooth, :type => :boolean, :default => false, :aliases => "-s"
|
22
|
+
option :method, :type => :string, :default => "sum-rgb", :banner => "[#{Pxlsrt::Colors::METHODS.join(" | ")}]", :aliases => "-m", :enum => Pxlsrt::Colors::METHODS
|
23
|
+
option :middle, :default => false, :aliases => "-M"
|
25
24
|
option :min, :type => :numeric, :default => Float::INFINITY, :banner => "MINIMUM BANDWIDTH"
|
26
25
|
option :max, :type => :numeric, :default => Float::INFINITY, :banner => "MAXIMUM BANDWIDTH"
|
27
26
|
desc "brute INPUT OUTPUT [options]", "Brute pixel sorting"
|
@@ -37,6 +36,12 @@ class CLI < Thor
|
|
37
36
|
Pxlsrt::Brute.suite(input, output, k)
|
38
37
|
end
|
39
38
|
|
39
|
+
option :reverse, :default => false, :aliases => "-r"
|
40
|
+
option :vertical, :type => :boolean, :default => false, :aliases => "-v"
|
41
|
+
option :diagonal, :type => :boolean, :default => false, :aliases => "-d"
|
42
|
+
option :smooth, :type => :boolean, :default => false, :aliases => "-s"
|
43
|
+
option :method, :type => :string, :default => "sum-rgb", :banner => "[#{Pxlsrt::Colors::METHODS.join(" | ")}]", :aliases => "-m", :enum => Pxlsrt::Colors::METHODS
|
44
|
+
option :middle, :default => false, :aliases => "-M"
|
40
45
|
option :absolute, :type => :boolean, :default => false, :aliases => "-a", :banner => "ABSOLUTE EDGE FINDING"
|
41
46
|
option :threshold, :type => :numeric, :default => 20, :aliases => "-t"
|
42
47
|
desc "smart INPUT OUTPUT [options]", "Smart pixel sorting"
|
@@ -51,6 +56,17 @@ class CLI < Thor
|
|
51
56
|
end
|
52
57
|
Pxlsrt::Smart.suite(input, output, k)
|
53
58
|
end
|
59
|
+
|
60
|
+
option :method, :type => :string, :default => "brightness", :aliases => "-m", :enum => ["brightness", "white", "black"]
|
61
|
+
option :value, :type => :numeric, :aliases => "-v"
|
62
|
+
desc "kim INPUT OUTPUT [outputs]", "Uses Kim Asendorf's algorithm"
|
63
|
+
def kim(input, output)
|
64
|
+
k= {:trusted => true}
|
65
|
+
for o in options.keys
|
66
|
+
k[o.to_sym] = options[o]
|
67
|
+
end
|
68
|
+
Pxlsrt::Kim.suite(input, output, k)
|
69
|
+
end
|
54
70
|
end
|
55
71
|
|
56
72
|
CLI.start(ARGV)
|
data/lib/pxlsrt.rb
CHANGED
data/lib/pxlsrt/brute.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'oily_png'
|
3
2
|
|
4
3
|
module Pxlsrt
|
@@ -15,7 +14,7 @@ module Pxlsrt
|
|
15
14
|
end
|
16
15
|
end
|
17
16
|
##
|
18
|
-
# The main attraction of the Brute class. Returns a ChunkyPNG::Image that is sorted according to the options provided. Will
|
17
|
+
# The main attraction of the Brute class. Returns a ChunkyPNG::Image that is sorted according to the options provided. Will raise any error that occurs.
|
19
18
|
def self.brute(input, o={})
|
20
19
|
startTime=Time.now
|
21
20
|
defOptions={
|
@@ -35,7 +34,7 @@ module Pxlsrt
|
|
35
34
|
:vertical => [false, true],
|
36
35
|
:diagonal => [false, true],
|
37
36
|
:smooth => [false, true],
|
38
|
-
:method =>
|
37
|
+
:method => Pxlsrt::Colors::METHODS,
|
39
38
|
:verbose => [false, true],
|
40
39
|
:min => [Float::INFINITY, {:class => [Fixnum]}],
|
41
40
|
:max => [Float::INFINITY, {:class => [Fixnum]}],
|
@@ -53,17 +52,14 @@ module Pxlsrt
|
|
53
52
|
else
|
54
53
|
Pxlsrt::Helpers.error("File #{input} is not a valid PNG.") if options[:verbose]
|
55
54
|
raise "Invalid PNG"
|
56
|
-
return
|
57
55
|
end
|
58
56
|
else
|
59
57
|
Pxlsrt::Helpers.error("File #{input} doesn't exist!") if options[:verbose]
|
60
58
|
raise "File doesn't exit"
|
61
|
-
return
|
62
59
|
end
|
63
60
|
elsif input.class!=String and input.class!=ChunkyPNG::Image
|
64
61
|
Pxlsrt::Helpers.error("Input is not a filename or ChunkyPNG::Image") if options[:verbose]
|
65
62
|
raise "Invalid input (must be filename or ChunkyPNG::Image)"
|
66
|
-
return
|
67
63
|
end
|
68
64
|
Pxlsrt::Helpers.verbose("Brute mode.") if options[:verbose]
|
69
65
|
Pxlsrt::Helpers.verbose("Creating Pxlsrt::Image object") if options[:verbose]
|
@@ -86,7 +82,9 @@ module Pxlsrt
|
|
86
82
|
else
|
87
83
|
iterator = lines.keys
|
88
84
|
end
|
89
|
-
|
85
|
+
prr = 0
|
86
|
+
len = iterator.to_a.length
|
87
|
+
Pxlsrt::Helpers.progress("Dividing and pixel sorting lines", prr, len) if options[:verbose]
|
90
88
|
for k in iterator
|
91
89
|
line = lines[k]
|
92
90
|
divisions = Pxlsrt::Lines.randomSlices(line.length,options[:min],options[:max])
|
@@ -102,6 +100,8 @@ module Pxlsrt
|
|
102
100
|
png.replaceDiagonal(k, newLine) if !options[:vertical]
|
103
101
|
png.replaceRDiagonal(k, newLine) if options[:vertical]
|
104
102
|
end
|
103
|
+
prr += 1
|
104
|
+
Pxlsrt::Helpers.progress("Dividing and pixel sorting lines", prr, len) if options[:verbose]
|
105
105
|
end
|
106
106
|
endTime=Time.now
|
107
107
|
timeElapsed=endTime-startTime
|
@@ -117,7 +117,6 @@ module Pxlsrt
|
|
117
117
|
else
|
118
118
|
Pxlsrt::Helpers.error("Options specified do not follow the correct format.") if options[:verbose]
|
119
119
|
raise "Bad options"
|
120
|
-
return
|
121
120
|
end
|
122
121
|
end
|
123
122
|
end
|
data/lib/pxlsrt/colors.rb
CHANGED
@@ -4,6 +4,9 @@ module Pxlsrt
|
|
4
4
|
##
|
5
5
|
# Includes color operations.
|
6
6
|
class Colors
|
7
|
+
##
|
8
|
+
# List of sorting methods.
|
9
|
+
METHODS = ["sum-rgb", "red", "green", "blue", "sum-hsb", "hue", "saturation", "brightness", "uniqueness", "luma", "random", "cyan", "magenta", "yellow", "alpha", "sum-rgba", "sum-hsba"]
|
7
10
|
##
|
8
11
|
# Converts a ChunkyPNG pixel into an array of the red, green, blue, and alpha values
|
9
12
|
def self.getRGBA(pxl)
|
@@ -15,38 +18,6 @@ module Pxlsrt
|
|
15
18
|
return File.open(path, 'rb').read(9).include?('PNG')
|
16
19
|
end
|
17
20
|
##
|
18
|
-
# Converts an RGB-like array ([red, green, blue]) into an HSB-like array ([hue, saturation, brightness]).
|
19
|
-
def self.rgb2hsb(rgb)
|
20
|
-
r = rgb[0] / 255.0
|
21
|
-
g = rgb[1] / 255.0
|
22
|
-
b = rgb[2] / 255.0
|
23
|
-
max = [r, g, b].max
|
24
|
-
min = [r, g, b].min
|
25
|
-
delta = max - min
|
26
|
-
v = max * 100
|
27
|
-
if (max != 0.0)
|
28
|
-
s = delta / max *100
|
29
|
-
else
|
30
|
-
s = 0.0
|
31
|
-
end
|
32
|
-
if (s == 0.0)
|
33
|
-
h = 0.0
|
34
|
-
else
|
35
|
-
if (r == max)
|
36
|
-
h = (g - b) / delta
|
37
|
-
elsif (g == max)
|
38
|
-
h = 2 + (b - r) / delta
|
39
|
-
elsif (b == max)
|
40
|
-
h = 4 + (r - g) / delta
|
41
|
-
end
|
42
|
-
h *= 60.0
|
43
|
-
if (h < 0)
|
44
|
-
h += 360.0
|
45
|
-
end
|
46
|
-
end
|
47
|
-
return [h,s,v]
|
48
|
-
end
|
49
|
-
##
|
50
21
|
# Averages an array of RGB-like arrays.
|
51
22
|
def self.colorAverage(ca, chunky = false)
|
52
23
|
if ca.length==1
|
@@ -106,49 +77,49 @@ module Pxlsrt
|
|
106
77
|
Pxlsrt::Helpers.error(list) if list.length == 0
|
107
78
|
case how.downcase
|
108
79
|
when "sum-rgb"
|
109
|
-
mhm= list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
80
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
110
81
|
when "sum-rgba"
|
111
|
-
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c)+ChunkyPNG::Color.a(c) }
|
82
|
+
mhm =list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c)+ChunkyPNG::Color.a(c) }
|
112
83
|
when "red"
|
113
|
-
mhm= list.sort_by { |c| ChunkyPNG::Color.r(c) }
|
84
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.r(c) }
|
114
85
|
when "yellow"
|
115
|
-
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c) }
|
86
|
+
mhm =list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c) }
|
116
87
|
when "green"
|
117
|
-
mhm= list.sort_by { |c| ChunkyPNG::Color.g(c) }
|
88
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.g(c) }
|
118
89
|
when "cyan"
|
119
|
-
mhm=list.sort_by { |c| ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
90
|
+
mhm =list.sort_by { |c| ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
120
91
|
when "blue"
|
121
|
-
mhm= list.sort_by { |c| ChunkyPNG::Color.b(c) }
|
92
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.b(c) }
|
122
93
|
when "magenta"
|
123
|
-
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.b(c) }
|
94
|
+
mhm =list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.b(c) }
|
124
95
|
when "hue"
|
125
|
-
mhm= list.sort_by { |c|
|
96
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.to_hsb(c)[0] % 360 }
|
126
97
|
when "saturation"
|
127
|
-
mhm= list.sort_by { |c|
|
98
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.to_hsb(c)[1] }
|
128
99
|
when "brightness"
|
129
|
-
mhm= list.sort_by { |c|
|
100
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.to_hsb(c)[2] }
|
130
101
|
when "sum-hsb"
|
131
|
-
mhm= list.sort_by { |c|
|
102
|
+
mhm = list.sort_by { |c| k = ChunkyPNG::Color.to_hsb(c); (k[0] % 360) / 360.0 + k[1] + k[2] }
|
132
103
|
when "sum-hsba"
|
133
|
-
mhm= list.sort_by { |c|
|
104
|
+
mhm = list.sort_by { |c| k = ChunkyPNG::Color.to_hsb(c); (k[0] % 360) / 360.0 + k[1] + k[2] + ChunkyPNG::Color.a(c) / 255.0 }
|
134
105
|
when "uniqueness"
|
135
|
-
avg=Pxlsrt::Colors.colorAverage(list, true)
|
136
|
-
mhm=list.sort_by { |c| Pxlsrt::Colors.colorUniqueness(c, [avg], true) }
|
106
|
+
avg = Pxlsrt::Colors.colorAverage(list, true)
|
107
|
+
mhm = list.sort_by { |c| Pxlsrt::Colors.colorUniqueness(c, [avg], true) }
|
137
108
|
when "luma"
|
138
|
-
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)*0.2126+ChunkyPNG::Color.g(c)*0.7152+ChunkyPNG::Color.b(c)*0.0722 }
|
109
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.r(c) * 0.2126 + ChunkyPNG::Color.g(c) * 0.7152 + ChunkyPNG::Color.b(c) * 0.0722 + ChunkyPNG::Color.a(c) }
|
139
110
|
when "random"
|
140
|
-
mhm=list.shuffle
|
111
|
+
mhm = list.shuffle
|
141
112
|
when "alpha"
|
142
|
-
mhm=list.sort_by{ |c| ChunkyPNG::Color.a(c) }
|
113
|
+
mhm = list.sort_by{ |c| ChunkyPNG::Color.a(c) }
|
143
114
|
else
|
144
|
-
mhm= list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
115
|
+
mhm = list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
145
116
|
end
|
146
117
|
if reverse == 0
|
147
118
|
return mhm
|
148
119
|
elsif reverse == 1
|
149
120
|
return mhm.reverse
|
150
121
|
else
|
151
|
-
return
|
122
|
+
return [true, false].sample ? mhm : mhm.reverse
|
152
123
|
end
|
153
124
|
end
|
154
125
|
##
|
data/lib/pxlsrt/helpers.rb
CHANGED
@@ -18,6 +18,16 @@ module Pxlsrt
|
|
18
18
|
return "\e[36m#{what}\e[0m"
|
19
19
|
end
|
20
20
|
##
|
21
|
+
# Used to output a yellow string to the terminal.
|
22
|
+
def self.yellow(what)
|
23
|
+
return "\e[33m#{what}\e[0m"
|
24
|
+
end
|
25
|
+
##
|
26
|
+
# Used to output a green string to the terminal.
|
27
|
+
def self.green(what)
|
28
|
+
return "\e[32m#{what}\e[0m"
|
29
|
+
end
|
30
|
+
##
|
21
31
|
# Determines if a string can be a float or integer.
|
22
32
|
def self.isNumeric?(s)
|
23
33
|
true if Float(s) rescue false
|
@@ -90,5 +100,16 @@ module Pxlsrt
|
|
90
100
|
def self.verbose(what)
|
91
101
|
puts "#{Pxlsrt::Helpers.cyan("pxlsrt")} #{what}"
|
92
102
|
end
|
103
|
+
##
|
104
|
+
# Progress indication.
|
105
|
+
def self.progress(what, amount, outof)
|
106
|
+
progress = (amount.to_f * 100.0 / outof.to_f).to_i
|
107
|
+
if progress == 100
|
108
|
+
puts "\r#{Pxlsrt::Helpers.green("pxlsrt")} #{what} (#{Pxlsrt::Helpers.green("#{progress}%")})"
|
109
|
+
else
|
110
|
+
$stdout.write "\r#{Pxlsrt::Helpers.yellow("pxlsrt")} #{what} (#{Pxlsrt::Helpers.yellow("#{progress}%")})"
|
111
|
+
$stdout.flush
|
112
|
+
end
|
113
|
+
end
|
93
114
|
end
|
94
115
|
end
|
data/lib/pxlsrt/image.rb
CHANGED
@@ -158,6 +158,16 @@ module Pxlsrt
|
|
158
158
|
def []=(x, y, color)
|
159
159
|
@modified[x, y] = color
|
160
160
|
end
|
161
|
+
def i(i)
|
162
|
+
x = i % @width
|
163
|
+
y = (i / @width).floor
|
164
|
+
return self[x, y]
|
165
|
+
end
|
166
|
+
def i=(i, color)
|
167
|
+
x = i % @width
|
168
|
+
y = (i / @width).floor
|
169
|
+
self[x, y] = color
|
170
|
+
end
|
161
171
|
##
|
162
172
|
# Return the original, unmodified image.
|
163
173
|
def returnOriginal
|
@@ -168,5 +178,11 @@ module Pxlsrt
|
|
168
178
|
def returnModified
|
169
179
|
return @modified
|
170
180
|
end
|
181
|
+
def getWidth
|
182
|
+
return @width
|
183
|
+
end
|
184
|
+
def getHeight
|
185
|
+
return @height
|
186
|
+
end
|
171
187
|
end
|
172
188
|
end
|
data/lib/pxlsrt/kim.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
|
3
|
+
module Pxlsrt
|
4
|
+
##
|
5
|
+
# Uses Kim Asendorf's pixel sorting algorithm, orginally written in Processing. https://github.com/kimasendorf/ASDFPixelSort
|
6
|
+
class Kim
|
7
|
+
##
|
8
|
+
# Uses Pxlsrt::Kim.kim to input and output from one method.
|
9
|
+
def self.suite(inputFileName, outputFileName, o={})
|
10
|
+
kml=Pxlsrt::Kim.kim(inputFileName, o)
|
11
|
+
if Pxlsrt::Helpers.contented(kml)
|
12
|
+
kml.save(outputFileName)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
##
|
16
|
+
# The main attraction of the Kim class. Returns a ChunkyPNG::Image that is sorted according to the options provided. Will raise any error that occurs.
|
17
|
+
def self.kim(input, o = {})
|
18
|
+
startTime = Time.now
|
19
|
+
defOptions={
|
20
|
+
:method => "brightness",
|
21
|
+
:verbose => false,
|
22
|
+
:value => false,
|
23
|
+
:trusted => false
|
24
|
+
}
|
25
|
+
defRules = {
|
26
|
+
:method => ["brightness", "black", "white"],
|
27
|
+
:verbose => [false, true],
|
28
|
+
:value => [false, {:class => [Fixnum]}],
|
29
|
+
:trusted => [false, true]
|
30
|
+
}
|
31
|
+
options = defOptions.merge(o)
|
32
|
+
if o.length == 0 or options[:trusted] == true or (options[:trusted] == false and o.length != 0 and Pxlsrt::Helpers.checkOptions(options, defRules) != false)
|
33
|
+
if input.class == String
|
34
|
+
Pxlsrt::Helpers.verbose("Getting image from file...") if options[:verbose]
|
35
|
+
if File.file?(input)
|
36
|
+
if Pxlsrt::Colors.isPNG?(input)
|
37
|
+
input = ChunkyPNG::Image.from_file(input)
|
38
|
+
else
|
39
|
+
Pxlsrt::Helpers.error("File #{input} is not a valid PNG.") if options[:verbose]
|
40
|
+
raise "Invalid PNG"
|
41
|
+
end
|
42
|
+
else
|
43
|
+
Pxlsrt::Helpers.error("File #{input} doesn't exist!") if options[:verbose]
|
44
|
+
raise "File doesn't exit"
|
45
|
+
end
|
46
|
+
elsif input.class != String and input.class != ChunkyPNG::Image
|
47
|
+
Pxlsrt::Helpers.error("Input is not a filename or ChunkyPNG::Image") if options[:verbose]
|
48
|
+
raise "Invalid input (must be filename or ChunkyPNG::Image)"
|
49
|
+
end
|
50
|
+
Pxlsrt::Helpers.verbose("Kim Asendorf mode.") if options[:verbose]
|
51
|
+
Pxlsrt::Helpers.verbose("Creating Pxlsrt::Image object") if options[:verbose]
|
52
|
+
png = Pxlsrt::Image.new(input)
|
53
|
+
column = 0
|
54
|
+
row = 0
|
55
|
+
options[:value] ||= ChunkyPNG::Color.rgba(11, 220, 0, 1) if options[:method] == "black"
|
56
|
+
options[:value] ||= 60 if options[:method] == "brightness"
|
57
|
+
options[:value] ||= ChunkyPNG::Color.rgba(57, 167, 192, 1) if options[:method] == "white"
|
58
|
+
Pxlsrt::Helpers.progress("Sorting columns", column, png.getWidth) if options[:verbose]
|
59
|
+
while column < png.getWidth
|
60
|
+
x = column
|
61
|
+
y = 0
|
62
|
+
yend = 0
|
63
|
+
while yend < png.getHeight
|
64
|
+
case options[:method]
|
65
|
+
when "black"
|
66
|
+
y = self.getFirstNotBlackY(png, x, y, options[:value])
|
67
|
+
yend = self.getNextBlackY(png, x, y, options[:value])
|
68
|
+
when "brightness"
|
69
|
+
y = self.getFirstBrightY(png, x, y, options[:value])
|
70
|
+
yend = self.getNextDarkY(png, x, y, options[:value])
|
71
|
+
when "white"
|
72
|
+
y = self.getFirstNotWhiteY(png, x, y, options[:value])
|
73
|
+
yend = self.getNextWhiteY(png, x, y, options[:value])
|
74
|
+
end
|
75
|
+
if y < 0
|
76
|
+
break
|
77
|
+
end
|
78
|
+
sortLength = yend - y;
|
79
|
+
unsorted = []
|
80
|
+
sorted = []
|
81
|
+
for i in (0...sortLength)
|
82
|
+
unsorted[i] = png[x, y + i];
|
83
|
+
end
|
84
|
+
sorted = unsorted.sort
|
85
|
+
for i in (0...sortLength)
|
86
|
+
png[x, y + i] = sorted[i];
|
87
|
+
end
|
88
|
+
y = yend + 1;
|
89
|
+
end
|
90
|
+
column += 1
|
91
|
+
Pxlsrt::Helpers.progress("Sorting columns", column, png.getWidth) if options[:verbose]
|
92
|
+
end
|
93
|
+
Pxlsrt::Helpers.progress("Sorting rows", row, png.getHeight) if options[:verbose]
|
94
|
+
while row < png.getHeight
|
95
|
+
x = 0
|
96
|
+
y = row
|
97
|
+
xend = 0
|
98
|
+
while xend < png.getWidth
|
99
|
+
case options[:method]
|
100
|
+
when "black"
|
101
|
+
x = self.getFirstNotBlackX(png, x, y, options[:value])
|
102
|
+
xend = self.getNextBlackX(png, x, y, options[:value])
|
103
|
+
when "brightness"
|
104
|
+
x = self.getFirstBrightX(png, x, y, options[:value])
|
105
|
+
xend = self.getNextDarkX(png, x, y, options[:value])
|
106
|
+
when "white"
|
107
|
+
x = self.getFirstNotWhiteX(png, x, y, options[:value])
|
108
|
+
xend = self.getNextWhiteX(png, x, y, options[:value])
|
109
|
+
end
|
110
|
+
if x < 0
|
111
|
+
break
|
112
|
+
end
|
113
|
+
sortLength = xend - x
|
114
|
+
unsorted = []
|
115
|
+
sorted = []
|
116
|
+
for i in (0...sortLength)
|
117
|
+
unsorted[i] = png[x + i, y]
|
118
|
+
end
|
119
|
+
sorted = unsorted.sort
|
120
|
+
for i in (0...sortLength)
|
121
|
+
png[x + i, y] = sorted[i];
|
122
|
+
end
|
123
|
+
x = xend + 1;
|
124
|
+
end
|
125
|
+
row += 1
|
126
|
+
Pxlsrt::Helpers.progress("Sorting rows", row, png.getHeight) if options[:verbose]
|
127
|
+
end
|
128
|
+
endTime=Time.now
|
129
|
+
timeElapsed=endTime-startTime
|
130
|
+
if timeElapsed < 60
|
131
|
+
Pxlsrt::Helpers.verbose("Took #{timeElapsed.round(4)} second#{ timeElapsed!=1.0 ? "s" : "" }.") if options[:verbose]
|
132
|
+
else
|
133
|
+
minutes=(timeElapsed/60).floor
|
134
|
+
seconds=(timeElapsed % 60).round(4)
|
135
|
+
Pxlsrt::Helpers.verbose("Took #{minutes} minute#{ minutes!=1 ? "s" : "" } and #{seconds} second#{ seconds!=1.0 ? "s" : "" }.") if options[:verbose]
|
136
|
+
end
|
137
|
+
Pxlsrt::Helpers.verbose("Returning ChunkyPNG::Image...") if options[:verbose]
|
138
|
+
return png.returnModified
|
139
|
+
else
|
140
|
+
Pxlsrt::Helpers.error("Options specified do not follow the correct format.") if options[:verbose]
|
141
|
+
raise "Bad options"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
# Helper methods
|
145
|
+
# Black
|
146
|
+
def self.getFirstNotBlackX(img, x, y, blackValue)
|
147
|
+
if x < img.getWidth
|
148
|
+
while img[x, y] < blackValue
|
149
|
+
x += 1
|
150
|
+
return -1 if x >= img.getWidth
|
151
|
+
end
|
152
|
+
end
|
153
|
+
return x
|
154
|
+
end
|
155
|
+
def self.getFirstNotBlackY(img, x, y, blackValue)
|
156
|
+
if y < img.getHeight
|
157
|
+
while img[x, y] < blackValue
|
158
|
+
y += 1
|
159
|
+
return -1 if y >= img.getHeight
|
160
|
+
end
|
161
|
+
end
|
162
|
+
return y
|
163
|
+
end
|
164
|
+
def self.getNextBlackX(img, x, y, blackValue)
|
165
|
+
x += 1
|
166
|
+
if x < img.getWidth
|
167
|
+
while img[x, y] > blackValue
|
168
|
+
x += 1
|
169
|
+
return (img.getWidth - 1) if x >= img.getWidth
|
170
|
+
end
|
171
|
+
end
|
172
|
+
return x - 1
|
173
|
+
end
|
174
|
+
def self.getNextBlackY(img, x, y, blackValue)
|
175
|
+
y += 1
|
176
|
+
if y < img.getHeight
|
177
|
+
while img[x, y] > blackValue
|
178
|
+
y += 1
|
179
|
+
return (img.getHeight - 1) if y >= img.getHeight
|
180
|
+
end
|
181
|
+
end
|
182
|
+
return y - 1
|
183
|
+
end
|
184
|
+
# Brightness
|
185
|
+
def self.getFirstBrightX(img, x, y, brightnessValue)
|
186
|
+
if x < img.getWidth
|
187
|
+
while ChunkyPNG::Color.to_hsb(img[x, y])[2] * 255 < brightnessValue
|
188
|
+
x += 1;
|
189
|
+
return -1 if x >= img.getWidth
|
190
|
+
end
|
191
|
+
end
|
192
|
+
return x
|
193
|
+
end
|
194
|
+
def self.getFirstBrightY(img, x, y, brightnessValue)
|
195
|
+
if y < img.getHeight
|
196
|
+
while ChunkyPNG::Color.to_hsb(img[x, y])[2] * 255 < brightnessValue
|
197
|
+
y += 1;
|
198
|
+
return -1 if y >= img.getHeight
|
199
|
+
end
|
200
|
+
end
|
201
|
+
return y
|
202
|
+
end
|
203
|
+
def self.getNextDarkX(img, x, y, brightnessValue)
|
204
|
+
x += 1
|
205
|
+
if x < img.getWidth
|
206
|
+
while ChunkyPNG::Color.to_hsb(img[x, y])[2] * 255 > brightnessValue
|
207
|
+
x += 1
|
208
|
+
return (img.getWidth - 1) if x >= img.getWidth
|
209
|
+
end
|
210
|
+
end
|
211
|
+
return x - 1
|
212
|
+
end
|
213
|
+
def self.getNextDarkY(img, x, y, brightnessValue)
|
214
|
+
y += 1
|
215
|
+
if y < img.getHeight
|
216
|
+
while ChunkyPNG::Color.to_hsb(img[x, y])[2] * 255 > brightnessValue
|
217
|
+
y += 1
|
218
|
+
return (img.getHeight - 1) if y >= img.getHeight
|
219
|
+
end
|
220
|
+
end
|
221
|
+
return y - 1
|
222
|
+
end
|
223
|
+
# White
|
224
|
+
def self.getFirstNotWhiteX(img, x, y, whiteValue)
|
225
|
+
if x < img.getWidth
|
226
|
+
while img[x, y] > whiteValue
|
227
|
+
x += 1
|
228
|
+
return -1 if x >= img.getWidth
|
229
|
+
end
|
230
|
+
end
|
231
|
+
return x
|
232
|
+
end
|
233
|
+
def self.getFirstNotWhiteY(img, x, y, whiteValue)
|
234
|
+
if y < img.getHeight
|
235
|
+
while img[x, y] > whiteValue
|
236
|
+
y += 1
|
237
|
+
return -1 if y >= img.getHeight
|
238
|
+
end
|
239
|
+
end
|
240
|
+
return y
|
241
|
+
end
|
242
|
+
def self.getNextWhiteX(img, x, y, whiteValue)
|
243
|
+
x += 1
|
244
|
+
if x < img.getWidth
|
245
|
+
while img[x, y] < whiteValue
|
246
|
+
x += 1
|
247
|
+
return (img.getWidth - 1) if x >= img.getWidth
|
248
|
+
end
|
249
|
+
end
|
250
|
+
return x - 1
|
251
|
+
end
|
252
|
+
def self.getNextWhiteY(img, x, y, whiteValue)
|
253
|
+
y += 1
|
254
|
+
if y < img.getHeight
|
255
|
+
while img[x, y] < whiteValue
|
256
|
+
y += 1
|
257
|
+
return (img.getHeight - 1) if y >= img.getHeight
|
258
|
+
end
|
259
|
+
end
|
260
|
+
return y - 1
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
data/lib/pxlsrt/smart.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'oily_png'
|
3
2
|
|
4
3
|
module Pxlsrt
|
@@ -16,7 +15,7 @@ module Pxlsrt
|
|
16
15
|
end
|
17
16
|
end
|
18
17
|
##
|
19
|
-
# The main attraction of the Smart class. Returns a ChunkyPNG::Image that is sorted according to the options provided. Will
|
18
|
+
# The main attraction of the Smart class. Returns a ChunkyPNG::Image that is sorted according to the options provided. Will raise any error that occurs.
|
20
19
|
def self.smart(input, o={})
|
21
20
|
startTime=Time.now
|
22
21
|
defOptions={
|
@@ -36,7 +35,7 @@ module Pxlsrt
|
|
36
35
|
:vertical => [false, true],
|
37
36
|
:diagonal => [false, true],
|
38
37
|
:smooth => [false, true],
|
39
|
-
:method =>
|
38
|
+
:method => Pxlsrt::Colors::METHODS,
|
40
39
|
:verbose => [false, true],
|
41
40
|
:absolute => [false, true],
|
42
41
|
:threshold => [{:class => [Float, Fixnum]}],
|
@@ -54,17 +53,14 @@ module Pxlsrt
|
|
54
53
|
else
|
55
54
|
Pxlsrt::Helpers.error("File #{input} is not a valid PNG.") if options[:verbose]
|
56
55
|
raise "Invalid PNG"
|
57
|
-
return
|
58
56
|
end
|
59
57
|
else
|
60
58
|
Pxlsrt::Helpers.error("File #{input} doesn't exist!") if options[:verbose]
|
61
59
|
raise "File doesn't exist"
|
62
|
-
return
|
63
60
|
end
|
64
61
|
elsif input.class!=String and input.class!=ChunkyPNG::Image
|
65
62
|
Pxlsrt::Helpers.error("Input is not a filename or ChunkyPNG::Image") if options[:verbose]
|
66
63
|
raise "Invalid input (must be filename or ChunkyPNG::Image)"
|
67
|
-
return
|
68
64
|
end
|
69
65
|
Pxlsrt::Helpers.verbose("Smart mode.") if options[:verbose]
|
70
66
|
png=Pxlsrt::Image.new(input)
|
@@ -88,7 +84,9 @@ module Pxlsrt
|
|
88
84
|
else
|
89
85
|
iterator = lines.keys
|
90
86
|
end
|
91
|
-
|
87
|
+
prr = 0
|
88
|
+
len = iterator.to_a.length
|
89
|
+
Pxlsrt::Helpers.progress("Dividing and pixel sorting lines", prr, len) if options[:verbose]
|
92
90
|
for k in iterator
|
93
91
|
line = lines[k]
|
94
92
|
divisions = []
|
@@ -133,6 +131,8 @@ module Pxlsrt
|
|
133
131
|
png.replaceDiagonal(k, newLine) if !options[:vertical]
|
134
132
|
png.replaceRDiagonal(k, newLine) if options[:vertical]
|
135
133
|
end
|
134
|
+
prr += 1
|
135
|
+
Pxlsrt::Helpers.progress("Dividing and pixel sorting lines", prr, len) if options[:verbose]
|
136
136
|
end
|
137
137
|
endTime=Time.now
|
138
138
|
timeElapsed=endTime-startTime
|
@@ -148,7 +148,6 @@ module Pxlsrt
|
|
148
148
|
else
|
149
149
|
Pxlsrt::Helpers.error("Options specified do not follow the correct format.") if options[:verbose]
|
150
150
|
raise "Bad options"
|
151
|
-
return
|
152
151
|
end
|
153
152
|
end
|
154
153
|
end
|
data/lib/pxlsrt/version.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.7.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-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- lib/pxlsrt/colors.rb
|
81
81
|
- lib/pxlsrt/helpers.rb
|
82
82
|
- lib/pxlsrt/image.rb
|
83
|
+
- lib/pxlsrt/kim.rb
|
83
84
|
- lib/pxlsrt/lines.rb
|
84
85
|
- lib/pxlsrt/smart.rb
|
85
86
|
- lib/pxlsrt/version.rb
|