web-dev-qa-db-fra.com

Qu'est-ce que l'orientation et l'amplitude du gradient

J'étudie actuellement un module en vision par ordinateur appelé détection des contours. J'essaie de comprendre la signification de l'orientation et de l'amplitude du gradient.

39
Jack welch

Comme expliqué par Dima dans son réponse , vous devez être familier avec le concept mathématique de gradient afin de mieux comprendre le gradient dans le domaine de traitement d'image.

Ma réponse est basée sur la réponse de mevatron à cette question .

Vous trouverez ici une simple image initiale d'un disque blanc sur fond noir:

enter image description here

vous pouvez calculer une approximation du gradient de cette image. Comme Dima l'a expliqué dans sa réponse, vous avez deux composantes du dégradé, une horizontale et une verticale.

Les images suivantes vous montrent le composant horizontal:

enter image description here

il montre combien les niveaux de gris de votre image changent dans le sens horizontal (c'est le sens du x positif, balayant l'image de gauche à droite), ce changement est "encodé" dans le niveau de gris de l'image du composant horizontal : le niveau de gris moyen ne signifie aucun changement, les niveaux lumineux signifient le changement d'une valeur sombre à une valeur lumineuse, les niveaux sombres signifient un changement d'une valeur lumineuse à une valeur sombre. Ainsi, dans l'image ci-dessus, vous voyez la valeur la plus lumineuse dans la partie gauche du cercle car c'est dans la partie gauche de l'image initiale que vous avez la transition du noir au blanc qui vous donne le bord gauche du disque; de même, dans l'image ci-dessus, vous voyez la valeur la plus sombre dans la partie droite du cercle car c'est dans la partie droite de l'image initiale que vous avez la transition du blanc au noir qui vous donne le bord droit du disque. Dans l'image ci-dessus, la partie intérieure du disque et l'arrière-plan sont à un niveau de gris moyen car il n'y a aucun changement à l'intérieur du disque et en arrière-plan.

Nous pouvons faire des observations analogues pour la composante verticale, cela montre comment l'image change dans le sens vertical, c'est-à-dire en scannant l'image de haut en bas:

enter image description here

Vous pouvez maintenant combiner les deux composants afin d'obtenir l'amplitude du dégradé et l'orientation du dégradé.

L'image suivante est la magnitude du gradient:

enter image description here

Encore une fois, dans l'image ci-dessus, le changement de l'image initiale est codé dans le niveau de gris: ici, vous voyez que le blanc signifie un changement élevé dans l'image initiale tandis que le noir signifie aucun changement du tout. Ainsi, lorsque vous regardez l'image de l'amplitude du gradient, vous pouvez dire "si l'image est lumineuse, cela signifie un grand changement dans l'image initiale; si elle est sombre, cela signifie aucun changement ou très peu de changement".

L'image suivante est l'orientation du dégradé:

enter image description here

Dans l'image ci-dessus, l'orientation est à nouveau codée en niveaux de gris: vous pouvez penser à l'orientation comme l'angle d'une flèche pointant de la partie sombre de l'image vers la partie lumineuse de l'image; l'angle est référé à un cadre xy où le x va de gauche à droite tandis que le y va de haut en bas. Dans l'image ci-dessus, vous voyez tout le niveau de gris du noir (zéro degré) au blanc (360 degrés). Nous pouvons encoder les informations avec la couleur:

enter image description here

dans l'image ci-dessus, les informations sont encodées de cette manière:

rouge: l'angle est compris entre 0 et 90 degrés

cyan: l'angle est compris entre 90 et 180 degrés

vert: l'angle est compris entre 180 et 270 degrés

jaune: l'angle est compris entre 270 et 360 degrés

Ici, c'est le code C++ OpenCV pour produire les images ci-dessus.

