GIMP has a convenient function that allows you to convert an arbitrary color to an alpha channel.
Essentially all pixels become transparent relative to how far away from the chosen color they are.
I want to replicate this functionality with opencv.
I tried iterating through the image:
for x in range(rows):
for y in range(cols):
mask_img[y, x][3] = cv2.norm(img[y, x] - (255, 255, 255, 255))
But this is prohibitively expensive, it takes about 10 times longer to do that iteration than it takes to simply set the field to 0 (6 minutes vs an hour)
This seems more a python problem than an algorithmic problem. I have done similar things in C++ and it's not as as bad in terms of performance.
Does anyone have suggestions on achieving this?
Here is my attempt only using numpy
matrix operations.
My input image colortrans.png
looks like this:
I want to make the diagonal purple part (128, 0, 128)
transparent with some tolerance +/- (25, 0, 25)
to the left and right, resulting in some transparency gradient.
Here comes the code:
import cv2
import numpy as np
# Input image
input = cv2.imread('images/colortrans.png', cv2.IMREAD_COLOR)
# Convert to RGB with alpha channel
output = cv2.cvtColor(input, cv2.COLOR_BGR2RGBA)
# Color to make transparent
col = (128, 0, 128)
# Color tolerance
tol = (25, 0, 25)
# Temporary array (subtract color)
temp = np.subtract(input, col)
# Tolerance mask
mask = (np.abs(temp) <= tol)
mask = (mask[:, :, 0] & mask[:, :, 1] & mask[:, :, 2])
# Generate alpha channel
temp[temp < 0] = 0 # Remove negative values
alpha = (temp[:, :, 0] + temp[:, :, 1] + temp[:, :, 2]) / 3 # Generate mean gradient over all channels
alpha[mask] = alpha[mask] / np.max(alpha[mask]) * 255 # Gradual transparency within tolerance mask
alpha[~mask] = 255 # No transparency outside tolerance mask
# Set alpha channel in output
output[:, :, 3] = alpha
# Output images
cv2.imwrite('images/colortrans_alpha.png', alpha)
cv2.imwrite('images/colortrans_output.png', output)
The resulting alpha channel colortrans_alpha.png
looks like this:
And, the final output image colortrans_output.png
looks like this:
Is that, what you wanted to achieve?
I had a go using pyvips.
This version calculates the pythagorean distance between each RGB pixel in your file and the target colour, then makes an alpha by scaling that distance metric by a tolerance.
import sys
import pyvips
image = pyvips.Image.new_from_file(sys.argv[1], access='sequential')
# Color to make transparent
col = [128, 0, 128]
# Tolerance ... ie., how close to target before we become solid
tol = 25
# for each pixel, pythagorean distance from target colour
d = sum(((image - col) ** 2).bandsplit()) ** 0.5
# scale d so that distances > tol become 255
alpha = 255 * d / tol
# attach the alpha and save
image.bandjoin(alpha).write_to_file(sys.argv[2])
On @HansHirse's nice test image:
I can run it like this:
$ ./mktrans.py ~/pics/colortrans.png x.png
To make:
To test speed, I tried on a 1920x1080 pixel jpg:
$ time ./mktrans.py ~/pics/horse1920x1080.jpg x.png
real 0m0.708s
user 0m1.020s
sys 0m0.029s
So 0.7s on this two-core 2015 laptop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With