OpenGL.Shader: Zhige vous apprend à écrire un client de filtre en direct (7) Filtres visuels: luminosité, exposition, saturation, teinte

OpenGL.Shader: Zhige vous apprend à écrire un client de filtre en direct (7)

 

Je suis occupé avec l'autre moitié de ma vie récemment, et il y a une petite commande étrangère, qui est effectivement un peu trop occupée. Bien que ce soit un peu occupé, je peux à peine maintenir la sortie de 1 à 2 articles de connaissances originaux par mois pour tout le monde. Sans parler des absurdités, cet article présente principalement quelques méthodes de base de traitement d'image. L'explication du contenu est basée sur le langage GPU.Shader, mais elle ne gêne pas la compréhension sur le plan cognitif. Il est également applicable dans OpenCV et d'autres SDK de traitement d'image. Sans plus tarder, montrez le code.

 

1. Luminosité

Je l'ai déjà touché lors de l'introduction de l'éclairage 3D, il suffit d'ajouter la valeur de la lumière sur la base du canal à trois couleurs RVB d'origine. Le code du shader est le suivant:

precision mediump float;
variant highp vec2 textureCoordinate; // Les coordonnées de texture transmises par le vertex shader
uniform sampler2D SamplerY; // Y component
uniform sampler2D SamplerU; // U component
uniform sampler2D SamplerV; // V component
mat3 colorConversionMatrix = mat3 (
                   1.0, 1.0, 1.0,
                   0.0, -0.39465, 2.03211,
                   1.13983, -0.58060, 0.0);
vec3 yuv2rgb (vec2 pos)
{    vec3 yuv;    yuv.x = texture2D (SamplerY, pos)    .r ; yuv.y = texture2D (SamplerU, pos)    .r -0.5; yuv.z = texture2D (SamplerV, pos) .r-0.5;    return colorConversionMatrix * yuv; } // La méthode de conversion de yuv à trois composants en RVB. luminosité du flotteur lowp uniforme; // Intensité lumineuse.








void main ()
{     vec4 textureColor = vec4 (yuv2rgb (textureCoordinate), 1.0);     gl_FragColor = vec4 ((textureColor.rgb + vec3 (luminosité)), textureColor.w); // Basé sur le composant rgb d'origine + luminosité, conservez w }


//光照强度建议控制在  -0.5~0.5,1的效果已经就是泛白了
void setAdjustEffect(float percent) {
    mBrightness = range(percent * 100.0f, -0.5f, 0.5f);
}
float range(float percentage, float start, float end) {
    return (end - start) * percentage / 100.0f + start;
}

Le code du shader du fragment de luminosité est comme ci-dessus, l'effet est relativement simple et non fourni, vous pouvez l'essayer sur le projet de démonstration. Le code spécifique se réfère à  https://github.com/MrZhaozhirong/NativeCppApp      cpp / gpufilter / filter / GpuBrightnessFilter.hpp

 

2. Exposition

Le principe de l'exposition et de la luminosité est fondamentalement le même. La luminosité est une augmentation linéaire de la valeur de couleur dans toutes les directions et l'exposition est basée sur la superposition exponentielle des valeurs de couleur primaire (le rouge sera plus rouge, le vert sera plus vert et le bleu sera plus La lumière bleue et blanche sera plus brillante) Le code du shader est le suivant:

flotteur de précision mediump;
variable highp vec2 textureCoordinate;
sampler2D SamplerY uniforme;
uniform sampler2D SamplerU;
sampler2D SamplerV uniforme;
mat3 colorConversionMatrix = mat3 (
                   1.0, 1.0, 1.0,
                   0.0, -0.39465, 2.03211,
                   1.13983, -0.58060, 0.0);
vec3 yuv2rgb (vec2 pos)
{    vec3 yuv;    yuv.x = texture2D (SamplerY, pos) .r;    yuv.y = texture2D (SamplerU, pos) .r - 0,5;    yuv.z = texture2D (SamplerV, pos) .r - 0,5;    return colorConversionMatrix * yuv; } exposition uniforme au flotteur lowp; // 曝光 度 效果 值void main () {









    vec4 textureColor = vec4 (yuv2rgb (textureCoordinate), 1.0);
    gl_FragColor = vec4 ((textureColor.rgb * pow (2.0, exposition)), textureColor.w); // rgb * 2 ^ 效果 值
}

