Changer couleur image [Résolu]

Messages postés
70
Date d'inscription
vendredi 27 décembre 2002
Dernière intervention
29 septembre 2018
- - Dernière réponse : vb95
Messages postés
1725
Date d'inscription
samedi 11 janvier 2014
Dernière intervention
11 décembre 2018
- 14 janv. 2018 à 16:05
Bonsoir et Merci pour le temps consacré et l'aide apporté
Voilà ce que je souhaiterais:
Dans mon programme je charge de petite image 100 pixel par 100 pixel;
Avec le code je récupère la couleurs du pixels:

For i = 0 To monImage.Width - 1
For j = 0 To MonImage.Height - 1
LaCouleur = monImage.GetPixel(i, j)
Next i
Next j


A partir de là je souhaiterais changer la couleur de chaque pixel avec une palette que j'ai crée donc je charge ces couleurs dans une liste comme ceci:

Private MaPalette As New List(Of Color) From {Color.FromArgb(236, 237, 237), Color.FromArgb(240, 232, 185), Color.FromArgb(240, 185, 1), Color.FromArgb(230, 79, 39), Color.FromArgb(182, 49, 54), Color.FromArgb(225, 136, 159), Color.FromArgb(105, 74, 130), Color.FromArgb(44, 70, 144), Color.FromArgb(48, 92, 176), Color.FromArgb(37, 104, 71)}


Donc à partir de là je voudrais que mon programme compare chaque pixel et le remplace avec la couleur la plus proche de ma palette

Voilà ce que j'ai déjà tenté ;mais j'ai une erreur de dépassement de capacité.
Voici le code:
For i = 0 To monImage.Width - 1
                For j = 0 To monImage.Height - 1
                    LaCouleur = monImage.GetPixel(i, j)
                    Dim lememe As Integer = 0
                    Dim oldmeme As Integer = 0
                Dim oldval As Double = 0
                Dim val As Double= Math.Sqrt((LaCouleur.A - MaPalette(0).A) * (LaCouleur.A - MaPalette(0).A) + (LaCouleur.G - MaPalette(0).G) * (LaCouleur.G - MaPalette(0).G) + (LaCouleur.B - MaPalette(0).B) * (LaCouleur.B - MaPalette(0).B) + (LaCouleur.R - MaPalette(0).R) * (LaCouleur.R - MaPalette(0).R))
                For idx As Integer = 0 To MaPalette.Count - 1
                    Dim val2 As Double = Math.Sqrt((LaCouleur.A - MaPalette(idx).A) * (LaCouleur.A - MaPalette(idx).A) + (LaCouleur.G - MaPalette(idx).G) * (LaCouleur.G - MaPalette(idx).G) + (LaCouleur.B - MaPalette(idx).B) * (LaCouleur.B - MaPalette(idx).B) + (LaCouleur.R - MaPalette(idx).R) * (LaCouleur.R - MaPalette(idx).R))
                    If val2 <= val Then
                            oldval = val
                            val = val2
                            oldmeme= meme
                            meme = idx
                        End If
                    Next idx
                    monimage2.SetPixel(i, j, MaPalette(meme))
                Next j
            Next i

Voilà à partir de là je suis bloqué ;et je ne sais pas pourquoi j'ai un dépassement de capacité
je vous remercie d'avance pour l'aide apporté et les conseils donnés
Afficher la suite 

Votre réponse

2 réponses

Messages postés
1725
Date d'inscription
samedi 11 janvier 2014
Dernière intervention
11 décembre 2018
0
Merci
Bonjour


Private MaPalette As New List(Of Color) From {Color.FromArgb(236, 237, 237), Color.FromArgb(240, 232, 185), Color.FromArgb(240, 185, 1), Color.FromArgb(230, 79, 39), Color.FromArgb(182, 49, 54), Color.FromArgb(225, 136, 159), Color.FromArgb(105, 74, 130), Color.FromArgb(44, 70, 144), Color.FromArgb(48, 92, 176), Color.FromArgb(37, 104, 71)}


Dans la déclaration de ta palette tu ne précises pas le paramètre A : donc celui est implicitement égal à 255 ( soit le plus opaque)

