3
\$\begingroup\$

I wrote an adaptive color thresholding function in Python (because OpenCV's cv2.adaptiveThreshold didn't fit my needs) and it is way too slow. I've made it as efficient as I can, but it still takes almost 500 ms on a 1280x720 image. I would greatly appreciate any suggestions that will make this function more efficient!

Here's what the function does:

It uses a cross-shape of one-pixel thickness as the structuring element. For each pixel in the image, it computes the average values of ksize adjacent pixels in four directions independently (i.e. the average of ksize pixels in the same row to the left, in the same column above, in the same row to the right, and in the same column below). I end with four average values, one for each direction. A pixel only meets the threshold criterion if it is brighter than either both the left AND right averages or both the top AND bottom averages (plus some constant C).

I compute those averages incrementally for all pixels at the same time using numpy.roll(), but I still need to do this ksize times. The ksize will usually be 20-50.

Here is the code, the relevant part is really just what happens inside the for-loop:

def bilateral_adaptive_threshold(img, ksize=20, C=0, mode='floor', true_value=255, false_value=0):
 mask = np.full(img.shape, false_value, dtype=np.int16)
 left_thresh = np.zeros_like(img, dtype=np.float32) #Store the right-side average of each pixel here
 right_thresh = np.zeros_like(img, dtype=np.float32) #Store the left-side average of each pixel here
 up_thresh = np.zeros_like(img, dtype=np.float32) #Store the top-side average of each pixel here
 down_thresh = np.zeros_like(img, dtype=np.float32) #Store the bottom-side average of each pixel here
 for i in range(1, ksize+1): 
 roll_left = np.roll(img, -i, axis=1)
 roll_right = np.roll(img, i, axis=1)
 roll_up = np.roll(img, -i, axis=0)
 roll_down = np.roll(img, i, axis=0)
 roll_left[:,-i:] = 0
 roll_right[:,:i] = 0
 roll_up[-i:,:] = 0
 roll_down[:i,:] = 0
 left_thresh += roll_right
 right_thresh += roll_left
 up_thresh += roll_down
 down_thresh += roll_up
 left_thresh /= ksize
 right_thresh /= ksize
 up_thresh /= ksize
 down_thresh /= ksize
 if mode == 'floor':
 mask[((img > left_thresh+C) & (img > right_thresh+C)) | ((img > up_thresh+C) & (img > down_thresh+C))] = true_value
 elif mode == 'ceil':
 mask[((img < left_thresh-C) & (img < right_thresh-C)) | ((img < up_thresh-C) & (img < down_thresh-C))] = true_value
 else: raise ValueError("Unexpected mode value. Expected value is 'floor' or 'ceil'.")
 return mask
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Mar 1, 2017 at 22:47
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

I found a solution using cv2.filter2D that cuts the time to 25% of the original time:

def bilateral_adaptive_threshold_f2d(img, ksize=30, C=0, mode='floor', true_value=255, false_value=0):
 mask = np.full(img.shape, false_value, dtype=np.int16)
 left_thresh = cv2.filter2D(img, ddepth=cv2.CV_32F, kernel=np.ones((1,ksize+1)), anchor=(ksize,0), delta=0, borderType=cv2.BORDER_CONSTANT)
 right_thresh = cv2.filter2D(img, ddepth=cv2.CV_32F, kernel=np.ones((1,ksize+1)), anchor=(0,0), delta=0, borderType=cv2.BORDER_CONSTANT)
 up_thresh = cv2.filter2D(img, ddepth=cv2.CV_32F, kernel=np.ones((ksize+1,1)), anchor=(0,ksize), delta=0, borderType=cv2.BORDER_CONSTANT)
 down_thresh = cv2.filter2D(img, ddepth=cv2.CV_32F, kernel=np.ones((ksize+1,1)), anchor=(0,0), delta=0, borderType=cv2.BORDER_CONSTANT) 
 left_thresh /= ksize
 right_thresh /= ksize
 up_thresh /= ksize
 down_thresh /= ksize
 if mode == 'floor':
 mask[((img > left_thresh+C) & (img > right_thresh+C)) | ((img > up_thresh+C) & (img > down_thresh+C))] = true_value
 elif mode == 'ceil':
 mask[((img < left_thresh-C) & (img < right_thresh-C)) | ((img < up_thresh-C) & (img < down_thresh-C))] = true_value
 else: raise ValueError("Unexpected mode value. Expected value is 'floor' or 'ceil'.")
 return mask

Also, see the amazing response to this post for more details: https://stackoverflow.com/questions/42540173/python-how-to-make-this-color-thresholding-function-more-efficient

answered Mar 2, 2017 at 2:06
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.