J'ai fait beaucoup de recherches mais je n'ai rien trouvé (mais je ne sais pas non plus quel genre de mots clés rechercher exactement). Je veux pouvoir convertir une image RVB d'entrée en niveaux de gris mais je veux pouvoir ajouter plus ou moins rouges / jaunes / verts / cyans / bleus / Magentas comme dans Photoshop . Savez-vous quelle est l'équation ou où je peux trouver ces équations afin que je puisse implémenter ma propre conversion optimisée RVB en niveaux de gris?

Modifier : Dans Photoshop, il est appelé Calque de réglage noir / blanc . J'ai trouvé quelque chose mais en fait ça ne semble pas fonctionner. Voici mon implémentation (dans les commentaires sont les ressources nécessaires pour comprendre l'algorithme):

import numpy as np
import scipy.misc
import matplotlib.pyplot as plt


%matplotlib inline

# Adapted from the answers of Ivan Kuckir and Royi here:
# https://dsp.stackexchange.com/questions/688/what-is-the-algorithm-behind-photoshops-black-and-white-adjustment-layer?newreg=77420cc185fd44099d8be961e736eb0c

def rgb2hls(img):
    """Adapted to use numpy from
       https://github.com/python/cpython/blob/2.7/Lib/colorsys.py"""
    r, g, b = img[:, :, 0], img[:, :, 1], img[:, :, 2]

    maxc = np.max(img, axis=-1)
    minc = np.min(img, axis=-1)
    l = (minc + maxc) / 2

    mask = np.ones_like(r)
    mask[np.where(minc == maxc)] = 0
    mask = mask.astype(np.bool)

    smask = np.greater(l, 0.5).astype(np.float32)

    s = (1.0 - smask) * ((maxc - minc) / (maxc + minc)) + smask * ((maxc - minc) / (2.0 - maxc - minc))
    s[~mask] = 0
    rc = np.where(mask, (maxc - r) / (maxc - minc), 0)
    gc = np.where(mask, (maxc - g) / (maxc - minc), 0)
    bc = np.where(mask, (maxc - b) / (maxc - minc), 0)

    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)
    h[~mask] = 0
    return np.stack([h, l, s], axis=-1)


def black_and_white_adjustment(image, weights):  
    # normalize input image to (0, 1) if uint8
    if 'uint8' in (image).dtype.name:
        image = image / 255

    # linearly remap input coeff [-200, 300] to [-2.5, 2.5]
    weights = (weights - 50) / 100
    n_weights = len(weights)
    h, w = image.shape[:2]

    # convert rgb to hls
    hls_img = rgb2hls(image)

    output = np.zeros((h, w), dtype=np.float32)

    # see figure 9 of https://en.wikipedia.org/wiki/HSL_and_HSV
    # to understand the algorithm
    for y in range(h):
        for x in range(w):
            hue_val = 6 * hls_img[y, x, 0]

            # Use distance on a hexagone (maybe circular distance is better?)
            diff_val = min(abs(0 - hue_val), abs(1 - (0 - hue_val)))
            luminance_coeff = weights[0] * max(0, 1 - diff_val)

            for k in range(1, n_weights):
                luminance_coeff += weights[k] * max(0, 1 - abs(k - hue_val))

            # output[y, x] = min(max(hls_img[y, x, 1] * (1 + luminance_coeff), 0), 1)
            output[y, x] = hls_img[y, x, 1] * (1 + luminance_coeff)


    return output


image = scipy.misc.imread("your_image_here.png")
w = np.array([40, 85, 204, 60, 20, 80])
out = black_and_white_adjustment(image, w)
plt.figure(figsize=(15, 20))
plt.imshow(out, cmap='gray')

Je vous remercie

1
priseJack 15 mars 2019 à 17:51

2 réponses

Meilleure réponse

