Reputation: 3070
I have a binary image size of 256x256x256
, where the foreground region is located in a small region, and I have a lot of zero margins. I want to cut zero edges by finding the minimum and maximum coordinate of points that has pixel non-zero in images. It worked but it spends time-consuming. I post my code and could you tell me how we can make it faster?
For the image size of 256x256x256
, it takes about 0.13024640083312988 seconds. This is the code and you can run online at https://repl.it/repls/AnxiousExoticBackup
import numpy as np
import time
def cut_edge(image, keep_margin):
'''
function that cuts zero edge
'''
D, H, W = image.shape
D_s, D_e = 0, D - 1
H_s, H_e = 0, H - 1
W_s, W_e = 0, W - 1
while D_s < D:
if image[D_s].sum() != 0:
break
D_s += 1
while D_e > D_s:
if image[D_e].sum() != 0:
break
D_e -= 1
while H_s < H:
if image[:, H_s].sum() != 0:
break
H_s += 1
while H_e > H_s:
if image[:, H_e].sum() != 0:
break
H_e -= 1
while W_s < W:
if image[:, :, W_s].sum() != 0:
break
W_s += 1
while W_e > W_s:
if image[:, :, W_e].sum() != 0:
break
W_e -= 1
if keep_margin != 0:
D_s = max(0, D_s - keep_margin)
D_e = min(D - 1, D_e + keep_margin)
H_s = max(0, H_s - keep_margin)
H_e = min(H - 1, H_e + keep_margin)
W_s = max(0, W_s - keep_margin)
W_e = min(W - 1, W_e + keep_margin)
return int(D_s), int(D_e)+1, int(H_s), int(H_e)+1, int(W_s), int(W_e)+1
image = np.zeros ((256,256,256),dtype=np.float32)
ones_D_min, ones_D_max, ones_H_min, ones_H_max,ones_W_min, ones_W_max= 100,200, 90,150, 60,200
image[ones_D_min: ones_D_max,ones_H_min:ones_H_max, ones_W_min:ones_W_max]=1
t0=time.time()
ones_D_min_result, ones_D_max_result, ones_H_min_result, ones_H_max_result, ones_W_min_result, ones_W_max_result= cut_edge(image,0)
t1=time.time()
print ('Time consuming ', t1-t0)
print (ones_D_min, ones_D_max, ones_H_min, ones_H_max,ones_W_min, ones_W_max)
print (ones_D_min_result, ones_D_max_result, ones_H_min_result, ones_H_max_result, ones_W_min_result, ones_W_max_result)
Upvotes: 2
Views: 272
Reputation: 29635
You can use the fact that if you sum
over an axis of your 3D array, then the value will still be 0 where there is no 1 in the row (or column or third dimension) depending on which axis
parameter. Then by using any
in one of the two other directions and np.argwhere
you will get the index where there is at least one 1 in the other axis. Using min
and max
will give the value you are looking for. Here is the function:
def cut_edge_2(image, keep_margin):
im_sum0 = (image.sum(0) !=0)
im_sum1 = (image.sum(1) !=0)
ones_D = np.argwhere(im_sum1.any(1))
ones_H = np.argwhere(im_sum0.any(1))
ones_W = np.argwhere(im_sum0.any(0))
if keep_margin != 0:
D, H, W = image.shape
return (max( 0, ones_D.min() - keep_margin), min(D, ones_D.max() + keep_margin+1),
max( 0, ones_H.min() - keep_margin), min(H, ones_H.max() + keep_margin+1),
max( 0, ones_W.min() - keep_margin), min(W, ones_W.max() + keep_margin+1))
return (ones_D.min(), ones_D.max() +1,
ones_H.min(), ones_H.max() +1,
ones_W.min(), ones_W.max() +1)
You get the same result than with your function:
print (cut_edge(image,0))
#(100, 200, 90, 150, 60, 200)
print (cut_edge_2(image,0))
#(100, 200, 90, 150, 60, 200)
print (cut_edge(image,60))
#(40, 256, 30, 210, 0, 256)
print (cut_edge_2(image,60))
#(40, 256, 30, 210, 0, 256)
and some timeit
:
%timeit cut_edge(image,0)
#93 ms ± 7.62 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit cut_edge_2(image,0)
#25.3 ms ± 8.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit cut_edge_2(image,1)
#26.2 ms ± 4.73 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit cut_edge(image,1)
#95.4 ms ± 6.63 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
It is faster.
Upvotes: 1
Reputation: 910
Your function can be improved significantly using numpy's built-in functions:
def cut_edge(image, keep_margin):
'''
function that cuts zero edge
'''
#Calculate sum along each axis
D_sum = np.sum(image, axis=(1,2)) #0
H_sum = np.sum(image, axis=(0,2)) #1
W_sum = np.sum(image, axis=(0,1)) #2
#Find the non-zero values
W_nz = np.nonzero(W_sum)[0]
H_nz = np.nonzero(H_sum)[0]
D_nz = np.nonzero(D_sum)[0]
#Take the first and last entries for start and end
D_s = D_nz[0]
D_e = D_nz[-1]
H_s = H_nz[0]
H_e = H_nz[-1]
W_s = W_nz[0]
W_e = W_nz[-1]
if keep_margin != 0:
D_s = max(0, D_s - keep_margin)
D_e = min(D - 1, D_e + keep_margin)
H_s = max(0, H_s - keep_margin)
H_e = min(H - 1, H_e + keep_margin)
W_s = max(0, W_s - keep_margin)
W_e = min(W - 1, W_e + keep_margin)
return D_s, D_e+1, H_s, H_e+1, W_s, W_e+1
And the result:
Time consuming 0.0963144302368164
Upvotes: 3