L'exposition doit également avoir des limites supérieures et inférieures pour éviter une surexposition.

void setAdjustEffect(float percent) {
    // (0.0 as the default)
    mExposure = range(percent * 100.0f, -5.0f, 5.0f);
}

Le code fragment shader de l'exposition est comme ci-dessus, l'effet est relativement simple et il n'est pas fourni.Vous pouvez l'essayer sur le projet de démonstration. Le code spécifique se réfère à  https://github.com/MrZhaozhirong/NativeCppApp      cpp / gpufilter / filter / GpuExposureFilter.hpp

 

3. Saturation

Après avoir rencontré deux méthodes de traitement de base relativement simples, examinons une méthode d'entrée de gamme. Comprenons d'abord ce qu'est la saturation des couleurs: la saturation fait référence à la vivacité de la couleur, également appelée pureté de la couleur. La saturation dépend du rapport entre la composante de couleur et la composante de décoloration (gris) dans la couleur. Plus la composante de couleur est grande, plus la saturation est grande; plus la composante achromatique est grande, plus la saturation est faible. Les couleurs pures sont très saturées, comme le rouge vif et le vert vif. Les couleurs mélangées avec du blanc, du gris ou d'autres tons sont des couleurs insaturées, telles que le violet, le rose, le brun jaunâtre, etc., les couleurs complètement insaturées n'ont aucune teinte, comme divers gris entre le noir et le blanc. 

Résumé du concept: Saturation = X · couleur primaire + Y · valeur de gris, où (x + y = 1)

L'algorithme Luma est utilisé pour calculer le niveau de gris: Gray = R * 0,2125 + G * 0,7154 + B * 0,0721

flotteur de précision mediump;
variable highp vec2 textureCoordinate;
sampler2D SamplerY uniforme;
uniform sampler2D SamplerU;
sampler2D SamplerV uniforme;
mat3 colorConversionMatrix = mat3 (
                   1.0, 1.0, 1.0,
                   0.0, -0.39465, 2.03211,
                   1.13983, -0.58060, 0.0);
vec3 yuv2rgb (vec2 pos)
{    vec3 yuv;    yuv.x = texture2D (SamplerY, pos) .r;    yuv.y = texture2D (SamplerU, pos) .r - 0,5;    yuv.z = texture2D (SamplerV, pos) .r - 0,5;    return colorConversionMatrix * yuv; } saturation uniforme du flotteur; // 饱和 度 比值







const mediump vec3 luminanceWeight = vec3 (0.2125, 0.7154, 0.0721); // Opérateur Luma
void main ()
{     vec4 textureColor = vec4 (yuv2rgb (textureCoordinate), 1.0);     lowp float luminance = dot (textureColor.rgb, luminanceWeight); // Calculer la valeur de gris     lowp vec3 greyScaleColor = vec3 (luminance);     gl_FragColor = vec4 (mix (greyScaleColor, textureColor.rgb, saturation), textureColor.w);     // Fonction intégrée GLSL mix (x, y, a) = x * (1-a) + y * a, répond juste à la définition de la formule de saturation. }





 À partir de la formule de définition, nous pouvons voir que la plage normale du taux de saturation est (0 ~ 1). Puisqu'il existe une plage normale, il doit y avoir une plage anormale. Vous pouvez ajuster les limites supérieure et inférieure de manière appropriée pour comparer les effets réels.

void setAdjustEffect(float percent) {
    mSaturation = range(percent * 100.0f, 0.0f, 1.0f); // 2.0f
}

Le code de shader de fragment de saturation est comme ci-dessus, l'effet est relativement simple et non fourni, vous pouvez l'essayer sur le projet de démonstration. Le code spécifique se réfère à  https://github.com/MrZhaozhirong/NativeCppApp      cpp / gpufilter / filter / GpuSaturationFilter.hpp

 

 

Laissez-moi vous présenter la préface des connaissances, les trois principaux modèles de couleurs dans le traitement d'image numérique : RVB, HSI, CMJN (attention! Ce n'est pas un format, mais un modèle de couleur)

(1) Le modèle de couleur RVB le plus couramment utilisé.

RVB est un espace défini en fonction des couleurs reconnues par l'œil humain et peut représenter la plupart des couleurs. Il s'agit de l'espace colorimétrique le plus basique, le plus couramment utilisé et orienté matériel dans le traitement d'image, et un système de mélange de lumière.

