Обнаружение порогов в цветовом пространстве HSV (из RGB) с помощью Python / PIL

Я хочу взять изображение RGB и преобразовать его в черно-белое изображение RGB, где пиксель черный, если его значение HSV находится между определенным диапазоном и белым в противном случае.

В настоящее время я создаю новое изображение, затем создаю список новых значений пикселей, повторяя его данные, затем .putdata() этот список, чтобы сформировать новое изображение.

похоже, что должен быть гораздо более быстрый способ сделать это, например, с .point(), но это, кажется,.point() не получает заданные пиксели, но значения от 0 до 255, а не. Есть ли .point() преобразование, но на пикселях?

4 ответов


ОК, этот тут работа (исправлены некоторые ошибки переполнения):

import numpy, Image
i = Image.open(fp).convert('RGB')
a = numpy.asarray(i, int)

R, G, B = a.T

m = numpy.min(a,2).T
M = numpy.max(a,2).T

C = M-m #chroma
Cmsk = C!=0

# Hue
H = numpy.zeros(R.shape, int)
mask = (M==R)&Cmsk
H[mask] = numpy.mod(60*(G-B)/C, 360)[mask]
mask = (M==G)&Cmsk
H[mask] = (60*(B-R)/C + 120)[mask]
mask = (M==B)&Cmsk
H[mask] = (60*(R-G)/C + 240)[mask]
H *= 255
H /= 360 # if you prefer, leave as 0-360, but don't convert to uint8

# Value
V = M

# Saturation
S = numpy.zeros(R.shape, int)
S[Cmsk] = ((255*C)/V)[Cmsk]

# H, S, and V are now defined as integers 0-255

Он основан на определении Википедии HSV. Я посмотрю, когда у меня будет больше времени. Есть определенно ускорения и, возможно, ошибки. Пожалуйста, дайте мне знать,если найдете. овации.


результаты:

начиная с этого colorwheel: enter image description here

Я получаю эти результаты:

Hue:

enter image description here

значение:

enter image description here

насыщенность:

enter image description here


EDIT 2: теперь это возвращает те же результаты, что и код Павла, как и должно...

import numpy, scipy

image = scipy.misc.imread("test.png") / 255.0

r, g, b = image[:,:,0], image[:,:,1], image[:,:,2]
m, M = numpy.min(image[:,:,:3], 2), numpy.max(image[:,:,:3], 2)
d = M - m

# Chroma and Value
c = d
v = M

# Hue
h = numpy.select([c ==0, r == M, g == M, b == M], [0, ((g - b) / c) % 6, (2 + ((b - r) / c)), (4 + ((r - g) / c))], default=0) * 60

# Saturation
s = numpy.select([c == 0, c != 0], [0, c/v])

scipy.misc.imsave("h.png", h)
scipy.misc.imsave("s.png", s)
scipy.misc.imsave("v.png", v)

который дает оттенок от 0 до 360, насыщенность от 0 до 1 и значение от 0 до 1. Я посмотрел результаты в формате изображения, и они кажутся хорошими.

Я не был уверен, прочитав ваш вопрос, Было ли это только "значение", как в V от HSV, что вас интересовало. Если это так, то вы можете обойти большую часть этого кода.

затем вы можете выбрать пиксели на основе этих значений и установите их в 1 (или белый/черный), используя что-то вроде:

newimage = (v > 0.3) * 1

это решение основано на коде пола. Я исправил ошибку DivByZero и реализовал RGB в HSL. Существует также HSL для RGB:

import numpy