Voici une tentative d'utiliser PIL plutôt que numpy. Il devrait être facile à convertir. Sans une copie de Photoshop avec laquelle comparer, je ne peux pas garantir qu'il correspond exactement à la sortie, mais il produit les valeurs exactes de l'exemple affiché dans votre lien. Les valeurs r_w, y_w, g_w, c_w, b_w, m_w sont les pondérations à appliquer à chaque couleur, 1,0 correspondant à 100% dans le curseur Photoshop correspondant. Naturellement, ils peuvent également être négatifs.

from PIL import Image
im = Image.open(r'c:\temp\temp.png')
def ps_black_and_white(im, weights):
    r_w, y_w, g_w, c_w, b_w, m_w = [w/100 for w in weights]
    im = im.convert('RGB')
    pix = im.load()
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            r, g, b = pix[x, y]
            gray = min([r, g, b])
            r -= gray
            g -= gray
            b -= gray
            if r == 0:
                cyan = min(g, b)
                g -= cyan
                b -= cyan
                gray += cyan * c_w + g * g_w + b * b_w
            elif g == 0:
                magenta = min(r, b)
                r -= magenta
                b -= magenta
                gray += magenta * m_w + r * r_w + b * b_w
            else:
                yellow = min(r, g)
                r -= yellow
                g -= yellow
                gray += yellow * y_w + r * r_w + g * g_w
            gray = max(0, min(255, int(round(gray))))
            pix[x, y] = (gray, gray, gray)
    return im

En utilisant cette image de test fournie, voici quelques exemples de résultats.

color test image

ps_black_and_white(im, [-17, 300, -100, 300, -200, 300])

-17, 300, -100, 300, -200, 300

ps_black_and_white(im, [40, 60, 40, 60, 20, 80])

40, 60, 40, 60, 20, 80

ps_black_and_white(im, [106, 65, 17, 17, 104, 19])

106, 65, 17, 17, 104, 19

3
Mark Ransom 21 mars 2019 à 02:32

Je réponds à ma propre question en ajoutant la version numpy / scipy du code, si cela peut être d'intérêt pour quiconque à l'avenir. Si vous souhaitez voter pour une réponse, vous devriez voter pour la réponse de Mark Ransom!

import numpy as np
import scipy.misc
import matplotlib.pyplot as plt

%matplotlib inline

def black_and_white_adjustment(img, weights):
    rw, yw, gw, cw, bw, mw = weights / 100

    h, w = img.shape[:2]
    min_c = np.min(img, axis=-1).astype(np.float)
    # max_c = np.max(img, axis=-1).astype(np.float)

    # Can try different definitions as explained in the Ligtness section from
    # https://en.wikipedia.org/wiki/HSL_and_HSV
    # like: luminance = (min_c + max_c) / 2 ...
    luminance = min_c 
    diff = img - min_c[:, :, None]

    red_mask = (diff[:, :, 0] == 0)
    green_mask = np.logical_and((diff[:, :, 1] == 0), ~red_mask)
    blue_mask = ~np.logical_or(red_mask, green_mask)

    c = np.min(diff[:, :, 1:], axis=-1)
    m = np.min(diff[:, :, [0, 2]], axis=-1)
    yel = np.min(diff[:, :, :2], axis=-1)

    luminance = luminance + red_mask * (c * cw + (diff[:, :, 1] - c) * gw + (diff[:, :, 2] - c) * bw) \
                + green_mask * (m * mw + (diff[:, :, 0] - m) * rw + (diff[:, :, 2] - m) * bw)  \
                + blue_mask * (yel * yw + (diff[:, :, 0] - yel) * rw + (diff[:, :, 1] - yel) * gw)

    return np.clip(luminance, 0, 255).astype(np.uint8)

input_img = scipy.misc.imread("palette.jpg")

weights = np.array([106, 65, 17, 17, 104, 19])
bw_image = black_and_white_adjustment(input_img, weights)

plt.figure(figsize=(15, 20))
plt.imshow(bw_image, cmap="gray")

Ce code est plus rapide car il utilise des opérations vect.

1
priseJack 21 mars 2019 à 08:48