Page 1 sur 2

Détection de contours rustique

Publié : mar. 10/avr./2012 20:49
par Huitbit
Hello,

Petit programme sans grande prétention sur le traitement de l'image.
Depuis plusieurs mois, je fais du Java (stage éducation nationale).
Maintenant, je comprends mieux les ";{ ;}" dans Pb :mrgreen:
A un moment donné, on a fait mumuse avec les filtres.
Le filtre est ici une petite matrice 3x3 que l'on va appliquer à chaque pixel de l'image.
On stocke le résultat dans une autre matrice "imageFiltrée".
Je suis retombé sur un truc que G-Rom avait utilisé, le filtre de Sobel.
Comme j'en aurai besoin quand je reviendrai sur "Perlin", j'ai fait quelques tests.
J'ai mis en forme avec PBSyntax de LSI (Merci M'sieur)!

Normalement ça fonctionne.
N'hésitez pas à trifouiller dans le filtre pour modifier le résultat.
Si vous avez des questions ou des remarques...

Code : Tout sélectionner

;**********************************************************
;Détection des contours avec le filtre de Sobel
;Auteur Huitbit
;Février 2012
;PureBasic 4.60 (Windows - x86)
;***********************************************************

#facteurDeNormalisation = 5.657 ; 5.657 = 4*Sqr(2)  facteur de normalisation pour le filtre de Sobel  
;dans l'expression :
;imageFiltree(x, y) = Sqr(imageFiltreX(x, y) * imageFiltreX(x, y) + imageFiltreY(x, y) * imageFiltreY(x, y))/ #facteurDeNormalisation
;les valeurs maximales pour les composantes X et Y sont 4*255
; => sans normalisation, la valeur finale maximale serait sqr(4²*255² + 4²*255²) = sqr(2*4²*255²) = 4*sqr(2)*255
; valeur supérieure à 255 et donc incorrecte

;On applique à un élément M(i,j) de la matrice M() le filtre choisi et on met le résultat dans c(i,j)
;On renvoie tout ça au programme principal
Procedure ConvolutionParMatrice3x3(i.i, j.i, Array filtre.i(2), Array M.i(2))
	cij.i ; composante de matrice qui sera renvoyée au programme
	For l = -1 To 1 ; lignes
		For k = -1 To 1 ; colonnes
			cij = cij + filtre(l + 1, k + 1) * M(i + l, j + k)
		Next k
	Next l
	ProcedureReturn cij
EndProcedure

;-PROGRAMME PRINCIPAL

