[PYTHON] Parlons de la courbe de tonalité du traitement d'image ~ LUT est incroyable ~

introduction

Je viens de commencer à utiliser ʻOpneCV` en Python J'ai essayé diverses choses et j'ai trébuché Je vais l'écrire dur.

Quand tu le prends et que tu parles ** LUT est incroyable! ** j'ai pensé ** J'ai essayé d'utiliser LUT! ** C'est le contenu.

(En conséquence) J'ai également comparé les performances avec ma propre fonction de conversion des couleurs.

Environnement d'exploitation

Terminal: Windows 10
Console: cmd(invite de commande)
python:3.6.8
Environnement virtuel: venv

Si vous voulez rendre cmd confortable, cliquez ici >> [https://qiita.com/RoaaaA/items/694ae4ccfa4a69fc8286)

introduction

J'ai appris la vision par ordinateur (CV) dans une classe universitaire (paperasse). Pour la première fois, je suis tombé sur quelque chose qui s'appelle «** courbe de tonalité **». Qu'est-ce qu'une courbe de tonalité [^ 1]

Chaque pixel de l'image numérique a une valeur (valeur de pixel) qui représente la teinte. Pour changer la teinte de l'image, comment associer la valeur de pixel de l'image de sortie à la valeur de pixel de l'image d'entrée. La fonction qui donne une telle correspondance est appelée fonction de transformation de niveau de gris, et sa représentation graphique est appelée courbe de ton.

En regardant le net, il semble familier à ceux qui aiment les caméras.

Parmi les courbes de ton apparaissant dans le manuel, il y avait les deux suivantes. (* Bien sûr, beaucoup d'autres sont apparus)

L'axe horizontal correspond à la ** valeur de pixel d'entrée ** et l'axe vertical à la ** valeur de pixel de sortie (convertie) **.

toneCurve12.png toneCurve22.png
Figure 1 Figure 2

Mettre tout simplement ** Conversion pour augmenter le contraste ** et ** Conversion qui réduit le contraste **.

Cette partie de la classe était déjà il y a plus de six mois. Je voulais en fait appliquer cette courbe de ton à l'image.

Fonctionnalisation

Exprimé en fonction, il se présente comme suit (x est la valeur du pixel d'entrée de 0 à 255).

Fonction dans la figure 1:

f(x) = \begin{cases} 2 \cdot x & (x < 128) \\ 255 & (otherwise)
  \end{cases}

Fonction dans la figure 2:

f(x) = \begin{cases} 0 & (x < 128) \\ 2 \cdot x - 255 & (otherwise)
  \end{cases}

Il semble que cela puisse être généralisé, mais laissons cela pour le moment.

Échec 1

Tout d'abord, j'ai décidé d'écrire le code illustré à la figure 1. Mais ... il est resté bloqué immédiatement.

Quand je suis venu avec la fonction ci-dessus J'ai pensé que je pourrais simplement doubler les valeurs des éléments du tableau original de valeurs de pixels.

Fonction de conversion


def toneCurve(frame):
    return 2 * frame

frame = cv2.imread('.\\image\\castle.jpg')
cv2.imwrite('cas.jpg', toneCurve(frame))

frame est un tableau de valeurs de pixels

Mais ça n'a pas marché. L'image de sortie est la suivante.

L'image originale Image convertie
L'image originale Image d'échec 1

Apparemment, si vous doublez simplement la valeur de pixel obtenue par cv2.imread () Tout ce qui est supérieur à 255 (maximum) sera 255 au lieu d'être complété Il semblait convertir la valeur de manière cyclique et la renvoyer. (ex:256=0, 260=4)

C'est très horrible et j'aime celui-ci Le but n'est pas cette image, donc je vais l'améliorer.

Amélioration 1 (échec 2)

Si vous le recherchez, vous pouvez utiliser cv2.add () Il semble que les valeurs supérieures à 255 soient traitées comme 255.

J'ai essayé.

Améliorations de la figure 1

Ajoutez votre propre valeur de pixel à vous-même = temps constants Parce que ça veut dire La fonction de conversion est la suivante.

Fonction de conversion


def toneCurve11(frame, n = 1):
    tmp = frame
    if n > 1:
        for i in range(1,n):
            tmp = cv2.add(tmp, frame)
    return tmp

frame est un tableau de valeurs de pixels «n» représente le nombre de fois que la valeur du pixel est multipliée, c'est-à-dire le nombre de fois à ajouter.

Ensuite, c'est le résultat.

L'image originale Image convertie
L'image originale Image de l'échec 2

Oui, c'est ce que je cherchais! Il produit une image avec un contraste accru. (L'image en échelle de gris était-elle meilleure?)

Cependant, comment pouvons-nous obtenir la conversion illustrée à la figure 2 en ajoutant simplement cette méthode? Voulez-vous produire une image?

** J'ai trouvé un coup rapide **: feux d'artifice:

Améliorations de la figure 2

Voici ce que j'ai trouvé.

** Inversion négative / positive>cv2.add ()> Inversion négative / positive **

Cela ressemble à ceci sur la figure.

ori negapoei
1.Pas de conversion 2.Inversion négative / positive
add renegaposi
3.cv2.add()Doublé avec 4.Inversion négative / positive

Les changements sont de l'ordre de 1, 2, 3, 4.

Le code de conversion est le suivant.

Fonction de conversion


def negaPosi(frame):
    return 255 - frame


def toneCurve21(frame, n = 1):
    if n > 1:
        return negaPoso(toneCurve11(negaPosi(frame), n))
    return frame

Comme avant frame est un tableau de valeurs de pixels «n» représente le nombre de fois que la valeur du pixel est multipliée, c'est-à-dire le nombre de fois à ajouter.

Eh bien, le résultat.

L'image originale Image convertie
L'image originale Image de l'échec 3

L'image avec le contraste réduit a été produite en toute sécurité.

Pause

Même si je fais ce qui précède, cela fonctionne bien. J'avais l'impression d'être interrogé sur la ** commande **, alors j'ai décidé de me pencher sur d'autres méthodes.

Ensuite, cv2 semble avoir une chose pratique appelée ** LUT (Look Up Table) **. De plus, si la commande peut être faite assez petite ...

LUT J'ai pris quelques approches ci-dessus, mais à la fin Si vous remplacez la valeur de pixel acquise par une autre valeur de pixel, vous pouvez la convertir.

Ici, les valeurs possibles des valeurs de pixel sont ** au plus 256 voies ** de 0 à 255. Si tel est le cas, ayez une table de correspondance dans laquelle la valeur de destination de conversion pour une certaine valeur de pixel est enregistrée à l'avance. Lorsque vous faites référence à une valeur de pixel, au lieu de calculer la valeur de pixel elle-même Les coûts de calcul peuvent être réduits en les remplaçant simplement.

Voici une brève description de la LUT.

Par exemple, la table de correspondance suivante peut être considérée.

Pixel de référence Changer de destination
0 -> 0
1 -> 2
... ...
127 -> 254
128 -> 255
... ...
255 -> 255

Oui, ce tableau est le tableau de correspondance de la courbe de tonalité de la figure 1.

Si la valeur de pixel de l'image [90] [90] est Si c'était «[12, 245, 98]» Après avoir appliqué LUT Il devient «[24, 255, 196]».

Amélioration 2

Nous utiliserons en fait la LUT.

Amélioration de la figure 1 (Re :)

Fonction de conversion


def toneCurve12(frame, n = 1):
    look_up_table = np.zeros((256,1), dtype = 'uint8')
    for i in range(256):
        if i < 256 / n:
            look_up_table[i][0] = i * n
        else:
            look_up_table[i][0] = 255
    return cv2.LUT(frame, look_up_table)

frame est un tableau de valeurs de pixels «n» indique combien de fois la valeur du pixel doit être multipliée.

Description de la fonction

    look_up_table = np.zeros((256,1), dtype = 'uint8')

Pour la partie de, créez une table de correspondance (256 x 1) pour la conversion.

À ce stade, les valeurs suivantes sont toujours stockées.

>>> look_up_table = np.zeros((256,1), dtype = 'uint8')
>>> look_up_table
array([[0],
       [0],
       [0],
       ...,
       [0],
       [0]], dtype=uint8)
>>> len(look_up_table)  
256

À partir de la ligne suivante, nous stockerons les valeurs à référencer dans ce tableau.

    for i in range(256):
        if i < 256 / n:
            look_up_table[i][0] = i * n
        else:
            look_up_table[i][0] = 255

En ce qui concerne la partie de, enregistrez la valeur de pixel dans la table de correspondance pour la conversion créée précédemment.

Aussi

        if i < 256 / n:

La partie de est une généralisation de l'expression conditionnelle afin qu'elle fonctionne même si la valeur du pixel est n fois. (ex: n = 3 [3 fois la valeur du pixel])

    return cv2.LUT(frame, look_up_table)

En ce qui concerne la partie de, la correspondance est prise par la LUT enregistrée plus tôt, Renvoie les données d'image converties.

Eh bien, le résultat.

L'image originale Amélioration 1 Amélioration 2
L'image originale Image de l'échec 2 Image de réussite 1

Vous pouvez obtenir l'image souhaitée.

Jetons un coup d'œil au temps d'exécution (ms).

n = 2 n = 3 n = 4
Amélioration 1 4.9848556 9.9725723 15.921831
Amélioration 2 4.9870014 4.9834251 4.9870014

Le traitement est plus rapide.

De la même manière, nous améliorerons la figure 2.

Amélioration de la figure 2 (Re :)

Fonction de conversion


def toneCurve22(frame, n = 1):
    look_up_table = np.zeros((256,1), dtype = 'uint8')
    for i in range(256):
        if i < 256 - 256 / n :
            look_up_table[i][0] = 0
        else:
            look_up_table[i][0] = i * n - 255 * (n - 1)
    return cv2.LUT(frame, look_up_table)

Ce que je fais est presque le même, donc je ne vais pas l'expliquer en détail

        if i < 256 - 256 / n :

La partie de est une généralisation de la partie fonction lorsque n = 2 x <128. Si n = 2 x <256 --256 / n = 128 Autrement dit, Il peut être transformé en «x <128».

Eh bien, le résultat.

L'image originale Amélioration 1 Amélioration 2
L'image originale Image de l'échec 2 Image de réussite 1

Vous pouvez obtenir l'image souhaitée.

Jetons un coup d'œil au temps d'exécution (ms).

n = 2 n = 3 n = 4
Amélioration 1 25.915145 32.911539 36.406755
Amélioration 2 4.9862861 4.9872398 4.9846172

Après tout, celui qui traite rapidement est bon.

en conclusion

Cette fois, j'ai parlé de la courbe des tons.

OpenCV est très pratique. Mes ancêtres ne s'énervent pas ... De plus, Python peut être utilisé avec une seule commande.

Quelqu'un verra-t-il cet article? Ou, même si je suis inquiet S'il s'agit d'un mémorandum, assurez-vous Je poste toujours.

J'espère que cet article sera utile à quelqu'un un jour.

Enfin, mettez le code source utilisé cette fois et terminez. Que devrais-je faire ensuite?

Puis: vague:

Code source

Sous le répertoire courant Dans un répertoire appelé image Il existe un fichier image appelé castle.jpg. Si vous utilisez ce code, veuillez le modifier vous-même.

Installez également la bibliothèque ci-dessous.

console


$ pip install opencv-python

$ pip freeze
numpy==1.18.4
opencv-python==4.2.0.34

ex.py


import cv2
import numpy as np


def toneCurve(frame):
    return 2 * frame


def negaPosi(frame):
    return 255 - frame


def toneCurve11(frame, n = 1):
    tmp = frame
    if n > 1:
        for i in range(1, n):
            tmp = cv2.add(tmp, frame)
    return tmp


def toneCurve12(frame, n = 1):
    look_up_table = np.zeros((256, 1), dtype = 'uint8')
    for i in range(256):
        if i < 256 / n:
            look_up_table[i][0] = i * n
        else:
            look_up_table[i][0] = 255
    return cv2.LUT(frame, look_up_table)


def toneCurve21(frame, n = 1):
    if n > 1:
        return negaPoso(toneCurve11(negaPosi(frame), n))
    return frame


def toneCurve22(frame, n = 1):
    look_up_table = np.zeros((256, 1), dtype = 'uint8')
    for i in range(256):
        if i < 256 - 256 / n :
            look_up_table[i][0] = 0
        else:
            look_up_table[i][0] = i * n - 255 * (n - 1)
    return cv2.LUT(frame, look_up_table)


def main():
    img_path = '.\\image\\castle.jpg'
    img = cv2.imread(img_path)
    cv2.imwrite('.\\image\\tone00.jpg', toneCurve(img))
    cv2.imwrite('.\\image\\tone11.jpg', toneCurve11(img, 2))
    cv2.imwrite('.\\image\\tone12.jpg', toneCurve12(img, 2))
    cv2.imwrite('.\\image\\tone21.jpg', toneCurve21(img, 2))
    cv2.imwrite('.\\image\\tone22.jpg', toneCurve22(img, 2))

if __name__ == '__main__':
    main()

référence

[^ 1]: Extrait de [Computer Graphics] cv

Recommended Posts

Parlons de la courbe de tonalité du traitement d'image ~ LUT est incroyable ~
La fonction d'affichage d'image d'iTerm est pratique lors du traitement d'images.
Traitement d'image avec Python et OpenCV [Tone Curve]
A propos des principales tâches de traitement d'image (vision par ordinateur) et de l'architecture utilisée
À propos de la vitesse de traitement de SVM (SVC) de scikit-learn
Traitement d'image? L'histoire du démarrage de Python pour
À propos du comportement de la file d'attente pendant le traitement parallèle
Devinons l'état de développement de la ville à partir de l'image satellite.
Traitement linguistique 100 knocks-29: Obtenez l'URL de l'image du drapeau
À propos des composants de Luigi
À propos des fonctionnalités de Python
L'image est Namekuji
Comprendre la fonction de convolution en utilisant le traitement d'image comme exemple
Considérez la vitesse de traitement pour déplacer le tampon d'image avec numpy.ndarray
[Word2vec] Visualisons le résultat du traitement en langage naturel des avis des entreprises