在python中有效地将颜色转换为透明度

问题描述 投票:2回答:3

GIMP具有方便的功能,允许您将任意颜色转换为Alpha通道。

基本上所有像素相对于它们所选颜色的距离变得透明。

我想用opencv复制这个功能。

我尝试迭代图像:

    for x in range(rows):
        for y in range(cols):
            mask_img[y, x][3] = cv2.norm(img[y, x] - (255, 255, 255, 255))

但这非常昂贵,迭代所需的时间比将字段设置为0(6分钟与一小时)相比需要大约10倍

这似乎是一个python问题而不是算法问题。我在C ++中做过类似的事情,但在性能方面并没有那么糟糕。

有没有人有这方面的建议?

python image opencv image-processing computer-vision
3个回答
4
投票

这是我尝试仅使用numpy矩阵运算。

我的输入图像colortrans.png看起来像这样:

Input image

我想使对角紫色部分(128, 0, 128)透明,左右两侧有一些公差+/- (25, 0, 25),产生一些透明度渐变。

代码如下:

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)

得到的alpha通道colortrans_alpha.png看起来像这样:

Alpha channel

并且,最终输出图像colortrans_output.png看起来像这样:

Output image

那是你想要实现的目标吗?


2
投票

我做了一个项目,使用PIL(python图像库)模块将所有接近白色的像素转换为透明像素。我不确定如何实现你的算法“相对于它们离选择的颜色有多远”,但我的代码看起来像:

from PIL import Image

planeIm = Image.open('InputImage.png')
planeIm = planeIm.convert('RGBA')
datas = planeIm.getdata()

newData = []
for item in datas:
    if item[0] > 240 and item[1] > 240 and item[2] > 240:
        newData.append((255, 255, 255, 0)) # transparent pixel
    else:
        newData.append(item) # unedited pixel
planeIm.putdata(newData)
planeIm.save('output.png', "PNG")

这是在1.605秒内为我拍摄的1920 X 1080图像,所以如果你将逻辑实现到这里,你会看到你想要的速度改进吗?

如果newData被初始化而不是每次都被.append()ed可能会更快!就像是:

planeIm = Image.open('EGGW spider.png')
planeIm = planeIm.convert('RGBA')
datas = planeIm.getdata()

newData = [(255, 255, 255, 0)] * len(datas)
for i in range(len(datas)):
    if datas[i][0] > 240 and datas[i][1] > 240 and datas[i][2] > 240:
        pass # we already have (255, 255, 255, 0) there
    else:
        newData[i] = datas[i]
planeIm.putdata(newData)
planeIm.save('output.png', "PNG")

虽然对我来说这第二种方法运行在2.067秒......

multithreading

计算不同图像的线程示例如下所示:

from PIL import Image
from threading import Thread
from queue import Queue
import time

start = time.time()
q = Queue()

planeIm = Image.open('InputImage.png')
planeIm = planeIm.convert('RGBA')
datas = planeIm.getdata()
new_data = [0] * len(datas)

print('putting image into queue')
for count, item in enumerate(datas):
    q.put((count, item))

def worker_function():
    while True:
        # print("Items in queue: {}".format(q.qsize()))
        index, pixel = q.get()
        if pixel[0] > 240 and pixel[1] > 240 and pixel[2] > 240:
            out_pixel = (0, 0, 0, 0)
        else:
            out_pixel = pixel
        new_data[index] = out_pixel
        q.task_done()

print('starting workers')
worker_count = 100
for i in range(worker_count):
    t = Thread(target=worker_function)
    t.daemon = True
    t.start()
print('main thread waiting')
q.join()
print('Queue has been joined')
planeIm.putdata(new_data)
planeIm.save('output.png', "PNG")

end = time.time()

elapsed = end - start
print('{:3.3} seconds elapsed'.format(elapsed))

对我来说现在需要58.1秒!一个可怕的速度差异!我认为这是:

  • 必须迭代每个像素两次,一次将其放入队列并一次处理它并将其写入new_data列表。
  • 创建线程所需的开销。每个新线程都需要几毫秒来创建,因此大量(在这种情况下为100)可以加起来。
  • 一个简单的算法用于修改像素,当每个输入需要大量计算时线程会闪耀(更像你的情况)
  • 线程不使用多个内核,你需要多处理来实现这一点 - >我的任务管理器说我只使用了10%的CPU并且它已经占据了1-2%......

1
投票

我去了pyvips

此版本计算文件中每个RGB像素与目标颜色之间的毕达哥拉斯距离,然后通过按公差缩放该距离度量来生成alpha。

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])

关于@ HansHirse的漂亮测试图像:

enter image description here

我可以像这样运行它:

$ ./mktrans.py ~/pics/colortrans.png x.png

要做:

enter image description here

为了测试速度,我尝试了一个1920x1080像素的jpg:

$ time ./mktrans.py ~/pics/horse1920x1080.jpg x.png
real    0m0.708s
user    0m1.020s
sys 0m0.029s

这款双核2015笔记本电脑的尺寸为0.7秒。

© www.soinside.com 2019 - 2024. All rights reserved.