Skip to content

Commit 042051f

Browse files
author
guoyongzhi
committed
randomize findroom and add teleport early stop
1 parent 36203b8 commit 042051f

File tree

8 files changed

+95
-53
lines changed

8 files changed

+95
-53
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.ipynb_checkpoints
22
.vscode
3-
res/*/*
3+
res/*/*
4+
address_compare
5+
guxiang_animation

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "WordCloud"
22
uuid = "6385f0a0-cb03-45b6-9089-4e0acc74b26b"
33
authors = ["guoyongzhi <momoshanghan@163.com>"]
4-
version = "0.2.5"
4+
version = "0.2.6"
55

66
[deps]
77
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"

examples/compare.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using WordCloud
22

33
stwords = ["us", "will"];
4-
println("Obama's")
4+
println("==Obama's==")
55
cs = WordCloud.randomscheme()
66
as = WordCloud.randomangles()
77
fr = 0.6 #not too high
@@ -10,7 +10,7 @@ wca = wordcloud(
1010
colors = cs,
1111
angles = as,
1212
filling_rate = fr) |> generate!
13-
println("Trump's")
13+
println("==Trump's==")
1414
tb, wb = process(open("res/Donald Trump's Inaugural Address.txt"), stopwords=WordCloud.stopwords_en stwords)
1515
samemask = tb .∈ Ref(wca.words)
1616
println(sum(samemask), " same words")
@@ -37,21 +37,29 @@ for i in 1:length(tb)
3737
WordCloud.QTree.setcenter!(wcb.qtrees[i], cxy)
3838
end
3939
end
40-
println("ignore defferent words")
40+
println("=ignore defferent words=")
4141
ignore(wcb, .!samemask) do
4242
generate!(wcb, 1000, patient=-1, retry=1) #patient=-1 means no teleport; retry=1 means no rescale
4343
end
44-
println("pin same words")
44+
println("=pin same words=")
4545
pin(wcb, samemask) do
4646
placement!(wcb)
4747
generate!(wcb, 1000, retry=1) #allow teleport but don‘t allow rescale
4848
end
49-
println("overall tuning")
50-
generate!(wcb, 1000, patient=-1, retry=2) #allow rescale but don‘t allow teleport
49+
if !iscompleted(wcb)
50+
println("=overall tuning=")
51+
generate!(wcb, 1000, patient=-1, retry=2) #allow rescale but don‘t allow teleport
52+
end
5153

5254
ma = paint(wca)
5355
mb = paint(wcb)
5456
h,w = size(ma)
5557
space = loadmask(shape(box, w÷20, h))
5658
space .= WordCloud.ARGB(0,0,0,0)
57-
WordCloud.ImageMagick.save("compare.png", [ma space mb])
59+
try `mkdir address_compare`|>run catch end
60+
WordCloud.ImageMagick.save("address_compare/compare.png", [ma space mb])
61+
62+
gif = WordCloud.GIF("address_compare")
63+
record(wca, "Obama", gif)
64+
record(wcb, "Trump", gif)
65+
WordCloud.Render.generate(gif, framerate=0.5)

src/WordCloud.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module WordCloud
22
export wordcloud, shape, ellipse, box, paint, loadmask, process,
33
train!, Momentum, generate!, generate_animation!, getposition
4-
export record, parsecolor, placement!, imageof, bitor, take, ignore, pin
4+
export record, parsecolor, placement!, imageof, bitor, take, ignore, pin, iscompleted
55
include("qtree.jl")
66
include("rendering.jl")
77
include("nlp.jl")

src/interface.jl

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ end
2727