If OpenWindow(0, 100, 100, 800, 600, "PureBasic - Image")
  
  ;-chargement de l'image à filtrer
	title$ = "Choisissez une image au format JPEG, PNG ou BMP"
	image$ = OpenFileRequester(title$, "", "*.JPEG ; *.PNG ; *.BMP", 1, #PB_Requester_MultiSelection)
	UseJPEGImageDecoder()
	UsePNGImageDecoder()
	If image$<> ""
		LoadImage(0, image$)
		height = ImageHeight(0)
		width = ImageWidth(0)
		
		ResizeWindow(0, 100, 100, width, height)
		
		Dim imageTableau.i(width - 1, height - 1)
		Dim imageFiltrex.i(width - 1, height - 1)
		Dim imageFiltreY.i(width - 1, height - 1)
		Dim imageFiltree.i(width - 1, height - 1)
		
		;-Filtres de Sobel
		;--selon X
		Dim filtreX.i(2, 2)
		filtreX(0, 0) = 1
		filtreX(1, 0) = 2
		filtreX(2, 0) = 1
		filtreX(0, 1) = 0
		filtreX(1, 1) = 0
		filtreX(2, 1) = 0
		filtreX(0, 2) = -1
		filtreX(1, 2) = -2
		filtreX(2, 2) = -1
		;--selon Y
		Dim filtreY.i(2, 2)
		filtreY(0, 0) = 1
		filtreY(1, 0) = 0
		filtreY(2, 0) = -1
		filtreY(0, 1) = 2
		filtreY(1, 1) = 0
		filtreY(2, 1) = -2
		filtreY(0, 2) = 1
		filtreY(1, 2) = 0
		filtreY(2, 2) = -1
				
		;- transformation n/b
		StartDrawing(ImageOutput(0))
			For i = 0 To width - 1
				For j = 0 To height - 1
					imageTableau(i, j) = (Red(Point(i, j)) + Green(Point(i, j)) + Blue(Point(i, j))) / 3
				Next j
			Next i
		StopDrawing() ; This is absolutely needed when the drawing operations are finished !!! Never forget it !
		
		;- application des  filtres selon X et Y
		For x = 1 To width - 2
			For y = 1 To height - 2
				imageFiltreX(x, y) = ConvolutionParMatrice3x3(x, y, filtreX(), imageTableau())
				imageFiltreY(x, y) = ConvolutionParMatrice3x3(x, y, filtreY(), imageTableau())
				;-Image finale normalisée
				imageFiltree(x, y) = Sqr(imageFiltreX(x, y) * imageFiltreX(x, y) + imageFiltreY(x, y) * imageFiltreY(x, y)) / #facteurDeNormalisation
			
			Next y
		Next x
		
		;-Création de l'image filtrée (en réalité le négatif pour une meilleure visibilité(choix arbitraire)
		If CreateImage(1, width, height)
		  
			StartDrawing(ImageOutput(1))
				For i = 0 To width - 1
					For j = 0 To height - 1
						niv = imageFiltree(i, j)
						Box(i, j, 1, 1, RGB(255 - niv, 255 - niv, 255 - niv))
					Next j
				Next i
				
			StopDrawing()
		EndIf
	Else
		End
		
		
	EndIf
	
	Repeat
		EventID = WaitWindowEvent()
		
		If EventID = #PB_Event_Repaint
			StartDrawing(WindowOutput(0))
				DrawImage(ImageID(1), 0, 0)
			StopDrawing()
		EndIf
		
	Until EventID = #PB_Event_CloseWindow ; If the user has pressed on the close button
	
EndIf

End ; All the opened windows are closed automatically by PureBasic
Hasta la vista !

Re: Détection de contours rustique

Publié : mar. 10/avr./2012 21:33
par lepiaf31
Hum ca me rappel un des mes cours ca tiens =).
Ca à l'air interessant, je pense m'y plonger un peu plus demain.

GG :D

Re: Détection de contours rustique

Publié : mar. 10/avr./2012 22:35
par Ar-S
Le résultat est vraiment pas mal !

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 8:16
par Kwai chang caine
C'est vraiment splendide...j'suis toujours épaté par ce genre de code "magique". 8O
Marche nikel ici :wink:

Cette fois un petit peu moins, car j'avais déjà vu cet effet avec un code de ATHOW , il y a quelques années....
Alors attention, j'y connais rien en math donc je sais pas si c'est le même style de code, mais le resultat est sensiblement le meme
http://www.purebasic.fr/french/viewtopi ... 671#p62671

En attendant, vous et vos mathematiques, vous me faites rever, moi qui ai du mal a faire une addition sans faute :oops:
En plus on peut se la peter en disant qu'on a un peu de talent dans le dessin...on prend n'importe quelle photo et hop ...grace a vos deux codes...une imprimante....et leonard il a plus qu'a retourner jouer au legos :mrgreen:

Merci beaucoup du partage... 8)

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 9:17
par Le Soldat Inconnu
Terrible ce truc, faut que je zieute ça de plus près :D

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 9:51
par Cool Dji
Yeah, bravo Huitbit => ça peut faire des effets sympa dans un jeu en faisant clignoter un écran avec image normale et l'image épurée-calculée :D

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 14:49
par Huitbit
Hugh !

