Skip to content

Commit 5ffae27

Browse files
committed
init code
1 parent 082dd3e commit 5ffae27

File tree

9 files changed

+518
-11
lines changed

9 files changed

+518
-11
lines changed

Project.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,19 @@ uuid = "a2bd6b23-9ae5-5b43-94df-c047a0516ae5"
33
authors = ["Benedikt Ehinger <[email protected]>"]
44
version = "0.1.0"
55

6+
[deps]
7+
Atomix = "a9b6321e-bd34-4604-b9c9-b65b8de01458"
8+
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
9+
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
10+
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
11+
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
12+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
13+
614
[compat]
15+
Atomix = "1.0.1"
16+
Distances = "0.10.12"
17+
Distributions = "0.25.117"
18+
Interpolations = "0.15.1"
19+
KernelAbstractions = "0.9.33"
20+
Random = "1.11.0"
721
julia = "1.10"

docs/Project.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
# Don't forget to run
2-
#
3-
# pkg> dev ..
4-
#
51
[deps]
2+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
3+
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
4+
DiscreteVoronoi = "01fd5c3e-b3b7-45fb-9d19-e82518019796"
65
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
76
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
87
MakieStippling = "a2bd6b23-9ae5-5b43-94df-c047a0516ae5"
8+
MeshGrid = "ebf956a0-ef5e-43be-9fb1-27952996e635"
9+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
10+
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
911

1012
[compat]
1113
Documenter = "1"

