将RGB数组转换为HSL

4
首先声明,我不是很擅长Python,你们有我的钦佩。
我的问题: 我需要从模板(128px x 128px)生成10k+张图像,具有各种色调和亮度。
我加载这些图像并将它们转化为数组。
image = Image.open(dir + "/" + file).convert('RGBA')
arr=np.array(np.asarray(image).astype('float'))

据我所知,以这种方式处理numpy数组比循环每个像素并使用colorsys要快得多。

现在,我发现了几个将RGB转换为HSV的函数。 这帮助我生成了具有不同色调的图像,但我还需要调整亮度,使一些变成黑色,其他变成白色。

def rgb_to_hsv(rgb):
    # Translated from source of colorsys.rgb_to_hsv
    hsv=np.empty_like(rgb)
    hsv[...,3:]=rgb[...,3:]
    r,g,b=rgb[...,0],rgb[...,1],rgb[...,2]
    maxc = np.max(rgb[...,:2],axis=-1)
    minc = np.min(rgb[...,:2],axis=-1)    
    hsv[...,2] = maxc   
    hsv[...,1] = (maxc-minc) / maxc
    rc = (maxc-r) / (maxc-minc)
    gc = (maxc-g) / (maxc-minc)
    bc = (maxc-b) / (maxc-minc)
    hsv[...,0] = np.select([r==maxc,g==maxc],[bc-gc,2.0+rc-bc],default=4.0+gc-rc)
    hsv[...,0] = (hsv[...,0]/6.0) % 1.0
    idx=(minc == maxc)
    hsv[...,0][idx]=0.0
    hsv[...,1][idx]=0.0
    return hsv

def hsv_to_rgb(hsv):
    # Translated from source of colorsys.hsv_to_rgb
    rgb=np.empty_like(hsv)
    rgb[...,3:]=hsv[...,3:]    
    h,s,v=hsv[...,0],hsv[...,1],hsv[...,2]   
    i = (h*6.0).astype('uint8')
    f = (h*6.0) - i
    p = v*(1.0 - s)
    q = v*(1.0 - s*f)
    t = v*(1.0 - s*(1.0-f))
    i = i%6
    conditions=[s==0.0,i==1,i==2,i==3,i==4,i==5]
    rgb[...,0]=np.select(conditions,[v,q,p,p,t,v],default=v)
    rgb[...,1]=np.select(conditions,[v,v,v,q,p,p],default=t)
    rgb[...,2]=np.select(conditions,[v,p,t,v,v,q],default=p) 
    return rgb

将这些函数修改为转换到和从HSL的功能有多容易?转换HSV到HSL的任何技巧吗?

非常感谢您能提供的任何信息,谢谢!

3个回答

2
是的,即向量化代码的numpy可以加速颜色转换。
此外,对于生产量大于10k个位图的情况,您可能需要重用现成的专业转换或对其进行子类化,如果它不完全符合您的首选亮度模型。
计算机视觉库OpenCV,目前可用于python作为cv2模块,可以处理颜色系统转换,无需任何额外的编码,只需使用一个现成的转换一行代码。
out = cv2.cvtColor(   anInputFRAME, cv2.COLOR_YUV2BGR ) # a bitmap conversion

以下是cv2中可用的一些颜色系统列表(您可能会注意到由于OpenCV约定图像的不同顺序,RGB被称为BRG,即Blue-Red-Green颜色平面),
(对称性适用于COLOR_YCR_CB2BGR<-|->COLOR_BGR2YCR_CB,但并非所有配对都显示)。
>>> import cv2
>>> for key in dir( cv2 ):                              # show all ready conversions
...     if key[:7] == 'COLOR_Y':
...         print key

