Skip to content

Commit

Permalink
new fellingoccupied
Browse files Browse the repository at this point in the history
  • Loading branch information
guoyongzhi committed Aug 21, 2020
1 parent 8f1078d commit 8fdafba
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 34 deletions.
4 changes: 2 additions & 2 deletions src/WordCloud.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
module WordCloud
export wordcloud, shape, ellipse, box, paint, loadmaskimg
export record, parsecolor, train!, Momentum, placement!, generate, generate_animation,imageof
export record, parsecolor, train!, Momentum, placement!, generate, generate_animation,imageof,bitor拉人个人
include("qtree.jl")
include("rendering.jl")
include("strategy.jl")
using .Render
using .QTree
include("train.jl")
include("interface.jl")
include("strategy.jl")
include("utils.jl")

end
52 changes: 41 additions & 11 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ end
## kargs example
### style kargs
colors = "black" #all same color
colors = ("black", (0.5,0.5,0.7), "yellow", "#ff0000") #choose randomly
colors = ["black", (0.5,0.5,0.7), "yellow", "red", (0.5,0.5,0.7), ......] #use sequentially in cycle
colors = ("black", (0.5,0.5,0.7), "yellow", "#ff0000", 0.2) #choose randomly
colors = ["black", (0.5,0.5,0.7), "yellow", "red", (0.5,0.5,0.7), 0.2, ......] #use sequentially in cycle
angles = 0 #all same angle
angles = (0, 90, 45) #choose randomly
angles = 0:180 #choose randomly
Expand Down Expand Up @@ -106,25 +106,28 @@ function wordcloud(texts::AbstractVector{<:AbstractString}, weights::AbstractVec
maskcolor = "black"
end
maskimg = randommask(maskcolor)
transparentcolor = get(params, :transparentcolor, ARGB(1, 1, 1, 0))
transparentcolor = get(params, :transparentcolor, ARGB(1, 1, 1, 0)) |> parsecolor
else
maskimg = params[:maskimg]
end
transparentcolor = get(params, :transparentcolor, maskimg[1])
transparentcolor = get(params, :transparentcolor, maskimg[1]) |> parsecolor
maskimg, maskqtree, groundsize, groundoccupied = preparebackground(maskimg, transparentcolor)
params[:maskimg] = maskimg
params[:maskqtree] = maskqtree
params[:groundsize] = groundsize
params[:groundoccupied] = groundoccupied

weights = weights ./ (sum(weights.^2) / length(weights))
weights = weights ./ (sum(weights.^2 .* length.(texts)) / length(weights))
params[:weights] = weights
scale = find_weight_scale(texts, weights, groundoccupied, border=border, initial_scale=0,
filling_rate=filling_rate, max_iter=5, error=0.03)
params[:scale] = scale
params[:filling_rate] = filling_rate
imgs, mimgs, qtrees = prepareforeground(texts, weights * scale, colors, angles, groundsize,
bgcolor=(0, 0, 0, 0), border=border, font=font);
params[:mimgs] = mimgs
# fr = feelingoccupied(mimgs)/groundoccupied
# println("scale = $scale, filling_rate = $fr")
params[:border] = border
params[:font] = font
placement!(deepcopy(maskqtree), qtrees)
Expand All @@ -151,19 +154,46 @@ function paint(wc::wordcloud, file)
end

function record(wc::wordcloud, ep::Number, gif_callback)
resultpic = overlay!(paint(wc), rendertext(string(ep), 32), 10, 10)
resultpic = overlay!(paint(wc), rendertext(string(ep), 32, color="black"), 10, 10)
resultpic = overlay!(resultpic, rendertext(string(ep), 35, color="white"), 10, 10)
gif_callback(resultpic)
end

function generate(wc::wordcloud, nepoch::Number=600, args...; trainer=trainepoch_gen!, optimiser=Momentum=1/4, ρ=0.5), patient=10, krags...)
ep, nc = train_with_teleport!(wc.qtrees, wc.maskqtree, nepoch, args...; trainer=trainer, optimiser=optimiser, patient=patient, krags...)
function generate(wc::wordcloud, nepoch::Number=600, args...; retry=2,
trainer=trainepoch_gen!, optimiser=Momentum=1/4, ρ=0.5), patient=10, krags...)
ep, nc = -1, -1
for r in 1:retry
fr = feelingoccupied(wc.params[:mimgs])/wc.params[:groundoccupied]
println("#$r. scale = $(wc.params[:scale]), filling_rate = $fr")
ep, nc = train_with_teleport!(wc.qtrees, wc.maskqtree, nepoch, args...;
trainer=trainer, optimiser=optimiser, patient=patient, krags...)
if nc == 0
break
end
sc = wc.params[:scale] * 0.95
rescale!(wc, sc)
end
@show ep, nc
if nc != 0
colllist = listcollision(wc.qtrees, wc.maskqtree)
get_text(i) = i>0 ? wc.texts[i] : "#MASK#"
colltexts = [(get_text(i), get_text(j)) for (i,j) in colllist]
if length(colllist) > 0
println("have $(length(colllist)) collision.",
" try setting a larger `nepoch` and `retry`, or lower `filling_rate` in `wordcloud` to fix that")
println("$colltexts")
end
end
wc
end