2828
import ImageTransformations.imresize
2929
"""
30-
loadmask("res/heart.jpg")
31-
loadmask("res/heart.jpg", 256, 256) #resize to 256*256
32-
loadmask("res/heart.jpg", ratio=0.3) #scale 0.3
33-
loadmask("res/heart.jpg", color="red", ratio=2) #set forecolor color
34-
loadmask("res/heart.jpg", color="red", transparentcolor=(1,1,1)) #set forecolor color with transparentcolor
30+
## examples
31+
* loadmask("res/heart.jpg")
32+
* loadmask("res/heart.jpg", 256, 256) #resize to 256*256
33+
* loadmask("res/heart.jpg", ratio=0.3) #scale 0.3
34+
* loadmask("res/heart.jpg", color="red", ratio=2) #set forecolor color
35+
* loadmask("res/heart.jpg", color="red", transparentcolor=(1,1,1)) #set forecolor color with transparentcolor
3536
"""
3637
function loadmask(img::AbstractMatrix, args...; color=:original, backgroundcolor=:original, transparentcolor=:auto, kargs...)
3738
if color!=:original || backgroundcolor!=:original
@@ -70,22 +71,22 @@ mutable struct wordcloud
7071
end
7172

7273
"""
73-
## kargs example
74+
## kargs examples
7475
### style kargs
75-
colors = "black" #all same color
76-
colors = ("black", (0.5,0.5,0.7), "yellow", "#ff0000", 0.2) #choose entries randomly
77-
colors = ["black", (0.5,0.5,0.7), "yellow", "red", (0.5,0.5,0.7), 0.2, ......] #use entries sequentially in cycle
78-
angles = 0 #all same angle
79-
angles = (0, 90, 45) #choose entries randomly
80-
angles = 0:180 #choose entries randomly
81-
angles = [0, 22, 4, 1, 100, 10, ......] #use entries sequentially in cycle
82-
filling_rate = 0.5
83-
border = 1
76+
* colors = "black" #all same color
77+
* colors = ("black", (0.5,0.5,0.7), "yellow", "#ff0000", 0.2) #choose entries randomly
78+
* colors = ["black", (0.5,0.5,0.7), "yellow", "red", (0.5,0.5,0.7), 0.2, ......] #use entries sequentially in cycle
79+
* angles = 0 #all same angle
80+
* angles = (0, 90, 45) #choose entries randomly
81+
* angles = 0:180 #choose entries randomly
82+
* angles = [0, 22, 4, 1, 100, 10, ......] #use entries sequentially in cycle
83+
* filling_rate = 0.5
84+
* border = 1
8485
### mask kargs
85-
mask = loadmask("res/heart.jpg", 256, 256) #see doc of `loadmask`
86-
mask = loadmask("res/heart.jpg", color="red", ratio=2) #see doc of `loadmask`
87-
mask = shape(ellipse, 800, 600, color="white", bgcolor=(0,0,0,0)) #see doc of `shape`
88-
transparentcolor = ARGB32(0,0,0,0) #set the transparent color in mask
86+
* mask = loadmask("res/heart.jpg", 256, 256) #see doc of `loadmask`
87+
* mask = loadmask("res/heart.jpg", color="red", ratio=2) #see doc of `loadmask`
88+
* mask = shape(ellipse, 800, 600, color="white", bgcolor=(0,0,0,0)) #see doc of `shape`
89+
* transparentcolor = ARGB32(0,0,0,0) #set the transparent color in mask
8990
"""
9091
wordcloud(wordsweights::Tuple; kargs...) = wordcloud(wordsweights...; kargs...)
9192
wordcloud(counter::AbstractDict; kargs...) = wordcloud(keys(counter)|>collect, values(counter)|>collect; kargs...)
@@ -157,11 +158,14 @@ function wordcloud(words::AbstractVector{<:AbstractString}, weights::AbstractVec
157158
params[:border] = border
158159
params[:font] = font
159160
params[:minfontsize] = minfontsize
161+
params[:completed] = false
162+
params[:epoch] = 0
160163
placement!(deepcopy(maskqtree), qtrees)
161164
wordcloud(words, weights, imgs, mask, qtrees, maskqtree, params)
162165
end
163166
Base.getindex(wc::wordcloud, inds...) = wc.words[inds...]=>wc.weights[inds...]
164167
Base.lastindex(wc::wordcloud) = lastindex(wc.words)
168+
iscompleted(wc::wordcloud) = wc.params[:completed]
165169
function getposition(wc)
166170
msy, msx = getshift(wc.maskqtree)
167171
pos = getshift.(wc.qtrees)
@@ -184,10 +188,10 @@ function paint(wc::wordcloud, file, args...; kargs...)
184188
img
185189
end
186190

187-
function record(wc::wordcloud, ep::Number, gif_callback=x->x)
191+
function record(wc::wordcloud, label::AbstractString, gif_callback=x->x)
188192
# @show size(n1)
189193
resultpic = overlay!(paint(wc),
190-
rendertextoutlines(string(ep), 32, color="black", linecolor="white", linewidth=1), 20, 20)
194+
rendertextoutlines(label, 32, color="black", linecolor="white", linewidth=1), 20, 20)
191195
gif_callback(resultpic)
192196
end
193197