COLOR_YCR_CB2BGR
COLOR_YCR_CB2RGB
COLOR_YUV2BGR
COLOR_YUV2BGRA_I420
COLOR_YUV2BGRA_IYUV
COLOR_YUV2BGRA_NV12
COLOR_YUV2BGRA_NV21
COLOR_YUV2BGRA_UYNV
COLOR_YUV2BGRA_UYVY
COLOR_YUV2BGRA_Y422
COLOR_YUV2BGRA_YUNV
COLOR_YUV2BGRA_YUY2
COLOR_YUV2BGRA_YUYV
COLOR_YUV2BGRA_YV12
COLOR_YUV2BGRA_YVYU
COLOR_YUV2BGR_I420
COLOR_YUV2BGR_IYUV
COLOR_YUV2BGR_NV12
COLOR_YUV2BGR_NV21
COLOR_YUV2BGR_UYNV
COLOR_YUV2BGR_UYVY
COLOR_YUV2BGR_Y422
COLOR_YUV2BGR_YUNV
COLOR_YUV2BGR_YUY2
COLOR_YUV2BGR_YUYV
COLOR_YUV2BGR_YV12
COLOR_YUV2BGR_YVYU
COLOR_YUV2GRAY_420
COLOR_YUV2GRAY_I420
COLOR_YUV2GRAY_IYUV
COLOR_YUV2GRAY_NV12
COLOR_YUV2GRAY_NV21
COLOR_YUV2GRAY_UYNV
COLOR_YUV2GRAY_UYVY
COLOR_YUV2GRAY_Y422
COLOR_YUV2GRAY_YUNV
COLOR_YUV2GRAY_YUY2
COLOR_YUV2GRAY_YUYV
COLOR_YUV2GRAY_YV12
COLOR_YUV2GRAY_YVYU
COLOR_YUV2RGB
COLOR_YUV2RGBA_I420
COLOR_YUV2RGBA_IYUV
COLOR_YUV2RGBA_NV12
COLOR_YUV2RGBA_NV21
COLOR_YUV2RGBA_UYNV
COLOR_YUV2RGBA_UYVY
COLOR_YUV2RGBA_Y422
COLOR_YUV2RGBA_YUNV
COLOR_YUV2RGBA_YUY2
COLOR_YUV2RGBA_YUYV
COLOR_YUV2RGBA_YV12
COLOR_YUV2RGBA_YVYU
COLOR_YUV2RGB_I420
COLOR_YUV2RGB_IYUV
COLOR_YUV2RGB_NV12
COLOR_YUV2RGB_NV21
COLOR_YUV2RGB_UYNV
COLOR_YUV2RGB_UYVY
COLOR_YUV2RGB_Y422
COLOR_YUV2RGB_YUNV
COLOR_YUV2RGB_YUY2
COLOR_YUV2RGB_YUYV
COLOR_YUV2RGB_YV12
COLOR_YUV2RGB_YVYU
COLOR_YUV420P2BGR
COLOR_YUV420P2BGRA
COLOR_YUV420P2GRAY
COLOR_YUV420P2RGB
COLOR_YUV420P2RGBA
COLOR_YUV420SP2BGR
COLOR_YUV420SP2BGRA
COLOR_YUV420SP2GRAY
COLOR_YUV420SP2RGB
COLOR_YUV420SP2RGBA