Faites attention au fait que, pour le calcul de l'orientation, j'utilise la fonction cv::phase qui, comme expliqué dans le doc , donne un angle de 0 lorsque la composante verticale et la composante horizontale du gradient sont nulles; cela peut être pratique mais d'un point de vue mathématique est tout à fait faux car lorsque les deux composants sont nuls, l'orientation n'est pas définie et la seule valeur significative pour une orientation conservée dans un type C++ à virgule flottante est un NaN.

C'est tout simplement faux car une orientation à 0 degré, par exemple, est déjà liée à un bord horizontal et elle ne peut pas être utilisée pour représenter autre chose comme une région sans bords et donc une région où l'orientation n'a pas de sens.

// original code by https://stackoverflow.com/users/951860/mevatron
// see https://stackoverflow.com/a/11157426/15485
// https://stackoverflow.com/users/15485/uvts-cvs added the code for saving x and y gradient component 

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

Mat mat2gray(const cv::Mat& src)
{
    Mat dst;
    normalize(src, dst, 0.0, 255.0, cv::NORM_MINMAX, CV_8U);

    return dst;
}

Mat orientationMap(const cv::Mat& mag, const cv::Mat& ori, double thresh = 1.0)
{
    Mat oriMap = Mat::zeros(ori.size(), CV_8UC3);
    Vec3b red(0, 0, 255);
    Vec3b cyan(255, 255, 0);
    Vec3b green(0, 255, 0);
    Vec3b yellow(0, 255, 255);
    for(int i = 0; i < mag.rows*mag.cols; i++)
    {
        float* magPixel = reinterpret_cast<float*>(mag.data + i*sizeof(float));
        if(*magPixel > thresh)
        {
            float* oriPixel = reinterpret_cast<float*>(ori.data + i*sizeof(float));
            Vec3b* mapPixel = reinterpret_cast<Vec3b*>(oriMap.data + i*3*sizeof(char));
            if(*oriPixel < 90.0)
                *mapPixel = red;
            else if(*oriPixel >= 90.0 && *oriPixel < 180.0)
                *mapPixel = cyan;
            else if(*oriPixel >= 180.0 && *oriPixel < 270.0)
                *mapPixel = green;
            else if(*oriPixel >= 270.0 && *oriPixel < 360.0)
                *mapPixel = yellow;
        }
    }

    return oriMap;
}

int main(int argc, char* argv[])
{
    Mat image = Mat::zeros(Size(320, 240), CV_8UC1);
    circle(image, Point(160, 120), 80, Scalar(255, 255, 255), -1, CV_AA);

    imshow("original", image);

    Mat Sx;
    Sobel(image, Sx, CV_32F, 1, 0, 3);

    Mat Sy;
    Sobel(image, Sy, CV_32F, 0, 1, 3);

    Mat mag, ori;
    magnitude(Sx, Sy, mag);
    phase(Sx, Sy, ori, true);

    Mat oriMap = orientationMap(mag, ori, 1.0);

    imshow("x", mat2gray(Sx));
    imshow("y", mat2gray(Sy));

    imwrite("hor.png",mat2gray(Sx));
    imwrite("ver.png",mat2gray(Sy));

    imshow("magnitude", mat2gray(mag));
    imshow("orientation", mat2gray(ori));
    imshow("orientation map", oriMap);
    waitKey();

    return 0;
}
96

Le gradient d'une fonction de deux variables x, y est un vecteur des dérivées partielles dans les directions x et y. Donc, si votre fonction est f (x, y), le gradient est le vecteur (f_x, f_y). Une image est une fonction discrète de (x, y), vous pouvez donc également parler du gradient d'une image.

Le gradient de l'image a deux composantes: la dérivée x et la dérivée y. Vous pouvez donc le considérer comme des vecteurs (f_x, f_y) définis à chaque pixel. Ces vecteurs ont une direction atan (f_y/fx) et une amplitude sqrt (f_x ^ 2 + f_y ^ 2). Ainsi, vous pouvez représenter le gradient d'une image soit une image dérivée x et une image dérivée y, soit comme une image de direction et une image de magnitude.

21
Dima