1

I am trying to identify connected regions of pixels in an image stack. Since it is a stack, the input is quite large (on the order of 10 million pixels, although only about 1 million are bright), and looping through it pixel by pixel is extremely slow. Nonetheless, my first attempt works as follows:

1) Check if current pixel is bright, if so go to 2. If not go to 4.

2) Obtain slice of image containing all 26 neighbors. Set all neighbors that have not yet been scanned to be dark. Set all neighbors that fall outside of the image (e.g. indices of -1) to be dark. Set current pixel to dark.

3) If all pixels in slice are dark, assign current pixel a new label. If exactly one pixel in slice is bright, set current pixel label to be equal to this bright pixel's label. If more than one pixel is bright, set current pixel to lowest of bright pixel labels, and record the equivalence of these labels.

4) Advance one row. Once all rows in the column are scanned, advance one column. Once every pixel is in the slice is scanned, advance one slice in the stack. Once the entire image is scanned, advance to 5.

5) Loop through the list of equivalencies, reassigning labels in the output.

I don't think the processing in steps 2 and 3 are much slower than other, similar algorithms, but I could be wrong. I think the major bottleneck is simply looping through each pixel. I know the bottleneck is not #5 because I have never been patient enough to reach that step. Even just looping over the image indices with a print statement is quite slow. I know that with the same data I can do this analysis quickly, so I suspect there must be a smarter way to do this with a very different approach. I have read something vague about performing dilation and using intersections, but I can't imagine what the mechanism they were alluding to was.

Is there something fast and clever out there, or is this basically the only way to do it? How is the MATLAB algorithm so much faster?

for i in xrange(0,nPix):
for j in xrange(0,nPix):
    for k in xrange(0,nFrames):
        if img[i,j,k] != 0: #If the current pixel is bright
            #Construct an array of already scanned
            #neighbors. Pixels are scanned first in z, then y,
            #then x.
            #Construct indices allowing for periodic boundary
            ir = np.array(range(i-1,i+2)).reshape(-1,1,1)%nPix
            jr = np.array(range(j-1,j+2)).reshape(1,-1,1)%nPix
            kr = np.array(range(k-1,k+2)).reshape(1,1,-1)%nFrames
            pastNeighbors = img[ir,jr,kr] #Includes i-1:i+1
            #Set all indices outside image and "future neighbors" to 0 
            pastNeighbors[2,:,:] = 0
            pastNeighbors[1,2,:] = 0
            pastNeighbors[1,1,2] = 0
            pastNeighbors[1,1,1] = 0 #Pixel itself; not a neighbor
            #Eliminate periodic boundary
            if i-1 < 0: pastNeighbors[0,:,:] = 0
            if i+1 == nPix: pastNeighbors[2,:,:] = 0
            if j-1 < 0: pastNeighbors[:,0,:] = 0
            if j+1 == nPix: pastNeighbors[:,2,:] = 0
            if k-1 < 0: pastNeighbors[:,:,0] = 0
            if k+1 == nFrames: pastNeighbors[:,:,2] = 0
            if np.count_nonzero(pastNeighbors) == 0: #If all dark neighbors
                label = label + 1 #Assign new label to pixel
                out[i,j,k] = label
            elif np.count_nonzero(pastNeighbors) == 1: #One bright neighbor
                relX, relY, relZ = np.nonzero(pastNeighbors)
                relX = relX - 1
                relY = relY - 1
                relZ = relZ - 1
                out[i,j,k] = out[i + relX, j + relY, k + relZ]
            else: #Mutliple bright neighbors
                relX, relY, relZ = np.nonzero(pastNeighbors)
                relX = relX - 1
                relY = relY - 1
                relZ = relZ - 1
                equiv = out[i + relX, j + relY, k + relZ]
                out[i, j, k] = np.min(equiv) #Assign one of the
                                             #multiple labels
                for i in xrange(0,equiv.size):
                    equivList = np.append(equivList, [[np.min(equiv),equiv[i]]], axis = 0)
Dylan B
  • 173
  • 1
  • 1
  • 13
  • 1
    I don't understand your algorithm description. Why are you changing pixel colours during labeling? – Daniel Nov 20 '13 at 18:24
  • I am not. For convenience in my code, I take a slice of the image containing only the neighbors. To figure out the label for the current pixel, the value of pixels I haven't yet labeled is irrelevant, therefore I eliminate them from the slice of neighbors. No modification is made to the image itself at any point. There are other ways to do it, such as explicitly only examining already labeled neighbors, but I found this to be less cumbersome to code/debug. – Dylan B Nov 20 '13 at 19:56
  • @EMS: I don't also don't know what bad habits I am demonstrating here. It would be helpful if you could expand upon that. This algorithm _works_, it just does not work _quickly_. I also suspect that the MATLAB algorithm, whatever it is (they do not disclose the algorithms for most of their native routines), is more efficient due to vectorization. This is exactly what I'm looking for help with. Simply saying "your code is bad" is not helpful. I already know my code is bad. – Dylan B Nov 20 '13 at 23:33

0 Answers0