On peut voir que le modèle de couleur RVB utilise un point dans l'espace tridimensionnel pour représenter une couleur, et chaque point a trois composants, qui représentent les valeurs de luminosité du rouge, du vert et du bleu respectivement, et la valeur de luminosité est limitée à [0, 1]. Dans le cube du modèle RVB, la couleur correspondant à l'origine est le noir, et ses trois valeurs de composants sont toutes 0; la couleur correspondant au sommet le plus éloigné de l'origine est le blanc et ses trois valeurs de composants sont toutes 1. Les valeurs de gris du noir au blanc sont réparties sur la ligne reliant ces deux points. La ligne en pointillé est appelée ligne grise; les points restants du cube correspondent à différentes couleurs, à savoir les trois couleurs primaires rouge, vert, bleu et leurs couleurs mélangées jaune, Magenta, cyan.

(2) modèle de couleur HSI, utilisé pour la transmission visuelle

L'espace colorimétrique HSI est basé sur le système visuel humain, utilisant la teinte (Teinte), la saturation (Saturation ou Chroma) et la luminosité (Intensité ou Luminosité) pour décrire les couleurs.

H-indique l'angle de phase de la couleur. Le rouge, le vert et le bleu sont séparés de 120 degrés; les couleurs complémentaires sont séparées de 180 degrés, qui est la catégorie de couleur.
S-exprimé comme le rapport entre la pureté de la couleur sélectionnée et la pureté maximale de la couleur, plage: [0, 1], c'est-à-dire le degré de profondeur de couleur.
I-indique la luminosité de la couleur, plage: [0, 1], les yeux humains sont très sensibles à la luminosité!

On peut voir que l'espace colorimétrique HSI et l'espace colorimétrique RVB ne sont que des représentations différentes de la même quantité physique, il existe donc une relation de conversion entre eux: la teinte en mode couleur HSI est représentée par la catégorie de couleur et la saturation est inversement proportionnelle à la luminosité de la lumière blanche de la couleur. Représente le rapport du gris à la teinte et la luminosité est la luminosité relative d'une couleur.

(3) Modèle CMJN, utilisé pour le mode couleur des imprimés reposant sur la réflexion

CMJN est un mode couleur qui repose sur les reflets Comment lisons-nous le contenu des journaux? C'est la lumière du soleil ou de la lumière qui brille sur le journal et la reflète ensuite dans nos yeux pour en voir le contenu. Il doit avoir une source de lumière externe, si vous êtes dans une pièce sombre, vous ne pouvez pas lire le journal. Tant que l'image affichée à l'écran est représentée par le mode RVB. Tant que l'image vue sur l'imprimé est exprimée en mode CMJN. La plupart des appareils qui déposent des pigments de couleur sur du papier, tels que les imprimantes et les photocopieurs couleur, nécessitent l'entrée de données CMJ et la conversion RVB en CMJ en interne.

Le cyan, le magenta et le jaune sont les couleurs secondaires de la lumière et sont les couleurs des pigments. Et K prend la dernière lettre de noir La raison pour laquelle la première lettre n'est pas prise est d'éviter la confusion avec le bleu. Lorsque les trois couleurs primaires rouge, vert et bleu sont mélangées, le blanc est produit et lorsque les trois couleurs primaires cyan, magenta et jaune sont mélangées, du noir est produit. Théoriquement parlant, seuls trois types d'encres CMJ suffisent, mais comme le processus de fabrication actuel ne peut pas produire d'encres de haute pureté, le résultat de l'ajout de CMY est en fait un rouge foncé.