Tant mieux si ça peut servir :D .
Avec des pointeurs(façon G-Rom), ce n'est pas beaucoup plus dur est c'est plus rapide (vu que je l'utiliserai pour des cartes créées avec Perlin) !

@KCC, c'est bien le même principe.

L'idée de départ est assez simple.
On calcule un gradient(grossièrement la manière dont une composante (ou pour le code ci-dessus, le niveau de gris) varie) sur une zone de 3x3 pixels
puis on garde la valeur moyenne à l'emplacement du pixel central.

Exemple :
Soient a, b ,c ,d ,e et f , 9 valeurs allant de 0 à 255
Le pixel traité correspond à la valeur e.

a b c
d e f
g h i

La variation de valeur par rapport à l'horizontale est (f - d) / 2 ("2" car le calcul de la variation se fait sur une distance de 2 pixels)
Si on passe du noir au blanc, on aura (255 - 0 ) / 2
Du blanc au noir, on aura une valeur négative (0 - 255) /2.
En fait, c'est la valeur approchée de la dérivé en math.
Le filtre va travailler simultanément sur les 3 lignes.
A l'emplacement de e, on affectera la valeur (c - a) / 2 + (f - d) / 2 + (i - g) / 2
Un filtre qui calcule le gradient peut donc s'écrire :
-1/2 0 1/2
-1/2 0 1/2
-1/2 0 1/2

Car avec un calcul approprié (attention, ce n'est pas un produit de matrice classique)
on a bien la valeur cherchée eFiltré = -1/2*a + 0*b + 1/2*c - 1/2*d + 0*e +.........
Ensuite, il faut normaliser car si on veut une image, les valeurs ne doivent pas dépasser 255.
Ici, la valeur maximale peut être 3 * 255/2.
Pour normaliser, on va diviser par 3/2

Vu que l'on normalise, on peut très bien travailler avec des entiers positifs :
-1 0 1
-1 0 1
-1 0 1
puis diviser par trois (1 + 1 + 1 = 3) pour normaliser.

Le filtre de Sobel insiste sur ligne principale en mettant un 2
-1 0 1
-2 0 2
-1 0 1
On normalise en divisant par 1 + 2 + 1 = 4

En conjugant les filtres selon l'horizontale et la verticale (Rappel : calcul de la norme d'un vecteur : l = Sqr(x ² + y ²)), on obtient le résultat final.
Remarque : on peut voir que le filtre suivant(celui utilisé dans le code !) donne la même chose après la normalisation
1 0 -1
2 0 -2
1 0 -1


Voilà, c'était pour faire avancer le schmilblick !


Hasta la vista !
Il existe toutes sortes de filtres (en diagonale, ...).

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 14:52
par lepiaf31
Pourrais-tu m'éclairer sur une ligne :

Code : Tout sélectionner

imageFiltree(x, y) = Sqr(imageFiltreX(x, y) * imageFiltreX(x, y) + imageFiltreY(x, y) * imageFiltreY(x, y)) / 4
Le sqr(...) c'est une la norme mais je ne comprends pas le '/4'.

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 14:59
par Huitbit
Hi,

Je divise par quatre car je n'ai pas normalisé les deux matrices filtreX et filtreY.
En effet la valeur maximale possible est 4*255 pour chaque filtre
Pour faire les choses proprement j'aurai dû diviser par sqr(4²+4²) c'est à dire 4*sqr(2).

Bien vu !

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 14:59
par lepiaf31
HA oui très juste autant pour moi =)
Merci ! =)

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 15:15
par Huitbit
Re Hugh !

Le plus propre, c'est de normaliser au fur et à mesure les matrices.

Sinon c'est 4*sqr(2) !
(Si tu divises seulement par 4 la matrice finale, tu peux avoir des valeurs supérieures à 255)

Le plus pratique, c'est d'ajuster le coefficient en fonction de la valeur maximale obtenue et du résultat souhaité.

Hasta la vista !

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 15:35
par Kwai chang caine
Merci pour toutes ces explications.
Y'a pas a tortiller du... pour chi..droit...la bosse des math on l'a ou on l'a pas.
Moi je l'ai eu à force de me tater sur la tete...mais celle la...ell m'a jamais aidé :lol:

En tout cas encore bravo 8)

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 18:38
par Huitbit
Petite correction suite à la remarque de Lepiaf31

Code : Tout sélectionner

#facteurDeNormalisation = 5.657 ; 5.657 = 4*Sqr(2)  facteur de normalisation pour le filtre de Sobel  
;dans l'expression :
;imageFiltree(x, y) = Sqr(imageFiltreX(x, y) * imageFiltreX(x, y) + imageFiltreY(x, y) * imageFiltreY(x, y)) / #facteurDeNormalisation
;les valeurs maximales pour les composantes X et Y sont 4*255
; => sans normalisation, la valeur finale maximale serait sqr(4²*255² + 4²*255²) = sqr(2*4²*255²) = 4*sqr(2)*255
; valeur supérieure à 255 et donc incorrecte
Hasta la vista !

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 18:53
par lepiaf31
Et autre chose: pourquoi calculer la norme pour l'image finale et pas simplement une bête moyenne des deux valeurs ? (je ne sais pas si je suis clair ^^ )

Re: Détection de contours rustique

Publié : mer. 11/avr./2012 19:19
par Huitbit
Hugh !

Car tu travailles avec des vecteurs (une dimension selon X, l'autre selon Y), seule la norme tient compte du sens et de la direction des vecteurs !

Hasta la vista !