0

I have a numpy array, which respresents an image. The image has 3 colors: orange (background), blue (object1) and green (object2). I use 3 values (0, 1 and 2) to indicate the 3 colors in numpy array. Two objects are not overlapped.

My question is: How do know which object is nearer to the center (red point) of the image? (Here, nearer means the nearest distance from the object to the center of the image of one object is smaller than the nearest distance from the object to the center of the image of the otehr object)

My code is like this:

import numpy as np
from scipy import spatial
import time

sub_image1 = np.ones((30, 30, 30))
sub_image2 = np.ones((20, 10, 15))

# pad the two sub_images to same shape (1200, 1200, 1200) to simulate my 3D medical data
img_1 = np.pad(sub_image1, ((1100, 70), (1100, 70), (1100, 70)))
img_2 = np.pad(sub_image1, ((1100, 80), (1130, 60), (1170, 15)))

def nerest_dis_to_center(img):
    position = np.where(img > 0)
    coordinates = np.transpose(np.array(position))  # get the coordinates where the voxels is not 0
    cposition = np.array(img.shape) / 2  # center point position/coordinate
    distance, index = spatial.KDTree(coordinates).query(cposition)
    return distance

t1 = time.time()
d1 = nerest_dis_to_center(img_1)
d2 = nerest_dis_to_center(img_2)

if d1 > d2:
    print("img2 object is nearer")
elif d2 > d1:
    print("img1 object is nearer")
else:
    print("They are the same far")
t2 = time.time()
print("used time: ", t2-t1)
# 30 seconds

The above code works, but slowly and it requires very big memory (about 30 GB). If you want to reproduce my code in your PC, you can use a smaller shape instead of (3200, 1200, 1200). Is there any more efficient way to achieve my goal?

Note: Actually my image is a 3D CT medical image, it is too big to be uploaded. The objects in the image is random, may be convex or not. That is why my implimentation is far slowly. Here in order to clarify my question, I use the 2D image to explain my method.

Figure