Chaque couleur est définie par 4 paramètres : A , R G et B ( chacun de ces paramètres est sur 8 bits( de 0 à 255 comme plage de valeurs)
Avant d'aller plus loin peux-tu expliquer la formule avec Math.Sqrt : tu fais l'addition de 4 valeurs et chacune de ces valeurs serait l'élévation au carré d'une composante de la couleur - la même composante de la palette

Math.Sqrt( ( (Acouleur - Apalette) ^2) + ( (Rcouleur - Rpalette) ^2) + ( (Gcouleur - Gpalette) ^2) + ( (Bcouleur - Bpalette) ^2) )



La théorie, c'est quand on sait tout et que rien ne fonctionne. La pratique, c'est quand tout fonctionne et que personne ne sait pourquoi. 
vb95
Messages postés
1725
Date d'inscription
samedi 11 janvier 2014
Dernière intervention
11 décembre 2018
> CGSI3
Messages postés
417
Date d'inscription
vendredi 22 février 2008
Dernière intervention
7 janvier 2018
-
Bonjour CGSI3
Meilleurs vœux aussi pour toi et ta famille !
Juste une remarque
Tu dis :

val = (255 * 255) + (255 * 255) + (255 * 255) + 1 = 195 076

pourrais peut être tout simplement être remplacer par

val = Integer.MaxValue = 2 147 483 647 en DoNet


Il faudra aussi que je pose la question à cs_roro69 pour savoir comment il a déduit cette valeur de 195 076 ( car j'ai un sérieux doute ) : je mettrais plutôt 255 * 255 * 255 ( soir 16 581 375 ) qui est la couleur du blanc sans tenir compte du paramètre Alpha de la couleur .

A++
cs_roro69
Messages postés
70
Date d'inscription
vendredi 27 décembre 2002
Dernière intervention
29 septembre 2018
-
Bonsoir vb95 ;et CGSI3
Pour mes besoins, je n'ai pas besoin de prendre en compte la valeur alpha d'une couleur. car dans ma palette de couleur je n'ai aucune couleur qui en est pourvu
Pour répondre au sujet de la valeur ; j'ai pris en considération la formule pour calculer la distance des couleurs.
vu que pour calculé la distance entre chaque composante on utilise le carré de chaque composante que l'on additionne; j'ai juste appliqué cette même règles à ma valeur d''entrée.
Bonne soiré à vous
vb95
Messages postés
1725
Date d'inscription
samedi 11 janvier 2014
Dernière intervention
11 décembre 2018
> cs_roro69
Messages postés
70
Date d'inscription
vendredi 27 décembre 2002
Dernière intervention
29 septembre 2018
-
Bonjour cs_roro69
C'est exact tu as raison pour la formule .
Bonjour CGSI3
On peut mettre Integer.MaxValue comme valeur de départ : de toute façon la première valeur pour une couleur sera toujours inférieure
Bon weekend à vous deux !
CGSI3
Messages postés
417
Date d'inscription
vendredi 22 février 2008
Dernière intervention
7 janvier 2018
-
Bonsoir,

je vous propose de lire ceci pour la vitesse d'execution
http://mfranc.com/programming/operacje-na-bitmapkach-net-1/

et ne retiendrai surtout que la conclusion:
<< GDI+ Lockbits are almost seven times faster than GetPixel SetPixel method >>
Sachant au passage qu'il y a bcp plus rapide que GDI ...

et ceci pour la comparaison de couleur
https://en.wikipedia.org/wiki/Color_difference

J'ai donc retravaillé le code que tu nous a proposé et j'ai tout transformé en class


Public Class Bitmap_Class
Public BaseImage As Bitmap

#Region "Champs privés"
Private ImageAddress As IntPtr
Private ImageContent As System.Drawing.Imaging.BitmapData
Private ImageBuffer() As Integer
#End Region

#Region "Propriétés publiques"

Public ReadOnly Property Clone() As Bitmap
Get
Return BaseImage.Clone(New Rectangle(0, 0, BaseImage.Width, BaseImage.Height), BaseImage.PixelFormat)
End Get
End Property

#End Region

Public Sub New(ByRef B As Bitmap)
BaseImage = B
End Sub

Public Sub New(ByRef width As Integer, ByRef Height As Integer, Optional ByRef Format As System.Drawing.Imaging.PixelFormat = Imaging.PixelFormat.Format32bppRgb)
BaseImage = New Bitmap(width, Height, Format)
End Sub

Private Sub LockTheImage()
' ----- Create a stable (locked) area in memory. It will store 32-bit color images.
ReDim ImageBuffer((BaseImage.Width * BaseImage.Height) - 1)
ImageContent = BaseImage.LockBits( _
New Rectangle(0, 0, BaseImage.Width, BaseImage.Height), _
System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppRgb)
ImageAddress = ImageContent.Scan0

' ----- Associate the buffer and the locked memory.
Runtime.InteropServices.Marshal.Copy(ImageAddress, ImageBuffer, 0, BaseImage.Width * BaseImage.Height)
End Sub

Private Sub UnlockTheImage()
' ----- Unlock the memory area.
Runtime.InteropServices.Marshal.Copy(ImageBuffer, 0, ImageAddress, BaseImage.Width * BaseImage.Height)
BaseImage.UnlockBits(ImageContent)
ImageContent = Nothing
ReDim ImageBuffer(0)
End Sub

Public Function BonneCouleur(MesCouleurs As List(Of Color)) As Bitmap_Class
Dim bmp2 As New Bitmap_Class(Clone)
bmp2.LockTheImage() ' ----- Lock the image for speed.
Dim bestindex As Integer, val As Integer, val1 As Integer
For cpt As Integer = 0 To bmp2.ImageBuffer.Count - 1
bestindex = 0 : val = Integer.MaxValue
For k = 0 To MesCouleurs.Count - 1
val1 = Distance(bmp2.ImageBuffer(cpt), MesCouleurs(k))
If val1 < val Then
val = val1 : bestindex = k
End If
Next k
bmp2.ImageBuffer(cpt) = ToInteger(MesCouleurs(bestindex))
Next
bmp2.UnlockTheImage() ' ----- Finished. Unlock the image.
Return bmp2
End Function

Private Function ToColor(ByVal onePixel As Integer) As Color
Dim A As Byte, R As Byte, G As Byte, B As Byte
A = (onePixel >> 24) And &HFF ' ----- Extract the color values.
R = (onePixel >> 16) And &HFF
G = (onePixel >> 8) And &HFF
B = onePixel And &HFF
Return System.Drawing.Color.FromArgb(A, R, G, B)
End Function

Private Function ToInteger(ByVal ColorVB As Color) As Integer
Dim A As Integer, R As Integer, G As Integer, B As Integer
A = ColorVB.A : R = ColorVB.R : G = ColorVB.G : B = ColorVB.B
Dim retour As Integer = (A << 24) + (R << 16) + (G << 8) + B
Return retour
End Function

Public Function Distance(ByVal Color1 As Integer, ByVal Color2 As Color) As Integer
Dim A As Integer, R As Integer, G As Integer, B As Integer
'A = (Color1 >> 24) And &HFF ' ----- Extract the color values.
R = (Color1 >> 16) And &HFF
G = (Color1 >> 8) And &HFF
B = Color1 And &HFF

Dim A2 As Integer, R2 As Integer, G2 As Integer, B2 As Integer
'A2 = Color2.A ' ----- Extract the color values.
R2 = Color2.R
G2 = Color2.G
B2 = Color2.B

Dim Rdiff As Integer, Gdiff As Integer, Bdiff As Integer
Rdiff = Math.Abs(R2 - R)
Gdiff = Math.Abs(G2 - G)
Bdiff = Math.Abs(B2 - B)

Return (Rdiff * Rdiff) * 2 + (Gdiff * Gdiff) * 4 + (Bdiff * Bdiff) * 3
End Function

End Class



Et l'Exemple pour l'utiliser


Dim MaPalette As New List(Of Color) From {Color.FromArgb(236, 237, 237), Color.FromArgb(240, 232, 185), Color.FromArgb(240, 185, 1), Color.FromArgb(230, 79, 39), Color.FromArgb(182, 49, 54), Color.FromArgb(225, 136, 159), Color.FromArgb(105, 74, 130), Color.FromArgb(44, 70, 144), Color.FromArgb(48, 92, 176), Color.FromArgb(37, 104, 71)}


Dim img As Bitmap
img = Image.FromFile("c:\*********.jpg")
Dim T As New Bitmap_Class(img)
PictureBox1.Image = T.BonneCouleur(MaPalette).BaseImage
PictureBox2.Image = T.Clone



Tu pourra ainsi comparer la vitesse de l'exercice par ce moyen. (Lockbits)
Désolé pour la complexité du code mais il permet de faire le tour de beaucoup d'opérations liées au couleurs et au Bitmap.
Tu aura ainsi de nombreuses pistes a explorer ..

A oui j oubliais tu dois convertir des que tu peux les couleurs (byte) en (Integer) pour éviter un dépassement de capacité dans les calculs.

Bonne Prog
CGSI3
vb95
Messages postés
1725
Date d'inscription
samedi 11 janvier 2014
Dernière intervention
11 décembre 2018
> CGSI3
Messages postés
417
Date d'inscription
vendredi 22 février 2008
Dernière intervention
7 janvier 2018
-
Bonsoir CGSI3
C'est vrai qu'avec l'image en mémoire cela améliore la vitesse d'éxécution
Beau travail ! Bravo
Commenter la réponse de vb95
Messages postés
70
Date d'inscription
vendredi 27 décembre 2002
Dernière intervention
29 septembre 2018
0
Merci
Bonjour CGSI3
Super code ;j'ai mis dans mon application c'est excellent .
Plus qu'a finaliser mon application de perle (un peu plus compliqué que je ne pensais ; difficile de travailler des images )
Merci beaucoup a toi et a vb95
Meric pour votre aide et votre temps consacré
--
vb95
Messages postés
1725
Date d'inscription
samedi 11 janvier 2014
Dernière intervention
11 décembre 2018
-
Bonjour !
Si nos réponses t'ont satisfait utilises la roue dentée en haut de ton message pour mettre le sujet en résolu !
Merci
Commenter la réponse de cs_roro69

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.