function generate_animation(wc::wordcloud, args...; outputdir="gifresult", callbackstep=1, kargs...)
try `mkdir $(outputdir)`|>run catch end
gif = GIF(outputdir)
record(wc, 0, gif)
ep, nc = generate(wc, args...; callbackstep=callbackstep, callbackfun=ep->record(wc, ep, gif), kargs...)
re = generate(wc, args...; callbackstep=callbackstep, callbackfun=ep->record(wc, ep, gif), kargs...)
Render.generate(gif)
ep, nc
end
re
end

Base.show(io, m::MIME"image/png", wc::wordcloud) = Base.show(io, m, paint(wc::wordcloud))
16 changes: 15 additions & 1 deletion src/qtree.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module QTree
export AbstractStackedQtree, StackedQtree, ShiftedQtree, buildqtree!,
shift!, setrshift!, setcshift!, setshift!, getshift, collision, collision_bfs, collision_bfs_rand,
shift!, setrshift!, setcshift!, setshift!, getshift,
collision, collision_bfs, collision_bfs_rand, listcollision,
findroom, levelnum, outofbounds, kernelsize, placement!, decode, placement!

using Random
Expand Down Expand Up @@ -317,6 +318,19 @@ function collision_bfs_rand(Q1::AbstractStackedQtree, Q2::AbstractStackedQtree,
return .- i # no collision
end

function listcollision(qtrees::AbstractVector, mask::AbstractStackedQtree)
collist = []
indpairs = combinations(0:length(qtrees), 2) |> collect
getqtree(i) = i==0 ? mask : qtrees[i]
for (i1, i2) in indpairs
cp = collision_bfs_rand(getqtree(i1), getqtree(i2))
if cp[1] >= 0
push!(collist, (i1, i2))
end
end
collist
end

function findroom(ground, q=[(levelnum(ground), 1, 1)])
if isempty(q)
push!(q, (levelnum(ground), 1, 1))
Expand Down
2 changes: 2 additions & 0 deletions src/rendering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ using ImageMagick

parsecolor(c) = parse(Colorant, c)
parsecolor(tp::Tuple) = ARGB32(tp...)
parsecolor(gray::Real) = Gray(gray)

function backgroundclip(p::AbstractMatrix, bgcolor; border=0)
a = c = 1
Expand Down Expand Up @@ -126,6 +127,7 @@ function shape(shape_, width, height, args...; color="white", bgcolor=(0,0,0,0))
Drawing(width, height, :image)
origin()
bgcolor = parsecolor(bgcolor)
background(bgcolor)
setcolor(parsecolor(color))
shape_(Point(0,0), width, height, args..., :fill)
mat = image_as_matrix()
Expand Down
20 changes: 15 additions & 5 deletions src/strategy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@ end
function occupied(imgs::AbstractVector, bgvalue=0)
return sum(p->occupied(p, bgvalue), imgs)
end
function box_occupied(img::AbstractMatrix)
return size(img, 1) * size(img, 2)
end
function box_occupied(imgs::AbstractVector)
return sum(box_occupied, imgs)
end
function feelingoccupied(imgs)
m = length(imgs) ÷ 4
occupied(imgs[1:m]) + box_occupied(imgs[m+1:end]) #兼顾大字的内隙和小字的占据
end

function text_occupied(texts, weights, scale; radius=0)
imgs = []
for (c, sz) in zip(texts, weights)
# print(c)
img = Render.rendertext(string(c), sz * scale, border=radius)
img = Render.textmask(img, img[1], radius=radius)
push!(imgs, img)
img, mimg = Render.rendertext(string(c), sz * scale, border=radius, returnmask=true)
push!(imgs, mimg)
end
return occupied(imgs)
feelingoccupied(imgs)
end

## prepare
Expand Down Expand Up @@ -64,7 +73,7 @@ function find_weight_scale(texts, weights, ground_size; border=0, initial_scale=
if initial_scale <= 0
initial_scale = (ground_size/length(texts))
end
@assert sum(weights.^2) / length(weights) 1.0
@assert sum(weights.^2 .* length.(texts)) / length(weights) 1.0
target_lower = (filling_rate - error) * ground_size
target_upper = (filling_rate + error) * ground_size
step = 0
Expand Down Expand Up @@ -177,6 +186,7 @@ function rescale!(wc::wordcloud, scale::Real)
wc.imgs = imgs
wc.qtrees = qtrees
wc.params[:scale] = scale
wc.params[:mimgs] = mimgs
lefttop(center, qt) = center .- kernelsize(qt) 2
setshift!.(qtrees, 1, lefttop.(centers, qtrees))
wc
Expand Down
15 changes: 1 addition & 14 deletions src/train.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,6 @@ function train_step_ind!(mask, qtrees, i1, i2, collisionpoint, optimiser)
end
end

function list_collision(qtrees, mask)
collist = []
indpairs = combinations(0:length(qtrees), 2) |> collect
getqtree(i) = i==0 ? mask : qtrees[i]
for (i1, i2) in indpairs
cp = collision_bfs_rand(getqtree(i1), getqtree(i2))
if cp[1] >= 0
push!(collist, (i1, i2))
end
end
collist
end

function trainepoch!(qtrees, mask; optimiser=(t, Δ)->Δ./4, optimiser_near=(t, Δ)->Δ./4, nearlevel=0, queue=Vector{Tuple{Int, Int, Int}}(), collpool=nothing)
nearlevel = nearlevel<0 ? levelnum(qtrees[1])+nearlevel : nearlevel
Expand Down Expand Up @@ -271,12 +259,11 @@ end

function train_with_teleport!(ts, maskqt, nepoch::Number, args...;
trainer=trainepoch_gen!, patient::Number=5, callbackstep=0, callbackfun=x->x,
queue=Vector{Tuple{Int, Int, Int}}(), kargs...)
queue=Vector{Tuple{Int, Int, Int}}(), collpool = Vector{Tuple{Int,Int}}(), kargs...)
ep = 0
nc = 0
count = 0
nc_min = Inf
collpool = Vector{Tuple{Int,Int}}()
while ep < nepoch
nc = trainer(ts, maskqt, args...; collpool=collpool, queue=queue, kargs...)
ep += 1
Expand Down
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ include("test_qtree.jl")
weights = randexp(length(texts)) .* 1000 .+ randexp(length(texts)) .* 200 .+ rand(20:100, length(texts));
wc = wordcloud(texts, weights, filling_rate=0.45)
paint(wc)
@show generate(wc)
generate(wc)
paint(wc::wordcloud, "test.jpg")
@test isempty(WordCloud.outofbounds(wc.maskqtree, wc.qtrees))
end
Expand Down

0 comments on commit 8fdafba

Please sign in to comment.