I want to achieve the same effect as in: cv::mean for non black pixel
However I am using PIL and converting a PIL image to cv image and back is too much overhead.
I have tried using mean_color = ImageStat.Stat(img).mean
to get the mean color. However, this will include all transparent pixels too. I would like to calculate the mean of all pixels that have an alpha value above 0. So the mean over all non-completely-transparent pixels.
I am trying to keep the code nice and quick as I have to process a bunch of files. I was hoping for some built-in PIL function to do this, but couldn't find any.
It might not be the cleanest solution, but I got it to work.
def mean(rgb, a):
"""
Supply with an RGB PIL Image and Alpha Channel PIL Image.
Calculates the mean over all non-fully-transparent pixels in rgb.
"""
a_arr = np.array(a) # Convert Alpha values Image to array.
img_arr = np.array(rgb) # Convert Image RGB values to array.
mask = (a_arr > 0) # Create mask from all non-transparent pixels
stuff = img_arr[mask] # Array containing all pixels that aren't transparent
rows = len(stuff) # Get the row size.
if rows < 1: # If all pixels are transparent:
return (0, 0, 0) # The mean is simply black
cols = len(stuff[0]) # Else, continue with the size of cols
data = np.zeros([cols, rows, 3], dtype = np.uint8) # Create an array to contain the pixels
data[:] = stuff # Put the pixels with at least a > 0 into the created array.
c_img = Image.fromarray(data, 'RGB') # Convert back to RGB PIL Image
return ImageStat.Stat(c_img).mean # Calculate the mean over all pixels
Performance-wise, it was enough for my case.
About 3.44 seconds to convert about a thousand 16x16 image files. The process was:
Taking the mean then saving a Image.new('RGB', (16, 16), mean)
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.