def rgb_to_hsl_hsv(a, isHSV=True):
    """
    Converts RGB image data to HSV or HSL.
    :param a: 3D array. Retval of numpy.asarray(Image.open(...), int)
    :param isHSV: True = HSV, False = HSL
    :return: H,S,L or H,S,V array
    """
    R, G, B = a.T

    m = numpy.min(a, 2).T
    M = numpy.max(a, 2).T

    C = M - m #chroma
    Cmsk = C != 0

    # Hue
    H = numpy.zeros(R.shape, int)
    mask = (M == R) & Cmsk
    H[mask] = numpy.mod(60 * (G[mask] - B[mask]) / C[mask], 360)
    mask = (M == G) & Cmsk
    H[mask] = (60 * (B[mask] - R[mask]) / C[mask] + 120)
    mask = (M == B) & Cmsk
    H[mask] = (60 * (R[mask] - G[mask]) / C[mask] + 240)
    H *= 255
    H /= 360 # if you prefer, leave as 0-360, but don't convert to uint8


    # Saturation
    S = numpy.zeros(R.shape, int)

    if isHSV:
        # This code is for HSV:
        # Value
        V = M

        # Saturation
        S[Cmsk] = ((255 * C[Cmsk]) / V[Cmsk])
        # H, S, and V are now defined as integers 0-255
        return H.swapaxes(0, 1), S.swapaxes(0, 1), V.swapaxes(0, 1)
    else:
        # This code is for HSL:
        # Value
        L = 0.5 * (M + m)

        # Saturation
        S[Cmsk] = ((C[Cmsk]) / (1 - numpy.absolute(2 * L[Cmsk]/255.0 - 1)))
        # H, S, and L are now defined as integers 0-255
        return H.swapaxes(0, 1), S.swapaxes(0, 1), L.swapaxes(0, 1)


def rgb_to_hsv(a):
    return rgb_to_hsl_hsv(a, True)


def rgb_to_hsl(a):
    return rgb_to_hsl_hsv(a, False)


def hsl_to_rgb(H, S, L):
    """
    Converts HSL color array to RGB array

    H = [0..360]
    S = [0..1]
    l = [0..1]

    http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL

    Returns R,G,B in [0..255]
    """

    C = (1 - numpy.absolute(2 * L - 1)) * S

    Hp = H / 60.0
    X = C * (1 - numpy.absolute(numpy.mod(Hp, 2) - 1))

    # initilize with zero
    R = numpy.zeros(H.shape, float)
    G = numpy.zeros(H.shape, float)
    B = numpy.zeros(H.shape, float)

    # handle each case:

    mask = (Hp >= 0) == ( Hp < 1)
    R[mask] = C[mask]
    G[mask] = X[mask]

    mask = (Hp >= 1) == ( Hp < 2)
    R[mask] = X[mask]
    G[mask] = C[mask]

    mask = (Hp >= 2) == ( Hp < 3)
    G[mask] = C[mask]
    B[mask] = X[mask]

    mask = (Hp >= 3) == ( Hp < 4)
    G[mask] = X[mask]
    B[mask] = C[mask]

    mask = (Hp >= 4) == ( Hp < 5)
    R[mask] = X[mask]
    B[mask] = C[mask]

    mask = (Hp >= 5) == ( Hp < 6)
    R[mask] = C[mask]
    B[mask] = X[mask]

    m = L - 0.5*C
    R += m
    G += m
    B += m

    R *=255.0
    G *=255.0
    B *=255.0

    return R.astype(int),G.astype(int),B.astype(int)

def combineRGB(r,g,b):
    """
    Combines separated R G B arrays into one array = image.
    scipy.misc.imsave("rgb.png", combineRGB(R,G,B))
    """
    rgb = numpy.zeros((r.shape[0],r.shape[1],3), 'uint8')
    rgb[..., 0] = r
    rgb[..., 1] = g
    rgb[..., 2] = b
    return rgb

Я думаю, что самый быстрый результат будет через numpy. Функция будет выглядеть примерно так (обновлено, добавлено более подробно к примеру):

limg = im.convert("L", ( 0.5, 0.5, 0.5, 0.5 ) )
na = numpy.array ( limg.getdata() )
na = numpy.piecewise(na, [ na > 128 ], [255, 0])
limg.pytdata(na)
limg.save("new.png")

В идеале вы можете использовать кусочную функцию без предварительного преобразования в черно-белый, что было бы больше похоже на исходный пример. Синтаксис будет примерно таким:

na = numpy.piecewise(na, [ na[0] > 128 ], [255, 0])

но вы должны быть осторожны, так как изображение RGB является кортежем 3 или 4 на возврате значение.