Skip to content

Commit

Permalink
#16 Added test scripts for diffusion and connected components.
Browse files Browse the repository at this point in the history
WIP; not ready for automated testing.
  • Loading branch information
carljohnsen committed Sep 9, 2024
1 parent 63abddf commit bf1b8e1
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 0 deletions.
93 changes: 93 additions & 0 deletions src/test/test_connected_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'''
Unit test for connected_components
'''
import cc3d
import lib.cpp.cpu.connected_components as cc
import datetime
from functools import partial
import multiprocessing as mp
from multiprocessing.pool import ThreadPool as tp
import numpy as np
import scipy.ndimage as ndi

def test1():
a = np.array([
[0,0,0],
[1,1,1],

[1,0,2],
[1,0,2],

[1,0,2],
[1,0,2],

[1,1,1],
[0,0,0],
], dtype=np.int64).reshape(4, 2, 1, 3)
labels_a = np.array([1, 2, 2, 1], dtype=np.int64)
print (a.shape, labels_a.shape)
print (a.reshape(8,3))
new_labels = cc.merge_labeled_chunks(a, labels_a, True)
print (a.reshape(8,3))
print (new_labels)

def test2():
a = np.array([
[0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,0,2,2,2,0,3,0,0,5],
[1,0,2,2,2,0,3,0,0,4,0,6],
[0,0,0,0,0,0,0,0,0,0,0,0],
], dtype=np.int64).reshape(2, 2, 1, 12)
labels_a = np.array([5,6], dtype=np.int64)
print (a.shape, labels_a.shape)
print (a.reshape(4,12))
new_labels = cc.merge_labeled_chunks(a, labels_a, True)
print (a.reshape(4,12))
print (new_labels)

def test3():
a = np.array([
[0,0,0,0,0,0,0,0,0,4,0,0],
[1,1,1,0,2,2,2,0,3,0,0,5],

[1,0,2,0,3,0,4,0,0,5,0,6],
[1,0,2,0,3,0,4,0,0,0,0,6],

[1,0,2,0,3,0,4,0,0,0,0,6],
[1,0,2,0,3,0,4,0,0,5,0,6],

[1,0,2,2,2,0,3,0,4,0,0,6],
[0,0,0,0,0,0,0,0,0,5,0,0],
], dtype=np.int64).reshape(4, 2, 1, 12)
labels_a = np.array([5,6,6,6], dtype=np.int64)
print (a.shape, labels_a.shape)
print (a.reshape(8,12))
new_labels = cc.merge_labeled_chunks(a, labels_a, True)
print (a.reshape(8,12))
print (new_labels)

def test4():
a = np.array([
[0,0,0,0,0,0,0,4,0,5,0,0],
[0,0,2,2,2,2,2,0,0,5,0,0],
[0,0,2,0,0,0,2,0,0,5,0,0],
[1,0,2,0,3,0,2,0,0,5,0,6],

[1,0,2,0,3,0,1,0,0,5,0,6],
[1,0,0,0,0,0,1,0,0,5,0,0],
[1,1,1,1,1,1,1,0,0,5,0,0],
[0,0,0,0,0,0,0,4,0,5,0,0],
], dtype=np.int64).reshape(2, 4, 1, 12)
labels_a = np.array([6,6], dtype=np.int64)

print (a.shape, labels_a.shape)
print (a.reshape(8,12))
new_labels = cc.merge_labeled_chunks(a, labels_a, True)
print (a.reshape(8,12))
print (new_labels)

if __name__ == '__main__':
test1()
test2()
test3()
test4()
114 changes: 114 additions & 0 deletions src/test/test_diffusion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'''
Unit test for diffusion.
Currently only tests in-memory diffusion, not the file-based one.
'''
import sys
sys.path.append(sys.path[0]+'/../')
import datetime
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage as ndi

from lib.cpp.cpu.diffusion import diffusion as diffusion_cpu
from lib.cpp.gpu.diffusion import diffusion as diffusion_gpu

n = 333
sigma = 4 # Radius has to be <= 16 for the GPU implementation
reps = 1
plot = False

