diff --git a/src/tdpacking.jl b/src/tdpacking.jl index fa602e7..bb5959a 100644 --- a/src/tdpacking.jl +++ b/src/tdpacking.jl @@ -1,5 +1,14 @@ using Base using Statistics, Images +import Luxor as L + +@kwdef struct Region + pos::Tuple{Int,Int} + size::Tuple{Int,Int} + # If true, this region increases from x to y. This direction variable steers + # the direction of cut. + dir::Bool +end # Time-dependent packing @kwdef struct Rect @@ -16,41 +25,53 @@ function aspect_ratio(r::Rect)::Real end """ - divide_image(img::BitMatrix, size_min::Int=16, size_max::Int=256) + divide_image(img::Matrix{}, size_min::Int=16, size_max::Int=256) Divides an image into rectangles of the same colour. The rectangles are not allowed to have extreme aspect ratios. There is a minimal size of each retangle. """ -function divide_image(img::Matrix{}, size_min::Int=16, size_max::Int=256, threshold_aspect::Real=3.0, threshold_std::Real=0.4)::Array{Rect} - remainder = Rect[Rect(pos=(1,1), size=size(img), color=mean(img))] +function divide_image(img::Matrix{}; size_min::Int=16, size_max::Int=128, threshold_aspect::Real=3.0, threshold_std::Real=0.4)::Array{Rect} + remainder = [Region(pos=(1,1), size=size(img), dir=false)] result = Rect[] # Loop until the entire field is filled while length(remainder) > 0 section = pop!(remainder) - width = size_min - height = size_min + (px, py) = section.pos + (sx, sy) = section.size + + @assert px + sx - 1 <= size(img)[1] + @assert py + sy - 1 <= size(img)[2] + + width = 1 + height = 1 + + width_max = min(sx, size_max) + height_max = min(sy, size_max) # Try to expand this rectangle to the maximum size - while width < section.size[1] || height < section.size[2] - subimg = img[section.pos[1]:section.pos[1]+width-1, section.pos[2]:section.pos[2]+height-1] + while width < width_max || height < height_max + subimg = img[px:px+width-1, py:py+height-1] if std(subimg) >= threshold_std break end colour = mean(subimg) ar = width / height - if width < section.size[1] && ar < threshold_aspect + if width < width_max && ar < threshold_aspect width += 1 - elseif height < section.size[1] + elseif height < height_max && ar > 1/threshold_aspect height += 1 else break end end - subimg = img[section.pos[1]:section.pos[1]+width-1, section.pos[2]:section.pos[2]+height-1] + @assert width <= sx + @assert height <= sy + + subimg = img[px:px+width-1, py:py+height-1] rect = Rect( pos=section.pos, size=(width, height), @@ -58,8 +79,54 @@ function divide_image(img::Matrix{}, size_min::Int=16, size_max::Int=256, thresh ) push!(result, rect) - # FIXME: Add next iteration region + if section.dir + if width < sx + push!(remainder, Region( + pos=(px + width, py), + size=(sx - width, height), + dir=false, + )) + end + if height < sy + push!(remainder, Region( + pos=(px, py + height), + size=(sx, sy - height), + dir=true, + )) + end + else + if height < sy + push!(remainder, Region( + pos=(px, py + height), + size=(width, sy - height), + dir=true, + )) + end + if width < sx + push!(remainder, Region( + pos=(px + width, py), + size=(sx - width, sy), + dir=false, + )) + end + end end return result end + +""" + draw_rect(size::Tuple{Int, Int}, rects::Array{Rect}) + +Displays the divided image in greyscale. +""" +function draw_rect(size::Tuple{Int, Int}, rects::Array{Rect}) + L.Drawing(size[1], size[2], :png) + L.background("black") + for rect in rects + c = N0f8(rect.color) + setcolor(c, c, c) + L.rect(L.Point(rect.pos), rect.size[1], rect.size[2], action = :fill) + end + L.finish() +end