pxlsrt 1.5.1 → 1.6
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 +0 -2
- data/lib/pxlsrt/brute.rb +33 -71
- data/lib/pxlsrt/colors.rb +40 -28
- data/lib/pxlsrt/helpers.rb +24 -0
- data/lib/pxlsrt/image.rb +172 -0
- data/lib/pxlsrt/lines.rb +2 -2
- data/lib/pxlsrt/smart.rb +59 -155
- data/lib/pxlsrt/version.rb +1 -1
- data/lib/pxlsrt.rb +6 -5
- 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: eddc063a560a8420871b095ee6e56c96d1b3b66b
|
4
|
+
data.tar.gz: dcfb8606b9c47d43402e6030a80d274b83d3a4e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4b768476d0bda214468c888b1357396d4c62bc5d60aa45546c8d283f73b1957a3e11112589ae689c6a67a2ec0ca37609b94c55b77d70f118949894fb976655c
|
7
|
+
data.tar.gz: 9afbf4b2e3515a51d95143d7d30e031788f7de39ade4870e98f17938b4026b5c9a687a840c493c58641669db04b7ff6dad3b0f045ed5f07fbe3baf513b593111
|
data/bin/pxlsrt
CHANGED
@@ -39,13 +39,11 @@ class CLI < Thor
|
|
39
39
|
|
40
40
|
option :absolute, :type => :boolean, :default => false, :aliases => "-a", :banner => "ABSOLUTE EDGE FINDING"
|
41
41
|
option :threshold, :type => :numeric, :default => 20, :aliases => "-t"
|
42
|
-
option :edge, :type => :numeric, :default => 2, :aliases => "-e", :banner => "EDGE BUFFERING"
|
43
42
|
desc "smart INPUT OUTPUT [options]", "Smart pixel sorting"
|
44
43
|
##
|
45
44
|
# Specific options:
|
46
45
|
# * threshold - Number used in edge finding. Specifics explained under "absolute".
|
47
46
|
# * absolute - Make edge finding absolute over relative. For example, define a range as a collection of values under the threshold. Relative edge finding is when the contrast of the next pixel is larger than the threshold.
|
48
|
-
# * edge - Buffers edge.
|
49
47
|
def smart(input, output)
|
50
48
|
k={:trusted=>true}
|
51
49
|
for o in options.keys
|
data/lib/pxlsrt/brute.rb
CHANGED
@@ -63,80 +63,42 @@ module Pxlsrt
|
|
63
63
|
return
|
64
64
|
end
|
65
65
|
Pxlsrt::Helpers.verbose("Brute mode.") if options[:verbose]
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
for xy in 0..(w*h-1)
|
81
|
-
kml.push(Pxlsrt::Colors.getRGBA(png[xy % w,(xy/w).floor]))
|
66
|
+
Pxlsrt::Helpers.verbose("Creating Pxlsrt::Image object") if options[:verbose]
|
67
|
+
png=Pxlsrt::Image.new(input)
|
68
|
+
if !options[:vertical] and !options[:diagonal]
|
69
|
+
Pxlsrt::Helpers.verbose("Retrieving rows") if options[:verbose]
|
70
|
+
lines = png.horizontalLines
|
71
|
+
elsif options[:vertical] and !options[:diagonal]
|
72
|
+
Pxlsrt::Helpers.verbose("Retrieving columns") if options[:verbose]
|
73
|
+
lines = png.verticalLines
|
74
|
+
elsif !options[:vertical] and options[:diagonal]
|
75
|
+
Pxlsrt::Helpers.verbose("Retrieving diagonals") if options[:verbose]
|
76
|
+
lines = png.diagonalLines
|
77
|
+
elsif options[:vertical] and options[:diagonal]
|
78
|
+
Pxlsrt::Helpers.verbose("Retrieving diagonals") if options[:verbose]
|
79
|
+
lines = png.rDiagonalLines
|
82
80
|
end
|
83
|
-
if options[:vertical]==true
|
84
|
-
Pxlsrt::Helpers.verbose("Rotating image for vertical mode...") if options[:verbose]
|
85
|
-
kml=Pxlsrt::Lines.rotateImage(kml, w, h, 3)
|
86
|
-
w,h=h,w
|
87
|
-
end
|
88
|
-
toImage=[]
|
89
81
|
if !options[:diagonal]
|
90
|
-
|
91
|
-
for m in Pxlsrt::Lines.imageRGBLines(kml, w)
|
92
|
-
sliceRanges=Pxlsrt::Lines.randomSlices(m, options[:min], options[:max])
|
93
|
-
newInTown=[]
|
94
|
-
if options[:smooth]!=true
|
95
|
-
for ranger in sliceRanges
|
96
|
-
newInTown.concat(Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(m[ranger[0]..ranger[1]], options[:method].downcase, nre), options[:middle]))
|
97
|
-
end
|
98
|
-
else
|
99
|
-
for ranger in sliceRanges
|
100
|
-
k=(m[ranger[0]..ranger[1]]).group_by { |x| x }
|
101
|
-
g=Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(k.keys, options[:method].downcase, nre), options[:middle])
|
102
|
-
j=g.map { |x| k[x] }.flatten(1)
|
103
|
-
newInTown.concat(j)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
toImage.concat(newInTown)
|
107
|
-
end
|
82
|
+
iterator = 0...(lines.length)
|
108
83
|
else
|
109
|
-
|
110
|
-
dia=Pxlsrt::Lines.getDiagonals(kml,w,h)
|
111
|
-
Pxlsrt::Helpers.verbose("Pixel sorting using method '#{options[:method]}'...") if options[:verbose]
|
112
|
-
for m in dia.keys
|
113
|
-
sliceRanges=Pxlsrt::Lines.randomSlices(dia[m], options[:min], options[:max])
|
114
|
-
newInTown=[]
|
115
|
-
if options[:smooth]!=true
|
116
|
-
for ranger in sliceRanges
|
117
|
-
newInTown.concat(Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(dia[m][ranger[0]..ranger[1]], options[:method].downcase, nre), options[:middle]))
|
118
|
-
end
|
119
|
-
else
|
120
|
-
for ranger in sliceRanges
|
121
|
-
k=(dia[m][ranger[0]..ranger[1]]).group_by { |x| x }
|
122
|
-
g=Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(k.keys, options[:method].downcase, nre), options[:middle])
|
123
|
-
j=g.map { |x| k[x] }.flatten(1)
|
124
|
-
newInTown.concat(j)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
dia[m]=newInTown
|
128
|
-
end
|
129
|
-
Pxlsrt::Helpers.verbose("Setting diagonals back to standard lines...") if options[:verbose]
|
130
|
-
toImage=Pxlsrt::Lines.fromDiagonals(dia,w)
|
131
|
-
end
|
132
|
-
if options[:vertical]==true
|
133
|
-
Pxlsrt::Helpers.verbose("Rotating back (because of vertical mode).") if options[:verbose]
|
134
|
-
toImage=Pxlsrt::Lines.rotateImage(toImage, w,h,1)
|
135
|
-
w,h=h,w
|
84
|
+
iterator = lines.keys
|
136
85
|
end
|
137
|
-
Pxlsrt::Helpers.verbose("
|
138
|
-
for
|
139
|
-
|
86
|
+
Pxlsrt::Helpers.verbose("Dividing and pixel sorting lines") if options[:verbose]
|
87
|
+
for k in iterator
|
88
|
+
line = lines[k]
|
89
|
+
divisions = Pxlsrt::Lines.randomSlices(line.length,options[:min],options[:max])
|
90
|
+
newLine = []
|
91
|
+
for division in divisions
|
92
|
+
band = line[division[0]..division[1]]
|
93
|
+
newLine.concat(Pxlsrt::Helpers.handlePixelSort(band, options))
|
94
|
+
end
|
95
|
+
if !options[:diagonal]
|
96
|
+
png.replaceHorizontal(k, newLine) if !options[:vertical]
|
97
|
+
png.replaceVertical(k, newLine) if options[:vertical]
|
98
|
+
else
|
99
|
+
png.replaceDiagonal(k, newLine) if !options[:vertical]
|
100
|
+
png.replaceRDiagonal(k, newLine) if options[:vertical]
|
101
|
+
end
|
140
102
|
end
|
141
103
|
endTime=Time.now
|
142
104
|
timeElapsed=endTime-startTime
|
@@ -148,7 +110,7 @@ module Pxlsrt
|
|
148
110
|
Pxlsrt::Helpers.verbose("Took #{minutes} minute#{ minutes!=1 ? "s" : "" } and #{seconds} second#{ seconds!=1.0 ? "s" : "" }.") if options[:verbose]
|
149
111
|
end
|
150
112
|
Pxlsrt::Helpers.verbose("Returning ChunkyPNG::Image...") if options[:verbose]
|
151
|
-
return
|
113
|
+
return png.returnModified
|
152
114
|
else
|
153
115
|
Pxlsrt::Helpers.error("Options specified do not follow the correct format.") if options[:verbose]
|
154
116
|
return
|
data/lib/pxlsrt/colors.rb
CHANGED
@@ -48,25 +48,37 @@ module Pxlsrt
|
|
48
48
|
end
|
49
49
|
##
|
50
50
|
# Averages an array of RGB-like arrays.
|
51
|
-
def self.colorAverage(ca)
|
51
|
+
def self.colorAverage(ca, chunky = false)
|
52
52
|
if ca.length==1
|
53
53
|
return ca.first
|
54
54
|
end
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
if !chunky
|
56
|
+
r=((ca.collect { |c| c[0] }).inject{ |sum, el| sum+el }).to_f / ca.size
|
57
|
+
g=((ca.collect { |c| c[1] }).inject{ |sum, el| sum+el }).to_f / ca.size
|
58
|
+
b=((ca.collect { |c| c[2] }).inject{ |sum, el| sum+el }).to_f / ca.size
|
59
|
+
a=((ca.collect { |c| c[3] }).inject{ |sum, el| sum+el }).to_f / ca.size
|
60
|
+
return [r,g,b,a]
|
61
|
+
else
|
62
|
+
r=((ca.collect { |c| ChunkyPNG::Color.r(c) }).inject{ |sum, el| sum+el }).to_f / ca.size
|
63
|
+
g=((ca.collect { |c| ChunkyPNG::Color.g(c) }).inject{ |sum, el| sum+el }).to_f / ca.size
|
64
|
+
b=((ca.collect { |c| ChunkyPNG::Color.b(c) }).inject{ |sum, el| sum+el }).to_f / ca.size
|
65
|
+
a=((ca.collect { |c| ChunkyPNG::Color.a(c) }).inject{ |sum, el| sum+el }).to_f / ca.size
|
66
|
+
return ChunkyPNG::Color.rgba(r,g,b,a)
|
67
|
+
end
|
60
68
|
end
|
61
69
|
##
|
62
70
|
# Determines color distance from each other using the Pythagorean theorem.
|
63
|
-
def self.colorDistance(c1,c2)
|
64
|
-
|
71
|
+
def self.colorDistance(c1,c2,chunky = false)
|
72
|
+
if !chunky
|
73
|
+
return Math.sqrt((c1[0]-c2[0])**2+(c1[1]-c2[1])**2+(c1[2]-c2[2])**2+(c1[3]-c2[3])**2)
|
74
|
+
else
|
75
|
+
return Math.sqrt((ChunkyPNG::Color.r(c1)-ChunkyPNG::Color.r(c2))**2+(ChunkyPNG::Color.g(c1)-ChunkyPNG::Color.g(c2))**2+(ChunkyPNG::Color.b(c1)-ChunkyPNG::Color.b(c2))**2+(ChunkyPNG::Color.a(c1)-ChunkyPNG::Color.a(c2))**2)
|
76
|
+
end
|
65
77
|
end
|
66
78
|
##
|
67
79
|
# Uses a combination of color averaging and color distance to find how "unique" a color is.
|
68
|
-
def self.colorUniqueness(c, ca)
|
69
|
-
return Pxlsrt::Colors.colorDistance(c, Pxlsrt::Colors.colorAverage(ca))
|
80
|
+
def self.colorUniqueness(c, ca, chunky = false)
|
81
|
+
return Pxlsrt::Colors.colorDistance(c, Pxlsrt::Colors.colorAverage(ca, chunky), chunky)
|
70
82
|
end
|
71
83
|
##
|
72
84
|
# Sorts an array of colors based on a method.
|
@@ -92,42 +104,42 @@ module Pxlsrt
|
|
92
104
|
mhm=[]
|
93
105
|
case how.downcase
|
94
106
|
when "sum-rgb"
|
95
|
-
mhm= list.sort_by { |c| c
|
107
|
+
mhm= list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
96
108
|
when "sum-rgba"
|
97
|
-
mhm=list.sort_by { |c| c
|
109
|
+
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c)+ChunkyPNG::Color.a(c) }
|
98
110
|
when "red"
|
99
|
-
mhm= list.sort_by { |c| c
|
111
|
+
mhm= list.sort_by { |c| ChunkyPNG::Color.r(c) }
|
100
112
|
when "yellow"
|
101
|
-
mhm=list.sort_by { |c| c
|
113
|
+
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c) }
|
102
114
|
when "green"
|
103
|
-
mhm= list.sort_by { |c| c
|
115
|
+
mhm= list.sort_by { |c| ChunkyPNG::Color.g(c) }
|
104
116
|
when "cyan"
|
105
|
-
mhm=list.sort_by { |c| c
|
117
|
+
mhm=list.sort_by { |c| ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
106
118
|
when "blue"
|
107
|
-
mhm= list.sort_by { |c| c
|
119
|
+
mhm= list.sort_by { |c| ChunkyPNG::Color.b(c) }
|
108
120
|
when "magenta"
|
109
|
-
mhm=list.sort_by { |c| c
|
121
|
+
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.b(c) }
|
110
122
|
when "hue"
|
111
|
-
mhm= list.sort_by { |c| Pxlsrt::Colors.
|
123
|
+
mhm= list.sort_by { |c| d = Pxlsrt::Colors.getRGBA(c); Pxlsrt::Colors.rgb2hsb(d)[0] }
|
112
124
|
when "saturation"
|
113
|
-
mhm= list.sort_by { |c| Pxlsrt::Colors.
|
125
|
+
mhm= list.sort_by { |c| d = Pxlsrt::Colors.getRGBA(c); Pxlsrt::Colors.rgb2hsb(d)[1] }
|
114
126
|
when "brightness"
|
115
|
-
mhm= list.sort_by { |c| Pxlsrt::Colors.
|
127
|
+
mhm= list.sort_by { |c| d = Pxlsrt::Colors.getRGBA(c); Pxlsrt::Colors.rgb2hsb(d)[2] }
|
116
128
|
when "sum-hsb"
|
117
|
-
mhm= list.sort_by { |c| k=Pxlsrt::Colors.rgb2hsb(
|
129
|
+
mhm= list.sort_by { |c| d = Pxlsrt::Colors.getRGBA(c); k=Pxlsrt::Colors.rgb2hsb(d); k[0]*100.0/360+k[1]+k[2] }
|
118
130
|
when "sum-hsba"
|
119
|
-
mhm= list.sort_by { |c| k=Pxlsrt::Colors.rgb2hsb(
|
131
|
+
mhm= list.sort_by { |c| d = Pxlsrt::Colors.getRGBA(c); k=Pxlsrt::Colors.rgb2hsb(d); k[0]*100.0/360+k[1]+k[2]+d.a*100.0/255 }
|
120
132
|
when "uniqueness"
|
121
|
-
avg=Pxlsrt::Colors.colorAverage(list)
|
122
|
-
mhm=list.sort_by { |c| Pxlsrt::Colors.colorUniqueness(c, [avg]) }
|
133
|
+
avg=Pxlsrt::Colors.colorAverage(list, true)
|
134
|
+
mhm=list.sort_by { |c| Pxlsrt::Colors.colorUniqueness(c, [avg], true) }
|
123
135
|
when "luma"
|
124
|
-
mhm=list.sort_by { |c| c
|
136
|
+
mhm=list.sort_by { |c| ChunkyPNG::Color.r(c)*0.2126+ChunkyPNG::Color.g(c)*0.7152+ChunkyPNG::Color.b(c)*0.0722 }
|
125
137
|
when "random"
|
126
138
|
mhm=list.shuffle
|
127
139
|
when "alpha"
|
128
|
-
mhm=list.sort_by{ |c| c
|
140
|
+
mhm=list.sort_by{ |c| ChunkyPNG::Color.a(c) }
|
129
141
|
else
|
130
|
-
mhm= list.sort_by { |c| c
|
142
|
+
mhm= list.sort_by { |c| ChunkyPNG::Color.r(c)+ChunkyPNG::Color.g(c)+ChunkyPNG::Color.b(c) }
|
131
143
|
end
|
132
144
|
if reverse == 0
|
133
145
|
return mhm
|
data/lib/pxlsrt/helpers.rb
CHANGED
@@ -57,6 +57,30 @@ module Pxlsrt
|
|
57
57
|
return match
|
58
58
|
end
|
59
59
|
##
|
60
|
+
# Pixel sorting helper to eliminate repetition.
|
61
|
+
def self.handlePixelSort(band, o)
|
62
|
+
if o[:reverse].downcase == "reverse"
|
63
|
+
reverse = 1
|
64
|
+
elsif o[:reverse].downcase == "either"
|
65
|
+
reverse = -1
|
66
|
+
else
|
67
|
+
reverse = 0
|
68
|
+
end
|
69
|
+
if o[:smooth]
|
70
|
+
u = band.group_by { |x| x }
|
71
|
+
k = u.keys
|
72
|
+
else
|
73
|
+
k = band
|
74
|
+
end
|
75
|
+
sortedBand = Pxlsrt::Colors.pixelSort(
|
76
|
+
k,
|
77
|
+
o[:method],
|
78
|
+
reverse
|
79
|
+
)
|
80
|
+
sortedBand = sortedBand.map { |x| u[x] }.flatten(1) if o[:smooth]
|
81
|
+
return Pxlsrt::Lines.handleMiddlate(sortedBand, o[:middle])
|
82
|
+
end
|
83
|
+
##
|
60
84
|
# Prints an error message.
|
61
85
|
def self.error(what)
|
62
86
|
puts "#{Pxlsrt::Helpers.red("pxlsrt")} #{what}"
|
data/lib/pxlsrt/image.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
module Pxlsrt
|
2
|
+
##
|
3
|
+
# Image class for handling ChunkyPNG images.
|
4
|
+
class Image
|
5
|
+
def initialize(png)
|
6
|
+
@original = png
|
7
|
+
@width = png.width
|
8
|
+
@height = png.height
|
9
|
+
@modified = png
|
10
|
+
end
|
11
|
+
##
|
12
|
+
# Retrieve a multidimensional array consisting of the horizontal lines (row) of the image.
|
13
|
+
def horizontalLines
|
14
|
+
return (0...@height).inject([]) { | arr, row | arr << @modified.row(row) }
|
15
|
+
end
|
16
|
+
##
|
17
|
+
# Replace a horizontal line (row) of the image.
|
18
|
+
def replaceHorizontal(y, arr)
|
19
|
+
@modified.replace_row!(y, arr)
|
20
|
+
return @modified
|
21
|
+
end
|
22
|
+
##
|
23
|
+
# Retrieve the x and y coordinates of a pixel based on the multidimensional array created using the horizontalLines method.
|
24
|
+
def horizontalXY(horizontal, index)
|
25
|
+
return {
|
26
|
+
"x" => index.to_i,
|
27
|
+
"y" => horizontal.to_i
|
28
|
+
}
|
29
|
+
end
|
30
|
+
##
|
31
|
+
# Retrieve a multidimensional array consisting of the vertical lines of the image.
|
32
|
+
def verticalLines
|
33
|
+
return (0...@width).inject([]) { | arr, column | arr << @modified.column(column) }
|
34
|
+
end
|
35
|
+
##
|
36
|
+
# Replace a vertical line (column) of the image.
|
37
|
+
def replaceVertical(y, arr)
|
38
|
+
@modified.replace_column!(y, arr)
|
39
|
+
return @modified
|
40
|
+
end
|
41
|
+
##
|
42
|
+
# Retrieve the x and y coordinates of a pixel based on the multidimensional array created using the verticalLines method.
|
43
|
+
def verticalXY(vertical, index)
|
44
|
+
return {
|
45
|
+
"x" => vertical.to_i,
|
46
|
+
"y" => index.to_i
|
47
|
+
}
|
48
|
+
end
|
49
|
+
##
|
50
|
+
# Retrieve a hash consisting of the diagonal lines (top left to bottom right) of the image.
|
51
|
+
def diagonalLines
|
52
|
+
return Pxlsrt::Lines.getDiagonals(self.horizontalLines.flatten(1), @width, @height)
|
53
|
+
end
|
54
|
+
##
|
55
|
+
# Retrieve a hash consisting of the diagonal lines (bottom left to top right) of the image.
|
56
|
+
def rDiagonalLines
|
57
|
+
return Pxlsrt::Lines.getDiagonals(self.horizontalLines.reverse.flatten(1).reverse, @width, @height)
|
58
|
+
end
|
59
|
+
##
|
60
|
+
# Get the column and row based on the diagonal hash created using diagonalLines.
|
61
|
+
def diagonalColumnRow(d, i)
|
62
|
+
return {
|
63
|
+
"column" => ((d.to_i < 0) ? i : d.to_i + i).to_i,
|
64
|
+
"row" => ((d.to_i < 0) ? d.to_i.abs + i : i).to_i
|
65
|
+
}
|
66
|
+
end
|
67
|
+
##
|
68
|
+
# Replace a diagonal line (top left to bottom right) of the image.
|
69
|
+
def replaceDiagonal(d, arr)
|
70
|
+
d = d.to_i
|
71
|
+
for i in 0...arr.length
|
72
|
+
xy = self.diagonalXY(d, i)
|
73
|
+
self[xy["x"], xy["y"]] = arr[i]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
##
|
77
|
+
# Replace a diagonal line (bottom left to top right) of the image.
|
78
|
+
def replaceRDiagonal(d, arr)
|
79
|
+
d = d.to_i
|
80
|
+
for i in 0...arr.length
|
81
|
+
xy = self.rDiagonalXY(d, i)
|
82
|
+
self[xy["x"], xy["y"]] = arr[i]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
##
|
86
|
+
# Retrieve the x and y coordinates of a pixel based on the hash created using the diagonalLines method and the column and row of the diagonalColumnRow method.
|
87
|
+
def diagonalXY(d, i)
|
88
|
+
cr = self.diagonalColumnRow(d, i)
|
89
|
+
return {
|
90
|
+
"x" => cr["column"],
|
91
|
+
"y" => cr["row"]
|
92
|
+
}
|
93
|
+
end
|
94
|
+
##
|
95
|
+
# Retrieve the x and y coordinates of a pixel based on the hash created using the rDiagonalLines method and the column and row of the diagonalColumnRow method.
|
96
|
+
def rDiagonalXY(d, i)
|
97
|
+
cr = self.diagonalColumnRow(d, i)
|
98
|
+
return {
|
99
|
+
"x" => @width - 1 - cr["column"],
|
100
|
+
"y" => cr["row"]
|
101
|
+
}
|
102
|
+
end
|
103
|
+
##
|
104
|
+
# Retrieve Sobel value for a given pixel.
|
105
|
+
def getSobel(x, y)
|
106
|
+
if !defined?(@sobels)
|
107
|
+
@grey ||= @original.grayscale
|
108
|
+
@sobel_x ||= [[-1,0,1], [-2,0,2], [-1,0,1]]
|
109
|
+
@sobel_y ||= [[-1,-2,-1], [ 0, 0, 0], [ 1, 2, 1]]
|
110
|
+
if x != 0 and x != (@width-1) and y != 0 and y != (@height-1)
|
111
|
+
t1=ChunkyPNG::Color.r(@grey[x-1,y-1])
|
112
|
+
t2=ChunkyPNG::Color.r(@grey[x,y-1])
|
113
|
+
t3=ChunkyPNG::Color.r(@grey[x+1,y-1])
|
114
|
+
t4=ChunkyPNG::Color.r(@grey[x-1,y])
|
115
|
+
t5=ChunkyPNG::Color.r(@grey[x,y])
|
116
|
+
t6=ChunkyPNG::Color.r(@grey[x+1,y])
|
117
|
+
t7=ChunkyPNG::Color.r(@grey[x-1,y+1])
|
118
|
+
t8=ChunkyPNG::Color.r(@grey[x,y+1])
|
119
|
+
t9=ChunkyPNG::Color.r(@grey[x+1,y+1])
|
120
|
+
pixel_x=(@sobel_x[0][0]*t1)+(@sobel_x[0][1]*t2)+(@sobel_x[0][2]*t3)+(@sobel_x[1][0]*t4)+(@sobel_x[1][1]*t5)+(@sobel_x[1][2]*t6)+(@sobel_x[2][0]*t7)+(@sobel_x[2][1]*t8)+(@sobel_x[2][2]*t9)
|
121
|
+
pixel_y=(@sobel_y[0][0]*t1)+(@sobel_y[0][1]*t2)+(@sobel_y[0][2]*t3)+(@sobel_y[1][0]*t4)+(@sobel_y[1][1]*t5)+(@sobel_y[1][2]*t6)+(@sobel_y[2][0]*t7)+(@sobel_y[2][1]*t8)+(@sobel_y[2][2]*t9)
|
122
|
+
return Math.sqrt(pixel_x * pixel_x + pixel_y * pixel_y).ceil
|
123
|
+
else
|
124
|
+
return 0
|
125
|
+
end
|
126
|
+
else
|
127
|
+
return @sobels[y * @width + x]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
##
|
131
|
+
# Retrieve the Sobel values for every pixel and set it as @sobel.
|
132
|
+
def getSobels
|
133
|
+
if !defined?(@sobels)
|
134
|
+
l = []
|
135
|
+
for xy in 0...(@width * @height)
|
136
|
+
s = self.getSobel(xy % @width, (xy/@width).floor)
|
137
|
+
l.push(s)
|
138
|
+
end
|
139
|
+
@sobels = l
|
140
|
+
end
|
141
|
+
return @sobels
|
142
|
+
end
|
143
|
+
##
|
144
|
+
# Retrieve the Sobel value and color of a pixel.
|
145
|
+
def getSobelAndColor(x, y)
|
146
|
+
return {
|
147
|
+
"sobel" => self.getSobel(x, y),
|
148
|
+
"color" => self[x, y]
|
149
|
+
}
|
150
|
+
end
|
151
|
+
##
|
152
|
+
# Retrieve the color of a pixel.
|
153
|
+
def [](x, y)
|
154
|
+
return @modified[x, y]
|
155
|
+
end
|
156
|
+
##
|
157
|
+
# Set the color of a pixel.
|
158
|
+
def []=(x, y, color)
|
159
|
+
@modified[x, y] = color
|
160
|
+
end
|
161
|
+
##
|
162
|
+
# Return the original, unmodified image.
|
163
|
+
def returnOriginal
|
164
|
+
return @original
|
165
|
+
end
|
166
|
+
##
|
167
|
+
# Return the modified image.
|
168
|
+
def returnModified
|
169
|
+
return @modified
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/pxlsrt/lines.rb
CHANGED
@@ -87,8 +87,8 @@ module Pxlsrt
|
|
87
87
|
# Outputs random slices of an array.
|
88
88
|
# Because of the requirements of pxlsrt, it doesn't actually slice the array, but returns a range-like array. Example:
|
89
89
|
# [[0, 5], [6, 7], [8, 10]]
|
90
|
-
def self.randomSlices(
|
91
|
-
len=
|
90
|
+
def self.randomSlices(mainLength, minLength, maxLength)
|
91
|
+
len=mainLength-1
|
92
92
|
if len!=0
|
93
93
|
min=[minLength, maxLength].min
|
94
94
|
max=[maxLength, minLength].max
|
data/lib/pxlsrt/smart.rb
CHANGED
@@ -28,7 +28,6 @@ module Pxlsrt
|
|
28
28
|
:verbose => false,
|
29
29
|
:absolute => false,
|
30
30
|
:threshold => 20,
|
31
|
-
:sorted => 2,
|
32
31
|
:trusted => false,
|
33
32
|
:middle => false
|
34
33
|
}
|
@@ -41,7 +40,6 @@ module Pxlsrt
|
|
41
40
|
:verbose => [false, true],
|
42
41
|
:absolute => [false, true],
|
43
42
|
:threshold => [{:class => [Float, Fixnum]}],
|
44
|
-
:sorted => [{:class => [Fixnum]}],
|
45
43
|
:trusted => [false, true],
|
46
44
|
:middle => :anything
|
47
45
|
}
|
@@ -66,166 +64,72 @@ module Pxlsrt
|
|
66
64
|
return
|
67
65
|
end
|
68
66
|
Pxlsrt::Helpers.verbose("Smart mode.") if options[:verbose]
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
valued="start"
|
83
|
-
k=[]
|
84
|
-
Pxlsrt::Helpers.verbose("Getting Sobel values and colors for pixels...") if options[:verbose]
|
85
|
-
grey=img.grayscale
|
86
|
-
for xy in 0..(w*h-1)
|
87
|
-
x=xy % w
|
88
|
-
y=(xy/w).floor
|
89
|
-
if x!=0 and x!=(w-1) and y!=0 and y!=(h-1)
|
90
|
-
t1=ChunkyPNG::Color.r(grey[x-1,y-1])
|
91
|
-
t2=ChunkyPNG::Color.r(grey[x,y-1])
|
92
|
-
t3=ChunkyPNG::Color.r(grey[x+1,y-1])
|
93
|
-
t4=ChunkyPNG::Color.r(grey[x-1,y])
|
94
|
-
t5=ChunkyPNG::Color.r(grey[x,y])
|
95
|
-
t6=ChunkyPNG::Color.r(grey[x+1,y])
|
96
|
-
t7=ChunkyPNG::Color.r(grey[x-1,y+1])
|
97
|
-
t8=ChunkyPNG::Color.r(grey[x,y+1])
|
98
|
-
t9=ChunkyPNG::Color.r(grey[x+1,y+1])
|
99
|
-
pixel_x=(sobel_x[0][0]*t1)+(sobel_x[0][1]*t2)+(sobel_x[0][2]*t3)+(sobel_x[1][0]*t4)+(sobel_x[1][1]*t5)+(sobel_x[1][2]*t6)+(sobel_x[2][0]*t7)+(sobel_x[2][1]*t8)+(sobel_x[2][2]*t9)
|
100
|
-
pixel_y=(sobel_y[0][0]*t1)+(sobel_y[0][1]*t2)+(sobel_y[0][2]*t3)+(sobel_y[1][0]*t4)+(sobel_y[1][1]*t5)+(sobel_y[1][2]*t6)+(sobel_y[2][0]*t7)+(sobel_y[2][1]*t8)+(sobel_y[2][2]*t9)
|
101
|
-
val = Math.sqrt(pixel_x * pixel_x + pixel_y * pixel_y).ceil
|
102
|
-
else
|
103
|
-
val = 2000000000
|
104
|
-
end
|
105
|
-
k.push({ "sobel" => val, "pixel" => [x, y], "color" => Pxlsrt::Colors.getRGBA(img[x, y]) })
|
106
|
-
end
|
107
|
-
if options[:vertical]==true
|
108
|
-
Pxlsrt::Helpers.verbose("Rotating image for vertical mode...") if options[:verbose]
|
109
|
-
k=Pxlsrt::Lines.rotateImage(k,w,h,3)
|
110
|
-
w,h=h,w
|
67
|
+
png=Pxlsrt::Image.new(input)
|
68
|
+
if !options[:vertical] and !options[:diagonal]
|
69
|
+
Pxlsrt::Helpers.verbose("Retrieving rows") if options[:verbose]
|
70
|
+
lines = png.horizontalLines
|
71
|
+
elsif options[:vertical] and !options[:diagonal]
|
72
|
+
Pxlsrt::Helpers.verbose("Retrieving columns") if options[:verbose]
|
73
|
+
lines = png.verticalLines
|
74
|
+
elsif !options[:vertical] and options[:diagonal]
|
75
|
+
Pxlsrt::Helpers.verbose("Retrieving diagonals") if options[:verbose]
|
76
|
+
lines = png.diagonalLines
|
77
|
+
elsif options[:vertical] and options[:diagonal]
|
78
|
+
Pxlsrt::Helpers.verbose("Retrieving diagonals") if options[:verbose]
|
79
|
+
lines = png.rDiagonalLines
|
111
80
|
end
|
81
|
+
Pxlsrt::Helpers.verbose("Retrieving edges") if options[:verbose]
|
82
|
+
png.getSobels
|
112
83
|
if !options[:diagonal]
|
113
|
-
lines
|
114
|
-
Pxlsrt::Helpers.verbose("Determining bands with a#{options[:absolute] ? "n absolute" : " relative"} threshold of #{options[:threshold]}...") if options[:verbose]
|
115
|
-
bands=Array.new()
|
116
|
-
for j in lines
|
117
|
-
slicing=true
|
118
|
-
pixel=0
|
119
|
-
m=Array.new()
|
120
|
-
while slicing do
|
121
|
-
n=Array.new
|
122
|
-
if m.length > 1
|
123
|
-
while m.last.length < options[:edge]
|
124
|
-
if m.length > 1
|
125
|
-
m[-2].concat(m[-1])
|
126
|
-
m.pop
|
127
|
-
else
|
128
|
-
break
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
bandWorking=true
|
133
|
-
while bandWorking do
|
134
|
-
n.push(j[pixel]["color"])
|
135
|
-
if (options[:absolute] ? (j[pixel+1]["sobel"]) : (j[pixel+1]["sobel"]-j[pixel]["sobel"])) > options[:threshold]
|
136
|
-
bandWorking=false
|
137
|
-
end
|
138
|
-
if (pixel+1)==(j.length-1)
|
139
|
-
n.push(j[pixel+1]["color"])
|
140
|
-
slicing=false
|
141
|
-
bandWorking=false
|
142
|
-
end
|
143
|
-
pixel+=1
|
144
|
-
end
|
145
|
-
m.push(n)
|
146
|
-
end
|
147
|
-
bands.concat(m)
|
148
|
-
end
|
149
|
-
Pxlsrt::Helpers.verbose("Pixel sorting using method '#{options[:method]}'...") if options[:verbose]
|
150
|
-
image=[]
|
151
|
-
if options[:smooth]
|
152
|
-
for band in bands
|
153
|
-
u=band.group_by {|x| x}
|
154
|
-
image.concat(Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(u.keys, options[:method], nre).map { |x| u[x] }.flatten(1), options[:middle]))
|
155
|
-
end
|
156
|
-
else
|
157
|
-
for band in bands
|
158
|
-
image.concat(Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(band, options[:method], nre), options[:middle]))
|
159
|
-
end
|
160
|
-
end
|
84
|
+
iterator = 0...(lines.length)
|
161
85
|
else
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
break
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
bandWorking=true
|
184
|
-
while bandWorking do
|
185
|
-
n.push(dia[j][pixel]["color"])
|
186
|
-
if (options[:absolute] ? (dia[j][pixel+1]["sobel"]) : (dia[j][pixel+1]["sobel"]-dia[j][pixel]["sobel"])) > options[:threshold]
|
187
|
-
bandWorking=false
|
188
|
-
end
|
189
|
-
if (pixel+1)==(dia[j].length-1)
|
190
|
-
n.push(dia[j][pixel+1]["color"])
|
191
|
-
slicing=false
|
192
|
-
bandWorking=false
|
193
|
-
end
|
194
|
-
pixel+=1
|
195
|
-
end
|
196
|
-
m.push(n)
|
86
|
+
iterator = lines.keys
|
87
|
+
end
|
88
|
+
Pxlsrt::Helpers.verbose("Dividing and pixel sorting lines") if options[:verbose]
|
89
|
+
for k in iterator
|
90
|
+
line = lines[k]
|
91
|
+
divisions = []
|
92
|
+
division = []
|
93
|
+
if line.length > 1
|
94
|
+
for pixel in 0...(line.length)
|
95
|
+
if !options[:vertical] and !options[:diagonal]
|
96
|
+
xy = png.horizontalXY(k, pixel)
|
97
|
+
elsif options[:vertical] and !options[:diagonal]
|
98
|
+
xy = png.verticalXY(k, pixel)
|
99
|
+
elsif !options[:vertical] and options[:diagonal]
|
100
|
+
xy = png.diagonalXY(k, pixel)
|
101
|
+
elsif options[:vertical] and options[:diagonal]
|
102
|
+
xy = png.rDiagonalXY(k, pixel)
|
197
103
|
end
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
for j in dia.keys
|
205
|
-
ell=[]
|
206
|
-
if options[:smooth]
|
207
|
-
for band in dia[j]
|
208
|
-
u=band.group_by {|x| x}
|
209
|
-
ell.concat(Pxlsrt::Lines.handleMiddlate(Pxlsrt::Colors.pixelSort(u.keys, options[:method], nre).map { |x| u[x] }.flatten(1), options[:middle]))
|
104
|
+
pxlSobel = png.getSobelAndColor(xy["x"], xy["y"])
|
105
|
+
if division.length == 0 or (options[:absolute] ? pxlSobel["sobel"] : pxlSobel["sobel"] - division.last["sobel"]) <= options[:threshold]
|
106
|
+
division.push(pxlSobel)
|
107
|
+
else
|
108
|
+
divisions.push(division)
|
109
|
+
division = [pxlSobel]
|
210
110
|
end
|
211
|
-
|
212
|
-
|
213
|
-
|
111
|
+
if pixel == line.length - 1
|
112
|
+
divisions.push(division)
|
113
|
+
division = []
|
214
114
|
end
|
215
115
|
end
|
216
|
-
dia[j]=ell
|
217
116
|
end
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
117
|
+
newLine = []
|
118
|
+
for band in divisions
|
119
|
+
newLine.concat(
|
120
|
+
Pxlsrt::Helpers.handlePixelSort(
|
121
|
+
band.map { |sobelAndColor| sobelAndColor["color"] },
|
122
|
+
options
|
123
|
+
)
|
124
|
+
)
|
125
|
+
end
|
126
|
+
if !options[:diagonal]
|
127
|
+
png.replaceHorizontal(k, newLine) if !options[:vertical]
|
128
|
+
png.replaceVertical(k, newLine) if options[:vertical]
|
129
|
+
else
|
130
|
+
png.replaceDiagonal(k, newLine) if !options[:vertical]
|
131
|
+
png.replaceRDiagonal(k, newLine) if options[:vertical]
|
132
|
+
end
|
229
133
|
end
|
230
134
|
endTime=Time.now
|
231
135
|
timeElapsed=endTime-startTime
|
@@ -237,7 +141,7 @@ module Pxlsrt
|
|
237
141
|
Pxlsrt::Helpers.verbose("Took #{minutes} minute#{ minutes!=1 ? "s" : "" } and #{seconds} second#{ seconds!=1.0 ? "s" : "" }.") if options[:verbose]
|
238
142
|
end
|
239
143
|
Pxlsrt::Helpers.verbose("Returning ChunkyPNG::Image...") if options[:verbose]
|
240
|
-
return
|
144
|
+
return png.returnModified
|
241
145
|
else
|
242
146
|
Pxlsrt::Helpers.error("Options specified do not follow the correct format.") if options[:verbose]
|
243
147
|
return
|
data/lib/pxlsrt/version.rb
CHANGED
data/lib/pxlsrt.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require "pxlsrt/version"
|
2
|
-
require "pxlsrt/helpers"
|
3
|
-
require "pxlsrt/lines"
|
4
|
-
require "pxlsrt/
|
5
|
-
require "pxlsrt/
|
1
|
+
require "pxlsrt/version"
|
2
|
+
require "pxlsrt/helpers"
|
3
|
+
require "pxlsrt/lines"
|
4
|
+
require "pxlsrt/image"
|
5
|
+
require "pxlsrt/colors"
|
6
|
+
require "pxlsrt/brute"
|
6
7
|
require "pxlsrt/smart"
|
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.6'
|
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-08-
|
11
|
+
date: 2014-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- lib/pxlsrt/brute.rb
|
80
80
|
- lib/pxlsrt/colors.rb
|
81
81
|
- lib/pxlsrt/helpers.rb
|
82
|
+
- lib/pxlsrt/image.rb
|
82
83
|
- lib/pxlsrt/lines.rb
|
83
84
|
- lib/pxlsrt/smart.rb
|
84
85
|
- lib/pxlsrt/version.rb
|