docs/src/01-stippling.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
```julia
2+
using MakieStippling
3+
using CUDA
4+
#using Interpolations
5+
using Random
6+
7+
using CairoMakie
8+
using MeshGrid
9+
```
10+
11+
create random data
12+
13+
```julia
14+
n_sites = 5000
15+
n_conditions = 2
16+
sites_grouping = rand(1:n_conditions,n_sites)
17+
18+
19+
grid = Int.(zeros(500,500))
20+
grid_combined = CuArray(grid)
21+
sites = CartesianIndices(size(grid))[rand(1:length(grid),n_sites)]
22+
nothing #hide
23+
```
24+
25+
create an arbitrary density function
26+
27+
```julia
28+
times = collect(range(0,size(grid_combined,1),length=5))
29+
density_interpolators = Vector{Any}(undef,n_conditions)
30+
max_density = 1
31+
for k = 1:n_conditions
32+
mean_data = rand(MersenneTwister(k),5).*size(grid,1)/4 .+ size(grid,1)/2
33+
std_data = [20,10,5,15,30.]#abs.(rand(MersenneTwister(k*2),5)).*size(grid,1)
34+
35+
itp_mean = MakieStippling.Interpolations.interpolate((times,), mean_data, MakieStippling.Interpolations.Gridded(MakieStippling.Interpolations.Linear()))
36+
itp_std = MakieStippling.Interpolations.interpolate((times,), std_data, MakieStippling.Interpolations.Gridded(MakieStippling.Interpolations.Linear()))
37+
38+
density_interpolators[k] = MakieStippling.DensityInterpolator(itp_mean,itp_std,max_density)
39+
40+
end
41+
42+
43+
x,y = meshgrid(1:size(grid_combined,1),1:size(grid_combined,2))
44+
pts = Point2f.(x,y)
45+
46+
fig = Figure()
47+
ax_dens = fig[1,1] = Axis(fig,title="underlying densities")
48+
hidespines!(ax_dens)
49+
hidedecorations!(ax_dens)
50+
for k = 1:n_conditions
51+
heatmap(fig[1,1][1,k],MakieStippling.calculate_density.(Ref(density_interpolators[k]),pts)')
52+
hidedecorations!(current_axis())
53+
end
54+
fig
55+
```
56+
57+
```julia
58+
grid_single,sites_sets = MakieStippling.run_iterations!(grid_combined,sites,sites_grouping,density_interpolators;n_iter=3,threshold_low=10e-10,threshold_high=0.65*10-6)
59+
60+
61+
site_set_to_point2f(s::Set) = [Point2f(k[1],k[2]) for k in MakieStippling.linearized_sites.(collect(s),Ref(size(grid_combined)))]
62+
63+
fig = Figure()
64+
ax = fig[1,1] = Axis(fig,aspect=1)
65+
scatter!.(ax,site_set_to_point2f.(sites_sets);markerspace = :data,)
66+
xlims!(ax,[0,500])
67+
ylims!(ax,[0,500])
68+
fig
69+
70+
```
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
```julia
2+
using MakieStippling
3+
using CUDA
4+
using DiscreteVoronoi
5+
using StaticArrays
6+
using CairoMakie
7+
8+
9+
```
10+
11+
```julia
12+
grid = Int.(zeros(30,30))
13+
grid_static = zeros(SVector{2,Int}, size(grid,1),size(grid,2))
14+
cu_grid = CuArray(grid)
15+
16+
n_sites = 100
17+
sites = CartesianIndices(size(grid))[rand(1:length(grid),n_sites)]
18+
#sites_lin = MakieStippling.linearized_sites(sites, size(grid))
19+
20+
sites_static = [SVector{2,Int}(x,y) for (x,y) in Tuple.(sites)]
21+
#sites = [CartesianIndex(1,1),CartesianIndex(10,10)]
22+
#grid[sites] .= 1:size(sites,1)
23+
#---
24+
MakieStippling.jfa_voronoi_gpu!(grid,sites)
25+
MakieStippling.jfa_voronoi_gpu!(cu_grid,sites)
26+
27+
DiscreteVoronoi.jfa_voronoi!(grid_static,sites_static)
28+
29+
# convert to linearized
30+
grid_static_lin =MakieStippling.linearized_sites(grid_static,size(grid_static))
31+
32+
n_elems = *(size(grid)...)
33+
34+
# DiscreteVoronoi vs. CPU
35+
sum(grid_static_lin .== grid) / n_elems
36+
```
37+
38+
```julia
39+
# CPU vs GPU
40+
sum(Matrix(cu_grid) .== grid) / n_elems
41+
```
42+
43+
```julia
44+
# DiscreteVoronoi vs. GPU
45+
sum(Matrix(cu_grid) .== grid_static_lin)/n_elems
46+
47+
```
48+
49+
```julia
50+
f = Figure()
51+
52+
heatmap(f[1,1],grid_static_lin,colormap=:tab10,axis=(;title="DiscreteVoronoi.jl"))
53+
heatmap(f[1,2],grid,colormap=:tab10,axis=(;title="cpu"))
54+
heatmap(f[1,3],Matrix(cu_grid),colormap=:tab10,axis=(;title="cu"))
55+
56+
heatmap(f[2,1],grid.==grid_static_lin,axis=(;title="grid - DiscreteVoronoi.jl"))
57+
heatmap(f[2,2],Matrix(cu_grid).==grid_static_lin,axis=(;title="Cu - DiscreteVoronoi.jl"))
58+
heatmap(f[2,3],Matrix(cu_grid).==grid,axis=(;title="Cu - Grid"))
59+
60+
f

src/MakieStippling.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
module MakieStippling
2+
using Distributions: value_support
23

3-
"""
4-
hi = hello_world()
5-
A simple function to return "Hello, World!"
6-
"""
7-
function hello_world()
8-
return "Hello, World!"
9-
end
4+
using Interpolations, Random
5+
using Distributions
6+
7+
using Distances
8+
using KernelAbstractions
9+
10+
using Atomix
11+
12+
13+
14+
include("jfa_voronoi.jl")
15+
include("grid_features.jl")
16+
include("density.jl")
17+
include("stippling.jl")
1018

1119
end

src/density.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
3+
4+
struct DensityInterpolator
5+
mean_interpolator::Any
6+
std_interpolator::Any
7+
max_density::Any
8+
end
9+
10+
function calculate_density(d::DensityInterpolator, point)
11+
#@show point
12+
mean = d.mean_interpolator(point[1])
13+
std = d.std_interpolator(point[1])
14+
pdf_value = pdf(Normal(mean, std), point[2])
15+
normalized_value = (pdf_value) / (d.max_density)
16+
return normalized_value
17+
end

src/grid_features.jl

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
2+
3+
"""
4+
returns area and centroids for all elements in sites_set.
5+
"""
6+
function grid_features(A::AbstractMatrix{<:Integer}, sites_set)
7+
device = get_backend(A)
8+
# Define the number of bins for the histogram
9+
num_bins = length(sites_set)
10+
11+
# Create arrays to store the histogram counts
12+
hist_counts = similar(A, Int32, num_bins)
13+
hist_counts .= 0
14+
15+
# Create arrays to store the sum of x-coordinates for each bin
16+
hist_x_sum = similar(A, Int32, num_bins)
17+
hist_x_sum .= 0
18+
# Create arrays to store the sum of y-coordinates for each bin
19+
hist_y_sum = similar(A, Int32, num_bins)
20+
hist_y_sum .= 0
21+
22+
sites_array = similar(A, Int32, num_bins)
23+
sites_array = typeof(sites_array)(Int32.(collect(sites_set)))
24+
# Define the kernel
25+
@kernel function histogram_kernel!(A, hist_counts, hist_x_sum, hist_y_sum, sites_set)
26+
i = @index(Global, Linear)
27+
#x = mod(i - 1, size(A, 2)) + 1
28+
#y = cld(i, size(A, 2))
29+
sz_grid = size(A)
30+
coords = CartesianIndices(sz_grid)
31+
32+
x = coords[i][1]
33+
y = coords[i][2]
34+
35+
bin_idx = findfirst(==(A[x, y]), sites_set)
36+
if !isnothing(bin_idx)
37+
Atomix.@atomic hist_counts[bin_idx] += 1
38+
KernelAbstractions.@atomic hist_x_sum[bin_idx] += Int32(x)
39+
KernelAbstractions.@atomic hist_y_sum[bin_idx] += Int32(y)
40+
#=
41+
=#
42+
end
43+
end
44+
# @show "launch hist"
45+
# Launch the kernel
46+
event = histogram_kernel!(device, 128)(
47+
A,
48+
hist_counts,
49+
hist_x_sum,
50+
hist_y_sum,
51+
sites_array;
52+
ndrange = size(A),
53+
)
54+
KernelAbstractions.synchronize(device)
55+
56+
57+
# Calculate the centroids
58+
centroids_x = similar(A, Float32, num_bins)
59+
centroids_y = similar(A, Float32, num_bins)
60+
61+
@kernel function centroid_kernel!(
62+
hist_counts,
63+
hist_x_sum,
64+
hist_y_sum,
65+
centroids_x,
66+
centroids_y,
67+
)
68+
i = @index(Global, Linear)
69+
if hist_counts[i] > 0
70+
centroids_x[i] = hist_x_sum[i] / hist_counts[i]
71+
centroids_y[i] = hist_y_sum[i] / hist_counts[i]
72+
end
73+
end
74+
# @show "launch centroid"
75+
# Launch the centroid kernel
76+
event = centroid_kernel!(device, 128)(
77+
hist_counts,
78+
hist_x_sum,
79+
hist_y_sum,
80+
centroids_x,
81+
centroids_y;
82+
ndrange = (num_bins,),
83+
)
84+
KernelAbstractions.synchronize(device)
85+
86+
return Vector(hist_counts) ./ *(size(A)...),
87+
vec2tuple.(Vector(centroids_x), Vector(centroids_y))
88+
end
89+
vec2tuple(x, y) = (x, y)
90+
91+
92+
93+
#=
94+
naive CPU version
95+
96+
function grid_features(grid,id::Int)
97+
ix = findall(x->x==id,grid)
98+
centroid = Tuple(sum(ix))./length(ix)
99+
area = length(ix)
100+
return centroid,area
101+
end
102+
=#

src/jfa_voronoi.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
function jfa_voronoi_gpu!(grid, sites::AbstractVector{<:CartesianIndex}; kwargs...)
3+
sz_grid = size(grid)
4+
jfa_voronoi_gpu!(grid, linearized_sites(sites, sz_grid); kwargs...)
5+
end
6+
7+
function jfa_voronoi_gpu!(
8+
grid,
9+
sites::Vector{Int};
10+
distance = euclidean,
11+
do_1plusjfa = false,
12+
)
13+
grid .= 0
14+
grid[sites] = sites
15+
k = max(size(grid)...)
16+
17+
if do_1plusjfa
18+
19+
jfa_kernel(get_backend(grid), 128)(grid, 1, ndrange = size(grid))
20+
KernelAbstractions.synchronize(get_backend(grid))
21+
end
22+
23+
while k > 1
24+
k = k ÷ 2 + k % 2
25+
jfa_kernel(get_backend(grid), 128)(grid, k, distance, ndrange = size(grid))
26+
KernelAbstractions.synchronize(get_backend(grid))
27+
end
28+
end
29+
30+
31+
32+
@kernel function jfa_kernel(grid, k, distance)
33+
ix_grid = @index(Global) # could be slightly optimized with Global, Cartesian
34+
35+
sz_grid = size(grid)
36+
coords = CartesianIndices(sz_grid)
37+
38+
coords_grid = Tuple(coords[ix_grid])
39+
(x, y) = coords_grid
40+
41+
for j in (-k, 0, k), i in (-k, 0, k)
42+
checkbounds(Bool, grid, x + i, y + j) || continue
43+
i == j == 0 && continue
44+
# get candidate at k-distanced grid location
45+
ix_siteq = grid[x+i, y+j]
46+
ix_siteq != 0 || continue
47+
# get current gridlocation value
48+
ix_sitep = grid[x, y]
49+
# if current location is not yet assigned, assign it with candidate
50+
if ix_sitep == 0
51+
grid[x, y] = ix_siteq
52+
elseif distance(Tuple(coords[ix_sitep]), coords_grid) >
53+
distance(Tuple(coords[ix_siteq]), coords_grid)
54+
# if candidate site q is closer to what is currently saved, use that
55+
grid[x, y] = ix_siteq
56+
end
57+
end
58+
59+
60+
end

0 commit comments

Comments
 (0)