Les trois modèles de couleurs ci-dessus sont des représentations mathématiques d'une série de couleurs. Les trois modèles de couleurs les plus populaires sont RVB (pour l'infographie), YIQ, YUV ou YCbCr (pour les systèmes vidéo) et CMJN (pour l'impression couleur). Cependant, aucune de ces trois couleurs n'est directement liée à notre concept intuitif de teinte, de saturation et de luminosité. Cela nous laisse temporairement à la recherche d'autres modèles qui simplifient la programmation, le traitement et les opérations de l'utilisateur final.

 

4. Teinte (teinte)

Le dernier ton de couleur peut être considéré comme une technique de traitement d'image au-dessus du niveau d'entrée. La partie théorique est de plus en plus profonde, vous devez donc la comprendre lentement. Comprenons d'abord ce qu'est la teinte et comment définir la teinte.

La teinte ne fait pas référence à la nature de la couleur, mais à l'évaluation globale d'une peinture. Par exemple, la tendance générale de la couleur de l'image dans une peinture est un effet de couleur important. Bien que de nombreuses couleurs puissent être utilisées dans cette peinture, la couleur générale est bleue ou rouge, chaude ou froide. et beaucoup plus. Ce type de couleur est enveloppé sur des objets de couleurs différentes, de sorte que les objets de couleurs différentes ont tous la même tendance de couleur.Ce phénomène de couleur est la teinte.

Parmi les trois modèles de couleurs, le modèle de couleurs HSI est plus proche du concept intuitif de teinte, de saturation et de luminosité. À cet égard, le format RVB n'est pas pratique à calculer, il doit donc d'abord être converti au format YIQ \ YUV qui convient au calcul de la teinte, de la saturation et de la luminosité. Le format YIQ est sélectionné ici. Pour toute question sur le format YIQ, vous pouvez lire cette traduction https://www.hisour.com/zh/yiq-color-space-26084/ Une    fois le format converti, le modèle de couleur doit être converti, à partir du modèle RVB Converti en modèle HSI pour faciliter l'ajustement linéaire de la tonalité de couleur.

Dites tellement de merde, regardons le code!

flotteur de précision mediump;
variable highp vec2 textureCoordinate;
uniform sampler2D SamplerY;
uniform sampler2D SamplerU;
sampler2D SamplerV uniforme;
mat3 colorConversionMatrix = mat3 (
                   1.0, 1.0, 1.0,
                   0.0, -0.39465, 2.03211,
                   1.13983, -0.58060, 0.0);
vec3 yuv2rgb (vec2 pos)
{    vec3 yuv;    yuv.x = texture2D (SamplerY, pos) .r;    yuv.y = texture2D (SamplerU, pos) .r - 0,5;    yuv.z = texture2D (SamplerV, pos) .r - 0,5;    return colorConversionMatrix * yuv; } uniform mediump float hueAdjust; // 用户 调整 值// RVB à YIQ 的 矩阵 向量








const highp vec4 kRGBToYPrime = vec4 (0,299, 0,587, 0,114, 0,0);
const highp vec4 kRGBToI = vec4 (0,595716, -0,274453, -0,321263, 0,0);
const highp vec4 kRGBToQ = vec4 (0,211456, -0,522591) ;
// YIQ à vecteur de matrice RVB
const highp vec4 kYIQToR = vec4 (1.0, 0.9563, 0.6210, 0.0);
const highp vec4 kYIQToG = vec4 (1.0, -0.2721, -0.6474, 0.0);
const highp vec4 kYIQToB = vec4 ( 1.0, -1.1070, 1.7046, 0.0);
void main ()
{     // # Extraire RVB     vec4 textureColor = vec4 (yuv2rgb (textureCoordinate), 1.0);     // # Convertir en YIQ     highp float YPrime = dot (textureColor, kRGBToYPrime);     highp float I = dot (textureColor, kRGBToI);





    highp float Q = dot (textureColor, kRGBToQ);
    // # Selon le modèle HSI, extraire teinte chroma
    highp float hue = atan (Q, I);
    highp float chroma = sqrt (I * I + Q * Q);
    // # Réglage dynamique externe
    hue - = hueAdjust;
    // # Reconvertir à la valeur de format YIQ du modèle HSI
    Q = chroma * sin (hue);
    I = chroma * cos (hue);
    // #
    Reconvertir en RVB pour afficher highp vec4 yIQ = vec4 (YPrime, I, Q, 0.0);
    textureColor.r
    = dot (yIQ, kYIQToR); textureColor.g
    = dot (yIQ, kYIQToG);
    textureColor.b = dot (yIQ, kYIQToB); gl_FragColor = textureColor ;
}

Le code ci-dessus est commenté et si vous ne comprenez pas, vous pouvez envoyer un message privé. La vidéo d'effet sera téléchargée plus tard. Vous pouvez l'essayer sur le projet de démonstration. Le code spécifique se réfère à  https://github.com/MrZhaozhirong/NativeCppApp      cpp / gpufilter / filter / GpuHueFilter.hpp

 

C'est tout.

Groupe de discussion d'intérêt: 703531738. Le code: Zhige 13567

Je suppose que tu aimes

Origine blog.csdn.net/a360940265a/article/details/106327333
conseillé
Classement