numpy 在Python中实时将RGB转换为自定义颜色空间图像的另一种更快的方法

6ss1mwsb  于 2023-01-30  发布在  Python
关注(0)|答案(2)|浏览(180)

问题:

我已经使用一些循环定义了自己的色彩空间(黄-蓝),并希望使用一些后处理过滤器将标准HD图像从RGB实时转换为YB,但我编写的方法执行此有利任务的速度较慢。

上下文:

我想知道狗会看到什么颜色,结果发现它们分不清绿色和红色:

所以我决定定义我自己的YB色彩空间,如下图所示:

∮ ∮ ∮ ∮ ∮一个月一个月

bits = 8
values = 2 ** bits - 1
color_count = values * 6

def hues():
    lst = []
    for i in range(color_count):
        r = g = b = 0

        turn = (i // values) + 1

        if turn == 1:
            r = values
            g = i % values
            b = 0

        elif turn == 2:
            r = values - i % values
            g = values
            b = 0

        elif turn == 3:
            r = 0
            g = values
            b = i % values

        elif turn == 4:
            r = 0
            g = values - i % values
            b = values

        elif turn == 5:
            r = i % values
            g = 0
            b = values

        elif turn == 6:
            r = values
            g = 0
            b = values - i % values

        r = round(r / values * 255)
        g = round(g / values * 255)
        b = round(b / values * 255)

        lst.append((r, g, b))

    return lst

def dues():
    lst = []
    for i in range(color_count):
        r = g = b = 0

        turn = (i // values) + 1

        if turn == 1:
            r = values
            g = values
            b = round((values - i % values) / 2)

        elif turn == 2:
            r = values
            g = values
            b = round((i % values) / 2)

        elif turn == 3:
            if i % values < values / 2:
                r = values
                g = values
                b = round((values / 2 + i % values))
            else:
                r = round((3 / 2 * values - i % values))
                g = round((3 / 2 * values - i % values))
                b = values

        elif turn == 4:
            r = round((values - i % values) / 2)
            g = round((values - i % values) / 2)
            b = values

        elif turn == 5:
            r = round((i % values) / 2)
            g = round((i % values) / 2)
            b = values

        elif turn == 6:
            if i % values < values / 2:
                r = round((values / 2 + i % values))
                g = round((values / 2 + i % values))
                b = values
            else:
                r = values
                g = values
                b = round((3 / 2 * values - i % values))

        r = round(r / values * 255)
        g = round(g / values * 255)
        b = round(b / values * 255)

        lst.append((r, g, b))

    return lst

def rgb_to_hsl(color: tuple):
    r, g, b = color

    r /= 255
    g /= 255
    b /= 255

    cmax = max(r, g, b)
    cmin = min(r, g, b)
    delta = cmax - cmin

    h = 0
    l = (cmax + cmin) / 2

    if delta == 0:
        h = 0
    elif cmax == r:
        h = ((g - b) / delta) % 6
    elif cmax == g:
        h = ((b - r) / delta) + 2
    elif cmax == b:
        h = ((r - g) / delta) + 4

    h *= 60

    if delta == 0:
        s = 0
    else:
        s = delta / (1 - abs(2 * l - 1))

    return h, s, l

def hsl_to_rgb(color: tuple):
    h, s, l = color

    c = (1 - abs(2 * l - 1)) * s
    x = c * (1 - abs((h / 60) % 2 - 1))
    m = l - c / 2

    r = g = b = 0

    if 0 <= h < 60:
        r = c
        g = x
    elif 60 <= h < 120:
        r = x
        g = c
    elif 120 <= h < 180:
        g = c
        b = x
    elif 180 <= h < 240:
        g = x
        b = c
    elif 240 <= h < 300:
        r = x
        b = c
    elif 300 <= h < 360:
        r = c
        b = x

    r = round((r + m) * 255)
    g = round((g + m) * 255)
    b = round((b + m) * 255)

    return r, g, b

在保存列表值时,我获得了预期的色调:

现在,主要处理包括按以下顺序逐个像素地转换颜色:
1.获取RGB

  1. RGB--〉HSL
    1.将色调值更改为dues_hsl列表中的相应值
    1.新建HSL--〉RGB
    1.在另一个数组中的相同坐标处设置新的RGB值
    对图像中的每个像素重复该过程,并且在尺寸为481 x 396像素的测试图像上花费大约58秒

输入和输出:

一个四个一个一个一个五个一个
相同代码:

defining.py

from PIL import Image
import numpy as np
from calculating import hues, dues
from calculating import rgb_to_hsl as hsl
from calculating import hsl_to_rgb as rgb

hues = hues()
dues = dues()

# Hues = human hues
# Dues = dog hues

hues_hsl = [hsl(i) for i in hues]
dues_hsl = [hsl(i) for i in dues]

img = np.array(Image.open('dog.png').convert('RGB'))

arr_blank = np.zeros(img.shape[0:3])
print(arr_blank.shape)
print(img.shape[0:3])

total = img.shape[0] * img.shape[1]

for i in range(img.shape[0]):
    for j in range(img.shape[1]):
        hsl_val = hsl(tuple(img[i, j]))
        h = dues_hsl[hues_hsl.index(min(hues_hsl, key=lambda x: abs(x[0] - hsl_val[0])))][0]
        pixel = np.array(rgb((h, hsl_val[1], hsl_val[2])))
        arr_blank[i, j, :] = pixel

        print(f'{i * img.shape[1] + j} / {total}  ---  {(i * img.shape[1] + j)/total*100} %')

print(arr_blank)
data = Image.fromarray(arr_blank.astype('uint8'), 'RGB')
data.save('dog_color.png')

结论:

在这之后,我想添加一个高斯模糊过滤器,实时转换后,但这是需要很长的时间才一帧。有什么办法可以提高速度?

机器信息:

如果此信息有帮助:i7 - 10750H@2.6Ghz,固态硬盘,16 GB内存
谢谢!

x7rlezfr

x7rlezfr1#

我忘了枕头也做HSV一样好,所以不需要OpenCV。
这在我的机器上执行大约0.45秒。

from PIL import Image
import numpy as np

values = 2 ** 8 - 1
color_count = values * 6

def dog_hues():
    # ... from original post, removed for brevity...
    return lst

# Convert the dog_hues() list into an image of size 256x1
hue_map_img = Image.new("RGB", (color_count, 1))
hue_map_img.putdata(dog_hues())
hue_map_img = hue_map_img.resize((256, 1), Image.LANCZOS)
# Get the hues out of it
hsv_array = np.array(hue_map_img.convert("HSV"))
hue_map = hsv_array[:, :, 0].flatten()
# Read in the dog, convert it to HSV
img = np.array(Image.open("dog.jpg").convert("HSV"))
# Remap hue
img[:, :, 0] = hue_map[img[:, :, 0]]
# Convert back to RGB and save
img = Image.fromarray(img, "HSV").convert("RGB")
img.save("dog_hsv.jpg")
5kgi1eie

5kgi1eie2#

第1条备注:你不能像这样改变色彩空间。因为当你看到一种颜色时,人眼会解读它(因此被人类RGB图像格式)显示为黄色,例如(255,255,0),你不知道它是由黄色频率组成的(例如570 nm)激发我们的红色和绿色视锥,但不是蓝色的。如果它是由红色频率的混合物(例如690 nm)和绿色频率(530 nm),或者导致相同的红色和绿色锥体饱和(255,255)和蓝色锥体未被触摸(0)的任何其他光谱。
你需要这些信息来推断这两个狗锥是如何受到影响的。
换句话说,人的颜色和狗的颜色之间没有任何Map,用数学的话来说,真实的颜色空间(∞维,一个光谱)和人的颜色空间(3D,简化为:在真实的颜色空间和狗颜色空间(2D,也是为了简化)之间存在另一投影。但是那些投影轴不包括在另一个中。因此,在3D人类颜色空间和2D狗颜色空间之间不存在任何投影。仅利用人类如何看到颜色的知识,无法知道狗如何看到颜色;你需要知道真实的的颜色。你可以用超光谱相机来做这件事(并且做两个投影来计算人类的rgb图像和狗的yb图像)。这是假设相当天真的(但在第一近似中是正确的)想法,即这些颜色遵循初等大学水平的线性代数,而实际上,它并不完全如此。
也就是说,基于PIL或OpenCV的解决方案是一种解决方案,但更一般地说,如果你不信任PIL或OpenCV,或任何现有的库颜色模型,并真的想发明你的轮子(我尊重这一点;没有更好的方法来理解事物,那就是重新发明轮子),那么你必须遵守的一条规则就是 * 永远不要在像素上迭代 *。如果你这样做,你就失去了性能匹配。Python非常非常慢。它仍然是一种流行语言的唯一原因,以及为什么仍然有用Python制作的快速程序,是因为python编码器做了它所需要的任何事情,所以计算繁重的循环(在图像处理中,那些是像素上的循环)并不是真的在python中制作的。
所以你必须依靠numpy来对所有像素执行操作,而不是自己编写for循环。
例如,这里重写了rgb_to_hsl,用numpy进行批量计算,也就是说,rgb_to_hsl不是用单一颜色调用的,而是用整个颜色数组(2d数组)调用的,也就是一个图像

def rgb_to_hsl(image):
    # rgb is the r,g,b channels between 0 and 1 (as you did for individual
    # r,g,b variables, but it is easier (see below) to keep them as a single 
    # array. Rgb is not just a triplet (unlike your r,g,b) but a 2d-array of
    # triplets (so a 3d-array)
    rgb = image/255

    # Likewise, cmax, cmin, delta are not scalar as in your code, but 
    # 2d array of such scalar
    cmax = rgb.max(axis=2) # axis=2 means that axis 0 and 1 are kept, and max 
                           # is computed along axis 2, that is along the 3 
                           # values of each triplets. So rgb is a HxWx3 
                           # 3d-array (axis 0 = y, axis 1=x, axis 2=color 
                           # channel). cmax is a HxW 2d-array
    cmin = rgb.min(axis=2) # likewise
    delta = cmax - cmin    # same code. But this is done on all HxW cmax and cmin

    h = zeros_like(delta) # 2d-array of 0 
    l = (cmax + cmin) / 2 # 2d array of (cmax+cmin)/2

    # Here come a trickier part. We need to separate cases, and do computation
    # in each subsets concerning those cases
    case1= delta==0
    h[case1] = 0 # In reality, we could skip those, since h is already 0 everywhere

    case2 = cmax==r
    h[case2] = (rgb[case2,1]-rgb[case2,2])/delta[case2] % 6

    case3 = cmax==g
    h[case3] = (rgb[case3,2]-rgb[case3,0])/delta[case3] + 2

    case4 = cmax==b
    h[case4] = (rgb[case4,0]-rgb[case4,1])/delta[case4] + 4

    h *= 60 # Same code, applies on all HxW values of h

    s=np.zeros_like(h)
    s[case1] = 0  # same remark. I just mimick your code as much as possible
                  # but that is already the default value
    s[~case1] = delta[~case1] / (1-abs(2*l[~case1]-1))
    # ~case1 is the opposite of case1. So, equivalent of the else in your code

    # returns 3 2d HxW arrays for h, s and l
    return h, s, l

相关问题