Jingnan Jia
  • 1,108
  • 2
  • 12
  • 28
  • are your object shapes fixed? – sai Oct 10 '20 at 19:25
  • @sai No. The shape is not fixed. – Jingnan Jia Oct 10 '20 at 19:26
  • Are the images convex, or have any dependable regular properties? – Prune Oct 10 '20 at 19:41
  • This is just an idea but using a sparse array might help with speed. – nocibambi Oct 10 '20 at 19:41
  • 1
    Please provide the expected [MRE](https://stackoverflow.com/help/minimal-reproducible-example). Show where the intermediate results deviate from the ones you expect. We should be able to paste a single block of your code into file, run it, and reproduce your problem. – Prune Oct 10 '20 at 19:41
  • @Prune I have updated my code. – Jingnan Jia Oct 10 '20 at 20:07
  • Much better. I can't run this on my personal device, but it looks complete. Down-vote removed. – Prune Oct 10 '20 at 20:11
  • @Prune You can use a smaller shape instead of (3200, 1200, 1200), in order to reproduce my code in your pc. – Jingnan Jia Oct 10 '20 at 20:15
  • If your images are these clean(and objects don't overlap), I have an approach to solving this just using the coordinates (row, column values). Basically it is to find a particular row or column to split and then take the average of rows and columns to calculate a metric similar to centroid. Does this come as a logically reasonable way to handle your input data? – sai Oct 10 '20 at 20:46
  • @sai I hope to compare the "nearest distance" instead of centroid. – Jingnan Jia Oct 10 '20 at 21:02
  • One can calculate the distance using centroids and then compare easily right? – sai Oct 10 '20 at 21:13
  • I donot know which disance is more time-consuming from one object to one point, "mearest distance" to the point? or "centroid" to the point? I have no idea. – Jingnan Jia Oct 10 '20 at 21:22
  • Color threshold, center of mass. – Mad Physicist Oct 11 '20 at 05:59

2 Answers2

0

This might not be the final solution or the most optimal w.r.t time, have to test it with the actual data. To get my idea through I have chosen smaller matrix sizes and only 2D case

import numpy as np
import matplotlib.pyplot as plt


sub_image1 = np.ones((30, 30))  # 1st object
sub_image2 = np.ones((20, 10)) * 2  # 2nd object

# pad the two sub_images to same shape (120, 120)
img_1 = np.pad(sub_image1, ((110, 60), (60, 110)))
img_2 = np.pad(sub_image2, ((100, 80), (130, 60)))

final_image = img_1 + img_2  # creating final image with both objects in a background of zeros

image_center = (np.array([final_image.shape[0], final_image.shape[1]]) / 2).astype(np.int)

# mark the center
final_image[image_center[0], image_center[1]] = 10

# find the coordinates of where the objects are
first_obj_coords = np.argwhere(final_image == 1)  # could be the most time consuming operation
second_obj_coords = np.argwhere(final_image == 2) # could be the most time consuming 

# find their centers
first_obj_ctr = np.mean(first_obj_coords, axis=0)
second_obj_ctr = np.mean(second_obj_coords, axis=0)

# turn the centers to int for using them to index
first_obj_ctr = np.floor(first_obj_ctr).astype(int)
second_obj_ctr = np.floor(second_obj_ctr).astype(int)

# mark the centers of the objects
final_image[first_obj_ctr[0], first_obj_ctr[1]] = 10
final_image[second_obj_ctr[0], second_obj_ctr[1]] = 10

# calculate the distances from center to the object center
print('Distance to first object: ', np.linalg.norm(image_center - first_obj_ctr))
print('Distance to second object: ', np.linalg.norm(image_center - second_obj_ctr))

plt.imshow(final_image)
plt.show()

Output

Distance to first object:  35.38361202590826
Distance to second object:  35.17101079013795

enter image description here

sai
  • 1,734
  • 1
  • 7
  • 13
  • Thanks for your complete answer at first. However, in my question, the "nearer" is not to compare the distance from the center of one object to the center of the image. I want to compare the distance from the "nearest voxel" of one object to the center of the image. – Jingnan Jia Oct 10 '20 at 22:57
  • so you mean like the corner voxel? – sai Oct 10 '20 at 22:59
  • Please see my answer. – Jingnan Jia Oct 11 '20 at 14:16
0

I solved this issue.

Because the two 3D arrays are too big. So at first I down sample them to a smaller size with nearest neighbor method. then continue:

import numpy as np
from scipy import spatial
import time

sub_image1 = np.ones((30, 30, 30))
sub_image2 = np.ones((20, 10, 15))

# pad the two sub_images to same shape (1200, 1200, 1200) to simulate my 3D medical data
img_1 = np.pad(sub_image1, ((1100, 70), (1100, 70), (1100, 70)))
img_2 = np.pad(sub_image1, ((1100, 80), (1130, 60), (1170, 15)))

ori_sz = np.array(img_1.shape)
trgt_sz = ori_sz / 4
zoom_seq = np.array(trgt_sz, dtype='float') / np.array(ori_sz, dtype='float')
img_1 = ndimage.interpolation.zoom(img_1, zoom_seq, order=0, prefilter=0)
img_2 = ndimage.interpolation.zoom(img_2, zoom_seq, order=0, prefilter=0)
print("it cost this secons to downsample the nearer image" + str(time.time() - t0))  # 0.8 seconds


def nerest_dis_to_center(img):
    position = np.where(img > 0)
    coordinates = np.transpose(np.array(position))  # get the coordinates where the voxels is not 0
    cposition = np.array(img.shape) / 2  # center point position/coordinate
    distance, index = spatial.KDTree(coordinates).query(cposition)
    return distance

t1 = time.time()
d1 = nerest_dis_to_center(img_1)
d2 = nerest_dis_to_center(img_2)

if d1 > d2:
    print("img2 object is nearer")
elif d2 > d1:
    print("img1 object is nearer")
else:
    print("They are the same far")
t2 = time.time()
print("used time: ", t2-t1)
# 1.1 seconds

Jingnan Jia
  • 1,108
  • 2
  • 12
  • 28