我为亮度转换进行了一些原型制作(基于 >>> http://en.wikipedia.org/wiki/HSL_and_HSV )。

但未经过发布测试。

def        get_YUV_V_Cr_Rec601_BRG_frame( brgFRAME ):                   # For the Rec. 601 primaries used in gamma-corrected sRGB, fast, VECTORISED MUL/ADD CODE
    out =  numpy.zeros( brgFRAME.shape[0:2] )
    out += 0.615 / 255 * brgFRAME[:,:,1]    # // Red                    # normalise to <0.0 - 1.0> before vectorised MUL/ADD, saves [usec] ... on 480x640 [px] faster goes about 2.2 [msec] instead of 5.4 [msec]
    out -= 0.515 / 255 * brgFRAME[:,:,2]    # // Green
    out -= 0.100 / 255 * brgFRAME[:,:,0]    # // Blue                   # normalise to <0.0 - 1.0> before vectorised MUL/ADD
    return out

谢谢您的回答。请原谅我的无知,但是我提供给brgFRAME的值是我的图像numpy数组吗?我得到了ValueError:缓冲区不够大的错误提示。 - NodeNodeNode
brgFRAME是通过对cv2.VideoCapture(0)实例调用.read()方法获取的,但它返回一个**numpy.array()**实例。你的<image>.shape.size.dtype是什么? - user3666197
这是我目前的代码:http://pastebin.com/dvQHhv5q 这些模板是带有一些透明度的png24格式。 - NodeNodeNode
你追踪到了哪一行代码引发了 ValueError 异常? - user3666197
我在使用你的get_YUV_V_Cr_Rec601_BRG_frame方法时出现了错误,而不是在第51行使用cv2.cvtColor。现在,我在第51行使用cv2.cvtColor时也遇到了错误,错误信息为“OpenCV错误:在cvtColor中断言失败(深度== CV_8U ||深度== CV_16U ||深度== CV_32F),文件/build/buildd/opencv-2.4.8+dfsg1/modules/imgproc/src/color.cpp,行3642”。 - NodeNodeNode
谢谢您的帮助,我会尝试另一种方法来解决我的问题。 - NodeNodeNode

0
# -*- coding: utf-8 -*-
# @File    : rgb2hls.py
# @Info    : @ TSMC
# @Desc    :


import colorsys

import numpy as np
import scipy.misc
import tensorflow as tf
from PIL import Image


def rgb2hls(img):
    """ note: elements in img is a float number less than 1.0 and greater than 0.
    :param img: an numpy ndarray with shape NHWC
    :return:
    """
    assert len(img.shape) == 3
    hue = np.zeros_like(img[:, :, 0])
    luminance = np.zeros_like(img[:, :, 0])
    saturation = np.zeros_like(img[:, :, 0])
    for x in range(height):
        for y in range(width):
            r, g, b = img[x, y]
            h, l, s = colorsys.rgb_to_hls(r, g, b)
            hue[x, y] = h
            luminance[x, y] = l
            saturation[x, y] = s
    return hue, luminance, saturation


def np_rgb2hls(img):
    r, g, b = img[:, :, 0], img[:, :, 1], img[:, :, 2]

    maxc = np.max(img, -1)
    minc = np.min(img, -1)
    l = (minc + maxc) / 2.0
    if np.array_equal(minc, maxc):
        return np.zeros_like(l), l, np.zeros_like(l)
    smask = np.greater(l, 0.5).astype(np.float32)

    s = (1.0 - smask) * ((maxc - minc) / (maxc + minc)) + smask * ((maxc - minc) / (2.001 - maxc - minc))
    rc = (maxc - r) / (maxc - minc + 0.001)
    gc = (maxc - g) / (maxc - minc + 0.001)
    bc = (maxc - b) / (maxc - minc + 0.001)

    rmask = np.equal(r, maxc).astype(np.float32)
    gmask = np.equal(g, maxc).astype(np.float32)
    rgmask = np.logical_or(rmask, gmask).astype(np.float32)

    h = rmask * (bc - gc) + gmask * (2.0 + rc - bc) + (1.0 - rgmask) * (4.0 + gc - rc)
    h = np.remainder(h / 6.0, 1.0)
    return h, l, s


def tf_rgb2hls(img):
    """ note: elements in img all in [0,1]
    :param img: a tensor with shape NHWC
    :return:
    """
    assert img.get_shape()[-1] == 3
    r, g, b = img[:, :, 0], img[:, :, 1], img[:, :, 2]
    maxc = tf.reduce_max(img, -1)
    minc = tf.reduce_min(img, -1)

    l = (minc + maxc) / 2.0

    # if tf.reduce_all(tf.equal(minc, maxc)):
    #     return tf.zeros_like(l), l, tf.zeros_like(l)
    smask = tf.cast(tf.greater(l, 0.5), tf.float32)

    s = (1.0 - smask) * ((maxc - minc) / (maxc + minc)) + smask * ((maxc - minc) / (2.001 - maxc - minc))
    rc = (maxc - r) / (maxc - minc + 0.001)
    gc = (maxc - g) / (maxc - minc + 0.001)
    bc = (maxc - b) / (maxc - minc + 0.001)

    rmask = tf.equal(r, maxc)
    gmask = tf.equal(g, maxc)
    rgmask = tf.cast(tf.logical_or(rmask, gmask), tf.float32)
    rmask = tf.cast(rmask, tf.float32)
    gmask = tf.cast(gmask, tf.float32)

    h = rmask * (bc - gc) + gmask * (2.0 + rc - bc) + (1.0 - rgmask) * (4.0 + gc - rc)
    h = tf.mod(h / 6.0, 1.0)

    h = tf.expand_dims(h, -1)
    l = tf.expand_dims(l, -1)
    s = tf.expand_dims(s, -1)

    x = tf.concat([tf.zeros_like(l), l, tf.zeros_like(l)], -1)
    y = tf.concat([h, l, s], -1)

    return tf.where(condition=tf.reduce_all(tf.equal(minc, maxc)), x=x, y=y)


if __name__ == '__main__':
    """
    HLS: Hue, Luminance, Saturation
    H: position in the spectrum
    L: color lightness
    S: color saturation
    """
    avatar = Image.open("hue.jpg")
    width, height = avatar.size
    print("width: {}, height: {}".format(width, height))
    img = np.array(avatar)
    img = img / 255.0
    print(img.shape)

    # # hue, luminance, saturation = rgb2hls(img)
    # hue, luminance, saturation = np_rgb2hls(img)

    img_tensor = tf.convert_to_tensor(img, tf.float32)
    hls = tf_rgb2hls(img_tensor)
    h, l, s = hls[:, :, 0], hls[:, :, 1], hls[:, :, 2]

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        hue, luminance, saturation = sess.run([h, l, s])
        scipy.misc.imsave("hls_h_.jpg", hue)
        scipy.misc.imsave("hls_l_.jpg", luminance)
        scipy.misc.imsave("hls_s_.jpg", saturation)

2
欢迎来到Stack Overflow!请不要仅仅回答源代码,尽量提供一个关于你的解决方案如何工作的好描述。参见:如何撰写一个好的答案?。谢谢。 - sɐunıɔןɐqɐp

0
如果有人正在寻找一个自包含的解决方案(我真的不想添加OpenCV作为依赖项),我重写了官方Python的colorsysrgb_to_hls()hls_to_rgb()函数,使其可用于numpy:
import numpy as np

def rgb_to_hls(rgb_array: np.ndarray) -> np.ndarray:
    """
    Expects an array of shape (X, 3), each row being RGB colours.
    Returns an array of same size, each row being HLS colours.
    Like `colorsys` python module, all values are between 0 and 1.

    NOTE: like `colorsys`, this uses HLS rather than the more usual HSL
    """
    assert rgb_array.ndim == 2
    assert rgb_array.shape[1] == 3
    assert np.max(rgb_array) <= 1
    assert np.min(rgb_array) >= 0

    r, g, b = rgb_array.T.reshape((3, -1, 1))
    maxc = np.max(rgb_array, axis=1).reshape((-1, 1))
    minc = np.min(rgb_array, axis=1).reshape((-1, 1))

    sumc = (maxc+minc)
    rangec = (maxc-minc)

    with np.errstate(divide='ignore', invalid='ignore'):
        rgb_c = (maxc - rgb_array) / rangec
    rc, gc, bc = rgb_c.T.reshape((3, -1, 1))

    h = (np.where(minc == maxc, 0, np.where(r == maxc, bc - gc, np.where(g == maxc, 2.0+rc-bc, 4.0+gc-rc)))
         / 6) % 1
    l = sumc/2.0
    with np.errstate(divide='ignore', invalid='ignore'):
        s = np.where(minc == maxc, 0,
                     np.where(l < 0.5, rangec / sumc, rangec / (2.0-sumc)))

    return np.concatenate((h, l, s), axis=1)


def hls_to_rgb(hls_array: np.ndarray) -> np.ndarray:
    """
    Expects an array of shape (X, 3), each row being HLS colours.
    Returns an array of same size, each row being RGB colours.
    Like `colorsys` python module, all values are between 0 and 1.

    NOTE: like `colorsys`, this uses HLS rather than the more usual HSL
    """
    ONE_THIRD = 1 / 3
    TWO_THIRD = 2 / 3
    ONE_SIXTH = 1 / 6

    def _v(m1, m2, h):
        h = h % 1.0
        return np.where(h < ONE_SIXTH, m1 + (m2 - m1) * h * 6,
                        np.where(h < .5, m2,
                                 np.where(h < TWO_THIRD, m1 + (m2 - m1) * (TWO_THIRD - h) * 6,
                                          m1)))


    assert hls_array.ndim == 2
    assert hls_array.shape[1] == 3
    assert np.max(hls_array) <= 1
    assert np.min(hls_array) >= 0

    h, l, s = hls_array.T.reshape((3, -1, 1))
    m2 = np.where(l < 0.5, l * (1 + s), l + s - (l * s))
    m1 = 2 * l - m2

    r = np.where(s == 0, l, _v(m1, m2, h + ONE_THIRD))
    g = np.where(s == 0, l, _v(m1, m2, h))
    b = np.where(s == 0, l, _v(m1, m2, h - ONE_THIRD))

    return np.concatenate((r, g, b), axis=1)


def _test1():
    import colorsys
    rgb_array = np.array([[.5, .5, .8], [.3, .7, 1], [0, 0, 0], [1, 1, 1], [.5, .5, .5]])
    hls_array = rgb_to_hls(rgb_array)
    for rgb, hls in zip(rgb_array, hls_array):
        assert np.all(abs(np.array(colorsys.rgb_to_hls(*rgb) - hls) < 0.001))
    new_rgb_array = hls_to_rgb(hls_array)
    for hls, rgb in zip(hls_array, new_rgb_array):
        assert np.all(abs(np.array(colorsys.hls_to_rgb(*hls) - rgb) < 0.001))
    assert np.all(abs(rgb_array - new_rgb_array) < 0.001)
    print("tests part 1 done")

def _test2():
    import colorsys
    hls_array = np.array([[0.6456692913385826, 0.14960629921259844, 0.7480314960629921], [.3, .7, 1], [0, 0, 0], [0, 1, 0], [.5, .5, .5]])
    rgb_array = hls_to_rgb(hls_array)
    for hls, rgb in zip(hls_array, rgb_array):
        assert np.all(abs(np.array(colorsys.hls_to_rgb(*hls) - rgb) < 0.001))
    new_hls_array = rgb_to_hls(rgb_array)
    for rgb, hls in zip(rgb_array, new_hls_array):
        assert np.all(abs(np.array(colorsys.rgb_to_hls(*rgb) - hls) < 0.001))
    assert np.all(abs(hls_array - new_hls_array) < 0.001)
    print("All tests done")

def _test():
    _test1()
    _test2()

if __name__ == "__main__":
    _test()

(请参见gist

(离题:以同样的方式转换其他函数实际上是对那些想要亲自动手进行numpy(或其他SIMD / GPU)编程的人的一次很好的训练。如果您这样做,请告诉我 :))


编辑:rgb_to_hsvhsv_to_rgb现在也在gist中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接