OpenGL.Shader: Zhige vous apprend à écrire un client de filtre en direct (10) Filtre visuel: réalisation du principe du filtre gaussien / flou gaussien

OpenGL.Shader: Zhige vous apprend à écrire un client de filtre en direct (10) La réalisation du principe du filtre gaussien / flou gaussien

1. Filtrage sans "modéré"

Le principe du chapitre précédent réalisait le filtrage moyen. Comme mentionné dans l'introduction du filtrage d'image, le filtrage moyen est le type le plus simple de filtre passe-bas d'image, qui peut filtrer le bruit uniforme et le bruit gaussien, mais provoquera un certain degré de flou sur l'image. C'est une méthode de moyenne des pixels dans la zone spécifiée de l'image. L'inconvénient du filtre de moyennage est qu'il rend l'image floue, car il fait la moyenne de tous les points. En fait, dans la plupart des cas, la proportion de bruit est faible, et tous les points sont traités avec le même poids, ce qui conduira inévitablement à des images floues. De plus, plus la largeur de ce filtre est grande, plus l'image filtrée est floue, ce qui signifie que les détails de l'image sont perdus, rendant l'image plus "modérée".

Alors, comment pouvons-nous réaliser un filtrage d'image sans "modéré"? Tant que le poids du filtre est modifié, les paramètres de ce filtre sont modifiés selon la forme de distribution gaussienne, alors ce filtre est appelé filtre gaussien, également appelé flou gaussien.

2. Distribution normale, fonction gaussienne

Voici une brève introduction à l'algorithme de déduction de la distribution normale gaussienne et à l'évolution de son code.

Quelle est la distribution normale fait référence à la distribution normale, plus la valeur est proche du centre, plus la valeur est élevée et plus la valeur est éloignée du centre .
Lors du calcul de la moyenne, il suffit d'utiliser le "point central" comme origine et d'attribuer des poids aux autres points en fonction de leurs positions sur la courbe normale pour obtenir une moyenne pondérée. La distribution normale est évidemment un modèle de distribution de poids souhaitable.

Comprenez quelle est la distribution normale, la classe suivante doit utiliser la fonction gaussienne pour réaliser le processus de transformation mathématique.
La distribution normale ci-dessus est unidimensionnelle, mais pour les images sont bidimensionnelles, nous avons donc besoin d'une distribution normale bidimensionnelle.

La fonction de densité de la distribution normale est appelée «fonction gaussienne». Sa forme bidimensionnelle est:

Obtenir la matrice de poids

En supposant que les coordonnées du point central sont (0,0), les coordonnées des 8 points les plus proches sont les suivantes:

Plus la plage est grande, plus le point est éloigné, etc.
Afin de calculer la matrice de poids, la valeur de σ doit être définie. En supposant que σ = 1,5 , la matrice de poids avec un rayon de flou de 1 est la suivante: (amener les valeurs de coordonnées dans la formule gaussienne)

La somme des poids de ces 9 points est égale à 0,4787147. Si seule la moyenne pondérée de ces 9 points est calculée, la somme de leurs poids doit être égale à 1. Par conséquent, les 9 valeurs ci-dessus doivent être divisées par 0,4787147 pour obtenir la matrice de poids finale.

Le processus de division par la valeur totale est également appelé «problème de normalisation». Le but est de rendre le poids total du filtre égal à 1. Sinon, l'utilisation d'un filtre avec une valeur totale supérieure à 1 rendra l'image plus lumineuse, et un filtre inférieur à 1 rendra l'image plus sombre. Jusqu'à présent, nous avons obtenu le noyau de convolution gaussien du troisième ordre.

3. Transfert multi-index GL shader

La prochaine étape est d'implémenter ce filtre de flou gaussien sur GL.Shader.En fait, l'idée d'implémentation est la même que le flou moyen dans le chapitre précédent, mais cette fois, nous allons introduire le transfert de tableau d'index du shader. Simplifiez le code du shader.

Le premier est le shader à virgule fixe. Faites attention à l'écriture du tableau de référence, il n'y a rien à dire, faites attention à la position de la matrice et à la position de l'index du tableau pour bien correspondre.