@@ -203,12 +207,16 @@ function generate!(wc::wordcloud, nepoch::Number=100, args...; retry=3,
203207
println("#$r. scale = $(wc.params[:scale])")
204208
ep, nc = train_with_teleport!(wc.qtrees, wc.maskqtree, nepoch, args...;
205209
trainer=trainer, optimiser=optimiser, patient=patient, krags...)
210+
wc.params[:epoch] += ep
206211
if nc == 0
207212
break
208213
end
209214
end
210215
@show ep, nc
211-
if nc != 0
216+
if nc == 0
217+
wc.params[:completed] = true
218+
else
219+
wc.params[:completed] = false
212220
colllist = first.(listcollision(wc.qtrees, wc.maskqtree))
213221
get_text(i) = i>0 ? wc.words[i] : "#MASK#"
214222
collwords = [(get_text(i), get_text(j)) for (i,j) in colllist]
@@ -227,8 +235,8 @@ function generate_animation!(wc::wordcloud, args...; outputdir="gifresult", over
227235
end
228236
try `mkdir $(outputdir)`|>run catch end
229237
gif = GIF(outputdir)
230-
record(wc, 0, gif)
231-
re = generate!(wc, args...; callbackstep=callbackstep, callbackfun=ep->record(wc, ep, gif), kargs...)
238+
record(wc, "0", gif)
239+
re = generate!(wc, args...; callbackstep=callbackstep, callbackfun=ep->record(wc, string(ep), gif), kargs...)
232240
Render.generate(gif)
233241
re
234242
end

src/qtreetools.jl

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,20 @@ function findroom(ground, q=[(levelnum(ground), 1, 1)])
137137
end
138138
while !isempty(q)
139139
i = popfirst!(q)
140-
for cn in shuffle4()
141-
ci = child(i, cn)
142-
if ground[ci] == EMPTY
143-
return ci
144-
elseif ground[ci] == HALF
145-
push!(q, ci)
140+
if i[1] == 1
141+
if ground[i] == EMPTY return i end
142+
else
143+
for cn in shuffle4()
144+
ci = child(i, cn)
145+
if ground[ci] == EMPTY
146+
if rand() < 0.7 #避免每次都是局部最优
147+
return ci
148+
else
149+
push!(q, ci)
150+
end
151+
elseif ground[ci] == HALF
152+
push!(q, ci)
153+
end
146154
end
147155
end
148156
end
@@ -200,16 +208,17 @@ end
200208

201209
"将sortedtrees依次叠加到ground上,同时修改sortedtrees的shift"
202210
function placement!(ground, sortedtrees)
203-
pos = Vector{Tuple{Int, Int, Int}}()
211+
# pos = Vector{Tuple{Int, Int, Int}}()
204212
for t in sortedtrees
205213
ind = placement!(ground, t)
206214
overlap!(ground, t)
207215
if ind === nothing
208216
return pos
209217
end
210-
push!(pos, ind)
218+
# push!(pos, ind)
211219
end
212-
return pos
220+
nothing
221+
# return pos
213222
end
214223

215224
function placement!(ground, qtree::ShiftedQtree)
@@ -233,8 +242,8 @@ function placement!(ground, sortedtrees, index::Number)
233242
end
234243
overlap!(ground, sortedtrees[i])
235244
end
236-
ind = placement!(ground, sortedtrees[index])
237-
return ind
245+
placement!(ground, sortedtrees[index])
246+
# return ind
238247
end
239248
function placement!(ground, sortedtrees, indexes)
240249
for i in 1:length(sortedtrees)

src/rendering.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,10 @@ schemes = [schemes_colorbrewer..., schemes_seaborn...]
142142

143143
"""
144144
get box or ellipse image
145-
shape(box, 80, 50) #80*50 box
146-
shape(box, 80, 50, 4) #box with cornerradius=4
147-
shape(ellipse, 80, 50, color="red") #80*50 red ellipse
145+
## Examples
146+
* shape(box, 80, 50) #80*50 box
147+
* shape(box, 80, 50, 4) #box with cornerradius=4
148+
* shape(ellipse, 80, 50, color="red") #80*50 red ellipse
148149
"""
149150
function shape(shape_, width, height, args...; color="white", bgcolor=(0,0,0,0))
150151
Drawing(width, height, :image)
@@ -163,11 +164,11 @@ function gif_callback_factory()
163164
counter = Iterators.Stateful(0:typemax(Int))
164165
pic->save(gifdirectory*@sprintf("/%010d.png", popfirst!(counter)), pic)
165166
end
166-
function try_gif_gen(gifdirectory)
167+
function try_gif_gen(gifdirectory; framerate=4)
167168
try
168169
pipeline(`ffmpeg -f image2 -i $(gifdirectory)/%010d.png -vf
169170
palettegen -y $(gifdirectory)/result-palette.png`, stdout=devnull, stderr=devnull) |> run
170-
pipeline(`ffmpeg -framerate 4 -f image2 -i $(gifdirectory)/%010d.png
171+
pipeline(`ffmpeg -framerate $(framerate) -f image2 -i $(gifdirectory)/%010d.png
171172
-i $(gifdirectory)/result-palette.png -lavfi paletteuse -y $(gifdirectory)/result.gif`,
172173
stdout=devnull, stderr=devnull) |> run
173174
catch e
@@ -183,5 +184,5 @@ function GIF(directory)
183184
end
184185
Base.push!(gif::GIF, img) = ImageMagick.save(gif.directory*@sprintf("/%010d.png", popfirst!(gif.counter)), img)
185186
(gif::GIF)(img) = Base.push!(gif, img)
186-
generate(gif::GIF) = try_gif_gen(gif.directory)
187+
generate(gif::GIF, args...; kargs...) = try_gif_gen(gif.directory, args...; kargs...)
187188
end

src/train.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ function train_with_teleport!(ts, maskqt, nepoch::Number, args...;
331331
nc = 0
332332
count = 0
333333
nc_min = Inf
334+
teleport_count = 0
335+
last_cinds = nothing
334336
while ep < nepoch
335337
# @show "##", ep, nc, length(collpool), (count,nc_min)
336338
nc = trainer(ts, maskqt, args...; collpool=collpool, queue=queue, kargs...)
@@ -343,15 +345,27 @@ function train_with_teleport!(ts, maskqt, nepoch::Number, args...;
343345
if nc != 0 && length(collpool)>0 && patient >0 && (count >= patient || count > length(collpool)) #超出耐心或少数几个碰撞
344346
nc_min = nc
345347
cinds = teleport!(ts, maskqt, collpool=collpool)
346-
println("@epoch $ep, count $count collision $nc($(length(collpool))) teleport $cinds")
348+
println("@epoch $ep, count $count collision $nc($(length(collpool))) teleport $cinds to $(getshift.(ts[cinds]))")
347349
count = 0
350+
cinds_set = Set(cinds)
351+
if last_cinds == cinds_set
352+
teleport_count += 1
353+
else
354+
teleport_count = 0
355+
end
356+
last_cinds = cinds_set
357+
348358
end
349359
if callbackstep>0 && ep%callbackstep==0
350360
callbackfun(ep)
351361
end
352362
if nc == 0
353363
return ep, nc
354364
end
365+
if patient > 0 && teleport_count >= patient
366+
println("The teleport strategy failed after $ep epochs")
367+
return ep, nc
368+
end
355369
end
356370
ep, nc
357-
end
371+
end

0 commit comments

Comments
 (0)