Algorithme de biseau alpha bitmap?

14

Je cherche à créer un algorithme qui ajoute un effet de biseau à un bitmap en utilisant son alpha comme carte de relief.

Comment pourrais-je faire quelque chose comme ça? J'ai essayé l'éclairage spéculaire mais je n'obtiens que le reflet et non l'ombre.

Voici l'effet dont je parle (réalisé à l'aide de Photoshop): entrez la description de l'image ici

Tous ces éléments ont été effectuées en utilisant size: 30px(La profondeur du biseau à partir du contour de l'image bitmap) angle 130, altitude 50.

De gauche à droite, de haut en bas:

  1. Ciseau biseau dur
  2. Biseau souple biseauté
  3. Biseau lisse
  4. Ciseau dur avec soften: 16px- un biseau flou?

J'essaie de créer chacun de ces effets, comment pourrais-je créer le biseau de base? et que dois-je obtenir pour chacun de ces biseaux

Shedokan
la source

Réponses:

10

Cela peut être accompli avec une convolution de la transformée de distance.

Utilisez une transformation de distance sur le bord du masque. Limitez ensuite cette transformation de distance pour supprimer les valeurs au-delà d'une certaine distance. Je pense que le secret pour obtenir l'ombrage est de convoluer le résultat de la transformation de distance avec un noyau qui ressemble à ceci:

[ -1.0  -1.0  -1.0
  -1.0   0.0   0.0
  -1.0   0.0   1.0 ]

Cela devrait vous aider à démarrer dans la bonne direction:

#include "opencv/cv.h"
#include "opencv/highgui.h"

using namespace cv;
using namespace std;

int main() {
    Mat mask, dist, bevel;
    mask = Mat::zeros(200, 400, CV_8U);
    rectangle(mask, Point(30,30), Point(180,180), Scalar(255), -1);
    circle(mask, Point(30,30), 50, Scalar(0), -1);
    circle(mask, Point(180,180), 50, Scalar(0), -1);
    circle(mask, Point(300,100), 75, Scalar(255), -1);
    imshow("1",mask);

entrez la description de l'image ici

    //find edges and invert image for distance transform
    Canny(mask, dist, 50, 150);
    dist = 255-dist;
    distanceTransform(dist, dist, CV_DIST_L2, CV_DIST_MASK_5);
    threshold(dist, dist, 20, 20, CV_THRESH_TRUNC);
    blur(dist, dist, Size(3,3));
    dist.convertTo(bevel, CV_8U);
    equalizeHist(bevel, bevel);
    imshow("2",bevel);

entrez la description de l'image ici

    //convolve with secret sauce
    float d[] = {-1,-2,-3,
                 -2, 0, 0,
                 -3, 0, 1 };
    Mat kernel(3, 3, CV_32F, d);
    kernel = kernel - mean(kernel)[0];
    filter2D(dist, dist, CV_32F, kernel);

    //normalize filtering result to [-1, 1]
    double maxVal;
    minMaxLoc(dist, NULL, &maxVal);
    dist = 128 * dist / maxVal;

    //convert and display result
    dist.convertTo(bevel, CV_8U, 1, 128);
    bevel = bevel.mul(mask)/255;
    imshow("3", bevel);

entrez la description de l'image ici

    waitKey(0);
}
Matt M.
la source
Ils ont l'air génial! Celui avec adoucir ressemble à un flou, mais comment pourrais-je faire le "Smooth Bevel" d'eux?
Shedokan
Je crois que Smooth Bevel brouille le masque de distance avant la convolution, et Soften brouille le résultat après la convolution.
Matt M.
4

Le biseau et l'embossage de Photoshop fonctionnent de manière prévisible:

1) Calculer une transformation de distance dans une image à canal unique 8 bits temporaire

  • Chisel utilise la transformation de distance euclidienne avec une métrique de chanfrein (3x3, 5x5 ou 7x7 selon la taille). Vous pouvez utiliser une transformation de distance euclidienne exacte si vous le souhaitez, je préfère celle de Meijster car elle peut être rendue anti-aliasée ("Un algorithme général pour calculer les transformations de distance en temps linéaire", MEIJSTER).

  • Smooth Bevel utilise une transformation de distance Chanfrein 5-7-11 suivie de deux applications d'un flou de boîte, pour produire la carte de relief.

2) Appliquer le mappage de relief à l'image de transformation de distance intermédiaire. La technique originale de Blinn convient.

3) Pour l'adoucissement, vous pouvez effectuer une convolution sur les normales de surface ou vous pouvez les filtrer à l'aide d'un noyau.

4) En utilisant la carte de relief, les normales de surface sont combinées avec la source de lumière globale pour calculer l'intensité d'éclairage en tant que valeur de -1 à 1 où les valeurs négatives sont des ombres, les valeurs positives sont des reflets et la valeur absolue est la magnitude de la lumière la source.

5) Deux images temporaires à canal unique de 8 bits sont calculées, l'une à partir des intensités de surbrillance et l'autre à partir des ombres. À partir de là, il est trivial d'utiliser chaque masque pour teinter le calque en utilisant une couleur, un mode de fusion et une opacité - un masque pour les reflets et l'autre pour les ombres.

Le code source Visual Basic pour implémenter une partie de ceci peut être trouvé ici:

http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=51640&lngWId=1

Veuillez visiter mon projet open source LayerEffects pour en savoir plus:

https://github.com/vinniefalco/LayerEffects.git

J'espère que ça aidera quelqu'un.

Vinnie Falco
la source
Merci, je suis très intéressé par la transformation de distance gaussienne que vous avez mentionnée, connaissez-vous un code disponible? Je n'arrive pas à bien lire les formules. :(
Shedokan
Je n'ai rien trouvé sur le GDT, à part les informations que j'ai déjà postées.
Vinnie Falco
Qu'entendez-vous par «2) Appliquer le mappage de relief à l'image de transformation de distance»? La transformation de distance donne des distances (1 nombre pour chaque pixel), tandis que le mappage de relief nécessite des normales (2 nombres pour chaque pixel) ... Etes-vous sûr de savoir de quoi vous parlez?
Ivan Kuckir
@IvanKuckir J'aurais dû être plus clair - une normale de surface peut être calculée en traitant la transformation de distance comme une carte de hauteur et en calculant dx / dy à partir des valeurs verticales et horizontales voisines. Ces deux nombres fournissent la normale requise pour le mappage de relief.
Vinnie Falco