attribute vec4 position;
attribute vec4 inputTextureCoordinate;
uniform float widthFactor;
uniform float heightFactor;
uniform float offset; // 采样点半径
const int GAUSSIAN_SAMPLES = 9;
varying vec2 textureCoordinate[GAUSSIAN_SAMPLES];
void main()
{
    gl_Position = position;
    vec2 widthStep = vec2(offset*widthFactor, 0.0);
    vec2 heightStep = vec2(0.0, offset*heightFactor);
    textureCoordinate[0] = inputTextureCoordinate.xy - heightStep - widthStep; // 左上
    textureCoordinate[1] = inputTextureCoordinate.xy - heightStep; // 上
    textureCoordinate[2] = inputTextureCoordinate.xy - heightStep + widthStep; // 右上
    textureCoordinate[3] = inputTextureCoordinate.xy - widthStep; // 左中
    textureCoordinate[4] = inputTextureCoordinate.xy; // 中
    textureCoordinate[5] = inputTextureCoordinate.xy + widthStep; // 右中
    textureCoordinate[6] = inputTextureCoordinate.xy + heightStep - widthStep; // 左下
    textureCoordinate[7] = inputTextureCoordinate.xy + heightStep; // 下
    textureCoordinate[8] = inputTextureCoordinate.xy + heightStep + widthStep; // 右下
}

Ensuite, il y a le fragment shader. Nous nous référons au noyau de convolution convolutionMatrix de l'extérieur, et passons dans le noyau de convolution du filtre gaussien juste calculé à partir du code de la couche C ++ externe. En fait, il peut être écrit statiquement dans le shader comme la matrice de conversion de yuv2rgb. Il est seulement pratique de déboguer et d'améliorer la lisibilité dans le transfert de code externe.

precision highp float;
uniform sampler2D SamplerY;
uniform sampler2D SamplerU;
uniform sampler2D SamplerV;
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 mat3 convolutionMatrix;
const int GAUSSIAN_SAMPLES = 9;
varying vec2 textureCoordinate[GAUSSIAN_SAMPLES];
void main()
{
    //mediump vec3 topLeftColor     = yuv2rgb(textureCoordinate[0]);
    //mediump vec3 topColor         = yuv2rgb(textureCoordinate[1]);
    //mediump vec3 topRightColor    = yuv2rgb(textureCoordinate[2]);
    //mediump vec3 leftColor        = yuv2rgb(textureCoordinate[3]);
    //mediump vec3 centerColor      = yuv2rgb(textureCoordinate[4]);
    //mediump vec3 rightColor       = yuv2rgb(textureCoordinate[5]);
    //mediump vec3 bottomLeftColor  = yuv2rgb(textureCoordinate[6]);
    //mediump vec3 bottomColor      = yuv2rgb(textureCoordinate[7]);
    //mediump vec3 bottomRightColor = yuv2rgb(textureCoordinate[8]);
    vec3 fragmentColor = (yuv2rgb(textureCoordinate[0]) * convolutionMatrix[0][0]);
    fragmentColor += (yuv2rgb(textureCoordinate[1]) * convolutionMatrix[0][1]);
    fragmentColor += (yuv2rgb(textureCoordinate[2]) * convolutionMatrix[0][2]);
    fragmentColor += (yuv2rgb(textureCoordinate[3]) * convolutionMatrix[1][0]);
    fragmentColor += (yuv2rgb(textureCoordinate[4]) * convolutionMatrix[1][1]);
    fragmentColor += (yuv2rgb(textureCoordinate[5]) * convolutionMatrix[1][2]);
    fragmentColor += (yuv2rgb(textureCoordinate[6]) * convolutionMatrix[2][0]);
    fragmentColor += (yuv2rgb(textureCoordinate[7]) * convolutionMatrix[2][1]);
    fragmentColor += (yuv2rgb(textureCoordinate[8]) * convolutionMatrix[2][2]);
    gl_FragColor = vec4(fragmentColor, 1.0);
}

