I have an image that contains black pixels. It can be vertical lines but also simple points. I would like to replace these pixels with the average of neighboring pixels (left and right).
The left and right neighbors of a black pixel all have a different value from black.
For now I have this:
import numpy as np
from matplotlib import pyplot as plt
import time
#Creating test img
test_img = np.full((2048, 2048, 3), dtype = np.uint8, fill_value = (255,127,127))
#Draw vertical black line
test_img[500:1000,1500::12] = (0,0,0)
test_img[1000:1500,1000::24] = (0,0,0)
#Draw black point
test_img[250,250] = (0,0,0)
test_img[300,300] = (0,0,0)
#Fill hole functions
def fill_hole(img):
#Find coords of black pixek
imggray = img[:,:,0]
coords = np.column_stack(np.where(imggray < 1))
print(len(coords))
#Return if no black pixel
if len(coords) == 0:
return img
percent_black = len(coords)/(img.shape[0]*img.shape[1]) * 100
print(percent_black)
#Make a copy of input image
out = np.copy(img)
#Iterate on all black pixels
for p in coords:
#Edge management
if p[0] < 1 or p[0] > img.shape[0] - 1 or p[1] < 1 or p[1] > img.shape[1] - 1:
continue
#Get left and right of each pixel
left = img[p[0], p[1] - 1]
right = img[p[0], p[1] 1]
#Get new pixel value
r = int((int(left[0]) int(right[0])))/2
g = int((int(left[1]) int(right[1])))/2
b = int((int(left[2]) int(right[2])))/2
out[p[0],p[1]] = [r,g,b]
return out
#Function call
start = time.time()
img = fill_hole(test_img)
end = time.time()
print(end - start)
This code works fine on my example but the loop over the list of black pixels takes time depending on its size.
Is there a way to optimize this?
CodePudding user response:
In general, for loops on numpy arrays usually cause slowing things, and in most of cases can be avoided by numpy built-in functions. In your case, consider using convolution on the image, see as a reference: Python get get average of neighbours in matrix with na value
CodePudding user response:
You always apply the same operation on a black pixel. So it's highly parallelizable . Divide your image in smaller rectangles and put threads and/or processes to act on each small rectangle. You can try to tweak the rectangle size to get the best performance.
Also, since your black pixels structure is very particular, you can implement some strategy to avoid checking some image regions that you are sure have no black pixels. An idea can be to check, for each image column, some random pixels in the beginning, middle and end of the column. Then, ignore the column if you find no black pixels in the check.