# Generate a random 3d array and binarize it
np.random.seed(42)
a = np.random.rand(n, n, n)
a = a > 0.99

# Get a gaussian kernel
# Stolen from ndimage
radius = round(4.0 * sigma) # stolen from the default scipy parameters
sigma2 = sigma * sigma
x = np.arange(-radius, radius+1)
phi_x = np.exp(-0.5 / sigma2 * x ** 2)
phi_x = phi_x / phi_x.sum()
kernel = phi_x.astype(np.float32)
print (f'Kernel shape: {kernel.shape}, radius: {radius}')

#
# Python implementation
#
python_start = datetime.datetime.now()
buf0 = a.copy().astype(np.float32)
buf1 = np.zeros_like(buf0)
for _ in range(reps):
buf1[:] = ndi.convolve1d(buf0, kernel, axis=0, mode='constant')
buf0[:] = ndi.convolve1d(buf1, kernel, axis=1, mode='constant')
buf1[:] = ndi.convolve1d(buf0, kernel, axis=2, mode='constant')
buf1[a] = 1
buf0, buf1 = buf1, buf0
python_impl = np.floor(buf0 * 65535).astype(np.uint16)
del buf1
python_end = datetime.datetime.now()
print (f"Python implementation took {python_end - python_start}")

if plot:
plt.imshow(python_impl[n//2], cmap='gray')
plt.savefig('python_impl.png')
plt.close()
plt.imshow(a[n//2], cmap='gray')
plt.savefig('original.png')
plt.close()

#
# Parallel CPU C++ implementation
#
cpp_start = datetime.datetime.now()
cpp_impl = np.empty(a.shape, np.uint16)
diffusion_cpu(a.astype(np.uint8), kernel, cpp_impl, reps)
cpp_end = datetime.datetime.now()
print (f"Parallel CPU C++ implementation took {cpp_end - cpp_start} ({(python_end - python_start) / (cpp_end - cpp_start):.2f}x)")

if plot:
plt.imshow(cpp_impl[n//2], cmap='gray')
plt.savefig('cpp_impl.png')
plt.close()

# Check if the results are the same
diff = np.abs(python_impl.astype(np.int32) - cpp_impl.astype(np.int32))
divergers = np.sum(diff > 0)
divergers2 = np.sum(diff > 1)
if divergers2 > 0:
print (f"Found {divergers} diverging pixels out of ({n**3}) ({divergers / n**3 * 100:.2f}%)")
print (f"Found {divergers2} pixels with a difference greater than 1 ({divergers2 / n**3 * 100:.2f}%)")
assert np.all(diff <= 1)

#
# Parallel GPU C++ implementation
#
gpu_start = datetime.datetime.now()
gpu_impl = np.empty(a.shape, np.uint16)
diffusion_gpu(a.astype(np.uint8), kernel, gpu_impl, reps)
gpu_end = datetime.datetime.now()
print (f"Parallel GPU C++ implementation took {gpu_end - gpu_start} ({(python_end - python_start) / (gpu_end - gpu_start):.2f}x)")

if plot:
plt.imshow(gpu_impl[n//2], cmap='gray')
plt.savefig('gpu_impl.png')
plt.close()

# Check if the results are the same
diff = np.abs(python_impl.astype(np.int32) - gpu_impl.astype(np.int32))
divergers = np.sum(diff > 0)
divergers2 = np.sum(diff > 1)
if divergers2 > 0:
print (f"Found {divergers} diverging pixels out of ({n**3}) ({divergers / n**3 * 100:.2f}%)")
print (f"Found {divergers2} pixels with a difference greater than 1 ({divergers2 / n**3 * 100:.2f}%)")
if plot:
plt.imshow(diff[n//2])
plt.colorbar()
plt.savefig('diff.png')
plt.close()
checksum = np.sum(gpu_impl)
print (f"Checksum of GPU: {checksum != 0} ({checksum})")
assert np.all(diff <= 1)

0 comments on commit bf1b8e1

Please sign in to comment.