Une partie de l'extrait de code C ++:

#ifndef GPU_GAUSSIANBLUR_FILTER_HPP
#define GPU_GAUSSIANBLUR_FILTER_HPP
#include "GpuBaseFilter.hpp"

class GpuGaussianBlurFilter : public GpuBaseFilter {
public:
    virtual int getTypeId() { return FILTER_TYPE_GAUSSIANBLUR; }

    GpuGaussianBlurFilter()
    {
        GAUSSIAN_BLUR_VERTEX_SHADER ="...";
        GAUSSIAN_BLUR_FRAGMENT_SHADER ="...";
    }

    void init() {
        GpuBaseFilter::init(GAUSSIAN_BLUR_VERTEX_SHADER.c_str(), GAUSSIAN_BLUR_FRAGMENT_SHADER.c_str());
        mWidthFactorLocation = glGetUniformLocation(getProgram(), "widthFactor");
        mHeightFactorLocation = glGetUniformLocation(getProgram(), "heightFactor");
        mSampleOffsetLocation = glGetUniformLocation(getProgram(), "offset");
        mUniformConvolutionMatrix = glGetUniformLocation(getProgram(), "convolutionMatrix");
        mSampleOffset = 0.0f;
        // 高斯滤波的卷积核
        convolutionKernel = new GLfloat[9]{
                0.0947416f, 0.118318f, 0.0947416f,
                0.118318f,  0.147761f, 0.118318f,
                0.0947416f, 0.118318f, 0.0947416f,
        };
    }
    void onOutputSizeChanged(int width, int height) {
        GpuBaseFilter::onOutputSizeChanged(width, height);
        glUniform1f(mWidthFactorLocation, 1.0f / width);
        glUniform1f(mHeightFactorLocation, 1.0f / height);
    }
    void setAdjustEffect(float percent) {
        mSampleOffset = range(percent * 100.0f, 0.0f, 3.0f);
        // 动态修正采样半径
    }

    void onDraw(GLuint SamplerY_texId, GLuint SamplerU_texId, GLuint SamplerV_texId,
                void* positionCords, void* textureCords)
    {
        if (!mIsInitialized)
            return;
        glUseProgram(mGLProgId);
        // 传递高斯滤波的卷积核
        glUniformMatrix3fv(mUniformConvolutionMatrix, 1, GL_FALSE, convolutionKernel);
        glUniform1f(mSampleOffsetLocation, mSampleOffset);
        glUniform1f(mWidthFactorLocation, 1.0f / mOutputWidth);
        glUniform1f(mHeightFactorLocation, 1.0f / mOutputHeight);

        glVertexAttribPointer(mGLAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, positionCords);
        glEnableVertexAttribArray(mGLAttribPosition);
        glVertexAttribPointer(mGLAttribTextureCoordinate, 2, GL_FLOAT, GL_FALSE, 0, textureCords);
        glEnableVertexAttribArray(mGLAttribTextureCoordinate);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, SamplerY_texId);
        glUniform1i(mGLUniformSampleY, 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, SamplerU_texId);
        glUniform1i(mGLUniformSampleU, 1);
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, SamplerV_texId);
        glUniform1i(mGLUniformSampleV, 2);
        // onDrawArraysPre
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        glDisableVertexAttribArray(mGLAttribPosition);
        glDisableVertexAttribArray(mGLAttribTextureCoordinate);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
};
#endif // GPU_GAUSSIANBLUR_FILTER_HPP

Ce qui précède est la réalisation du filtrage gaussien.L'effet de pratique vidéo est transformé en gif avec une très faible reconnaissance, donc je ne le mettrai pas ici. Si vous êtes intéressé, vous pouvez exécuter le projet de démonstration pour voir l'effet.

Adresse du projet: https://github.com/MrZhaozhirong/NativeCppApp  Mean Blur Filter cpp / gpufilter / filter / GpuGaussianBlurFilter.hpp

C'est tout.

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

 

 

Référence de citation

Algorithme de distribution gaussienne   https://www.cnblogs.com/invisible2/p/9177018.html

Je suppose que tu aimes

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