Aujourd’hui, nous allons transformer une images en son dessin animé. Pour cela, nous allons construire une application python qui prend une image en entrée et applique divers filtres pour lui donner un aspect de dessin animé à l’aide d’OpenCV.
OpenCV est une bibliothèque multiplateforme utilisée pour la vision par ordinateur. Il comprend des applications telles que la capture et le traitement de vidéos et d’images. Il est principalement utilisé dans la transformation d’images, la détection d’objets, la reconnaissance faciale et de nombreuses autres applications étonnantes.
Avant de commencer, assurez-vous d'avoir installé les bibliothèques OpenCV (cv2), easygui, numpy, imageio, et Pillow. Le code ci-dessous permet de charger une image, de la "cartoonifier" et de la sauvegarder. Vous pouvez personnaliser le code selon vos besoins.
Le code complet:
import cv2
import easygui
import numpy as np
import imageio
import sys
import matplotlib.pyplot as plt
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import *
from PIL import ImageTk, Image
top = tk.Tk()
top.geometry('400x400')
top.title('Cartoonify Your Image!')
top.configure(background='white')
label = Label(top, background='#CDCDCD', font=('calibri', 20, 'bold'))
def upload():
ImagePath = easygui.fileopenbox()
cartoonify(ImagePath)
def cartoonify(ImagePath):
originalImage = cv2.imread(ImagePath)
originalImage = cv2.cvtColor(originalImage, cv2.COLOR_BGR2RGB)
if originalImage is None:
print("Can not find any image. Choose appropriate file")
sys.exit()
ReSized1 = cv2.resize(originalImage, (960, 540))
grayScaleImage = cv2.cvtColor(originalImage, cv2.COLOR_BGR2GRAY)
ReSized2 = cv2.resize(grayScaleImage, (960, 540))
smoothGrayScale = cv2.medianBlur(grayScaleImage, 5)
ReSized3 = cv2.resize(smoothGrayScale, (960, 540))
getEdge = cv2.adaptiveThreshold(smoothGrayScale, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
ReSized4 = cv2.resize(getEdge, (960, 540))
colorImage = cv2.bilateralFilter(originalImage, 9, 300, 300)
ReSized5 = cv2.resize(colorImage, (960, 540))
cartoonImage = cv2.bitwise_and(colorImage, colorImage, mask=getEdge)
ReSized6 = cv2.resize(cartoonImage, (960, 540))
images = [ReSized1, ReSized2, ReSized3, ReSized4, ReSized5, ReSized6]
fig, axes = plt.subplots(3, 2, figsize=(8, 8), subplot_kw={'xticks': [], 'yticks': []}, gridspec_kw={'hspace': 0.1, 'wspace': 0.1})
for i, ax in enumerate(axes.flat):
ax.imshow(images[i], cmap='gray')
save1 = Button(top, text="Save cartoon image", command=lambda: save(ReSized6, ImagePath), padx=30, pady=5)
save1.configure(background='#364156', foreground='white', font=('calibri', 10, 'bold'))
save1.pack(side=TOP, pady=50)
plt.show()
def save(ReSized6, ImagePath):
newName = "cartoonified_Image"
path1 = os.path.dirname(ImagePath)
extension = os.path.splitext(ImagePath)[1]
path = os.path.join(path1, newName + extension)
cv2.imwrite(path, cv2.cvtColor(ReSized6, cv2.COLOR_RGB2BGR))
I = "Image saved by name " + newName + " at " + path
tk.messagebox.showinfo(title=None, message=I)
upload = Button(top, text="Cartoonify an Image", command=upload, padx=10, pady=5)
upload.configure(background='#364156', foreground='white', font=('calibri', 10, 'bold'))
upload.pack(side=TOP, pady=50)
top.mainloop()
Explication
Étape 1 : Importation des modules requis
import cv2 #for image processing
import easygui #to open the filebox
import numpy as np #to store image
import imageio #to read image stored at particular path
import sys
import matplotlib.pyplot as plt
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import *
from PIL import ImageTk, Image
CV2 : Importé pour utiliser OpenCV pour le traitement d’images
easygui : Importé pour ouvrir une boîte de fichiers. Il nous permet de sélectionner n’importe quel fichier de notre système.
Numpy : Les images sont stockées et traitées sous forme de nombres. Ceux-ci sont pris sous forme de tableaux. Nous utilisons NumPy pour traiter les tableaux.
Imageio : Permet de lire le fichier qui est choisi par la boîte de fichier à l’aide d’un chemin.
Matplotlib : Cette bibliothèque est utilisée pour la visualisation et le traçage. Ainsi, il est importé pour former le tracé des images.
OS : pour l’interaction avec le système d’exploitation. Ici, pour lire le chemin et enregistrer des images dans ce chemin.
Étape 2: Créer la fenêtre principale
top=tk.Tk()
top.geometry('400x400')
top.title('Cartoonify Your Image !')
top.configure(background='white')
label=Label(top,background='#CDCDCD', font=('calibri',20,'bold'))
Étape 3 : Création de File Box pour le choix de l'image
Construire la fenêtre principale de notre application,qui contiendra les boutons, les étiquettes et les images. Nous lui donnons également une fonction title by title().
""" fileopenbox opens the box to choose file
and help us store file path as string """
def upload():
ImagePath=easygui.fileopenbox()
cartoonify(ImagePath)
Une fenêtre contextuelle s'ouvre, elle permet de choisir le fichier sur l’appareil, qui s’ouvre à chaque fois que le code est executé. fileopenbox() est la méthode du module easyGUI qui renvoie le chemin du fichier choisi sous forme de chaîne de caractères.
L’opération est lancée dés qu'on clique sur le bouton, donc toutes les étapes ci-dessous font partie de la fonction cartoonify (ImagePath).
def cartoonify(ImagePath):
Étape 4 : stocker l'image
Comment un programme peut-t-il lire une image ? Pour un ordinateur, tout n’est que chiffres. Ains, nous allons convertir notre image en un tableau numpy.
#read the image
originalmage = cv2.imread(ImagePath)
originalmage = cv2.cvtColor(originalmage, cv2.COLOR_BGR2RGB)
#print(image) # image is stored in form of numbers
# confirm that image is chosen
if originalmage is None:
print("Can not find any image. Choose appropriate file")
sys.exit()
ReSized1 = cv2.resize(originalmage, (960, 540))
#plt.imshow(ReSized1, cmap='gray')
Imread est une méthode en cv2 qui est utilisée pour stocker des images sous forme de nombres. Cela nous aide à effectuer des opérations en fonction de nos besoins. L’image est lue sous la forme d’un tableau numpy, dans lequel les valeurs de cellule représentent les valeurs R, G et B d’un pixel. Nous redimensionnons l’image après chaque transformation pour afficher enfin toutes les images à une échelle similaire.
Étape 5 : Transformation d’une image
Pour convertir une image en dessin animé, plusieurs transformations sont effectuées:
1- Tout d’abord, une image est convertie en image en niveaux de gris. Oui, comme sur les photos d’autrefois.
#converting an image to grayscale
grayScaleImage = cv2.cvtColor(originalmage, cv2.COLOR_BGR2GRAY)
ReSized2 = cv2.resize(grayScaleImage, (960, 540))
#plt.imshow(ReSized2, cmap='gray')
cvtColor(image, flag) est une méthode de cv2 qui sert à transformer une image dans l’espace colorimétrique mentionné comme 'flag'. Ici, notre première étape consiste à convertir l’image en niveaux de gris. Ainsi, nous utilisons le drapeau BGR2GRAY. Cela renvoie l’image en niveaux de gris. Une image en niveaux de gris est stockée sous la forme grayScaleImage.
Après chaque transformation, nous redimensionnons l’image résultante à l’aide de la méthode resize() dans cv2 et l’affichons à l’aide de la méthode imshow(). Cela permet d’obtenir des informations plus claires sur chaque étape de la transformation.
2- Ensuite, l’image en niveaux de gris est lissée et nous essayons d’extraire les bords de l’image.
#applying median blur to smoothen an image
smoothGrayScale = cv2.medianBlur(grayScaleImage, 5)
ReSized3 = cv2.resize(smoothGrayScale, (960, 540))
#plt.imshow(ReSized3, cmap='gray')
Pour lisser une image, il suffit d’appliquer un effet de flou. Cela se fait à l’aide de la fonction medianBlur(). Ici, le pixel central se voit attribuer une valeur moyenne de tous les pixels qui tombent sous le noyau. À son tour, la création d’un effet de flou.
3- Aprés le lissage, nous récupérons les bords de la photo.
#retrieving the edges for cartoon effect
#by using thresholding technique
getEdge = cv2.adaptiveThreshold(smoothGrayScale, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 9)
ReSized4 = cv2.resize(getEdge, (960, 540))
#plt.imshow(ReSized4, cmap='gray')
Nous allons essayer de récupérer les bords et de les mettre en évidence. Ceci est réalisé par la technique de seuillage adaptatif. La valeur seuil est la moyenne de la surface des valeurs des pixels de voisinage moins la constante C. C est une constante qui est soustraite de la moyenne ou de la somme pondérée des pixels de voisinage. Thresh_binary est le type de seuil appliqué, et les paramètres restants déterminent la taille du bloc.
4- Nous préparons par la suite l'image de masque:
#applying bilateral filter to remove noise
#and keep edge sharp as required
colorImage = cv2.bilateralFilter(originalmage, 9, 300, 300)
ReSized5 = cv2.resize(colorImage, (960, 540))
#plt.imshow(ReSized5, cmap='gray')
Nous préparons une image couleur éclaircie que nous masquons avec des bords à la fin pour produire une image de dessin animé. Nous utilisons bilateralFilter qui supprime le bruit. Le troisième paramètre est le diamètre du voisinage du pixel, c’est-à-dire le nombre de pixels autour d’un certain pixel qui déterminera sa valeur. Les quatrième et cinquième paramètres définissent signmaColor et sigmaSpace. Ces paramètres sont utilisés pour donner un effet sigma, c’est-à-dire donner à une image un aspect vicieux et comme de la peinture à l’eau, en supprimant la rugosité des couleurs.
Oui, c’est similaire à BEAUTIFY ou à l’effet AI dans les appareils photo des téléphones mobiles modernes.
5- Nous donnons finalement un effet de dessin animé
#masking edged image with our "BEAUTIFY" image
cartoonImage = cv2.bitwise_and(colorImage, colorImage, mask=getEdge)
ReSized6 = cv2.resize(cartoonImage, (960, 540))
#plt.imshow(ReSized6, cmap='gray')
Par la combinaison des deux étapes précedentes et à l’aide de MASKING, nous effectuons bit à bit et sur deux images pour les masquer. C’est ainsi que nous masquons l’image bordée sur notre image « BEAUTIFY ».
Cela permet enfin de CARICATURER notre image !
Cela crée une belle image de dessin animé avec des bords et une couleur éclaircie de l’image originale.
Étape 6: Afficher l'ensemble des transformations
# Plotting the whole transition
images=[ReSized1, ReSized2, ReSized3, ReSized4, ReSized5, ReSized6]
fig, axes = plt.subplots(3,2, figsize=(8,8), subplot_kw={'xticks':[], 'yticks':[]}, gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(images[i], cmap='gray')
//save button code
plt.show()
Pour tracer toutes les images, nous faisons d’abord une liste de toutes les images. La liste ici s’appelle « images » et contient toutes les images redimensionnées. Maintenant, nous créons des axes comme subplots dans un tracé et affichons une ou une image dans chaque bloc de l’axe en utilisant la méthode imshow().
plt.show() trace l’ensemble en une seule fois.
Étape 7 : Effectuer une sauvegarde
def save(ReSized6, ImagePath):
#saving an image using imwrite()
newName="cartoonified_Image"
path1 = os.path.dirname(ImagePath)
extension=os.path.splitext(ImagePath)[1]
path = os.path.join(path1, newName+extension)
cv2.imwrite(path, cv2.cvtColor(ReSized6, cv2.COLOR_RGB2BGR))
I = "Image saved by name " + newName +" at "+ path
tk.messagebox.showinfo(title=None, message=I)
Afin d’enregistrer l’image résultante, nous prenons l’ancien chemin, et changeons simplement la queue (nom de l’ancien fichier) en un nouveau nom et stockons l’image caricaturée avec un nouveau nom dans le même dossier en ajoutant le nouveau nom à la partie tête du fichier.
Pour cela, nous extrayons la partie tête du chemin du fichier par la méthode os.path.dirname(). De même, os.path.splitext(ImagePath)[1] est utilisé pour extraire l’extension du fichier du chemin d’accès.
Ici, newName stocke « Cartoonified_Image » comme nom d’un nouveau fichier. os.path.join(path1, newName + extension) joint l’en-tête du chemin au newname et à l’extension. Il s’agit du chemin d’accès complet au nouveau fichier.
imwrite() de cv2 est utilisée pour enregistrer le fichier dans le chemin mentionné. cv2.cvtColor(ReSized6, cv2. COLOR_RGB2BGR) est utilisé pour s’assurer qu’aucune couleur n’est extraite ou mise en surbrillance pendant que nous enregistrons notre image. Ainsi, l’utilisateur reçoit enfin la confirmation que l’image est enregistrée avec le nom et le chemin du fichier.
Étape 8 : Créer le bouton Cartoonify dans la fenêtre principale
upload=Button(top,text="Cartoonify an Image",command=upload,padx=10,pady=5)
upload.configure(background='#364156', foreground='white',font=('calibri',10,'bold'))
upload.pack(side=TOP,pady=50)
Étape 9 : Créer le bouton save cartoon image
save1=Button(top,text="Save cartoon image",command=lambda: save(ImagePath, ReSized6),padx=30,pady=5)
save1.configure(background='#364156', foreground='white',font=('calibri',10,'bold'))
save1.pack(side=TOP,pady=50)
Aprés la transformation de l’image, il est temps de sauvegarder l'image resultante, Le code ci-dessus crée un bouton et donne la possibilité à l’utilisateur d’enregistrer l'image caricaturée.
Étape 10 : Fonction principale pour construire la fenêtre tkinter
top.mainloop()
Nous avons développé avec succès Image Cartoonifier avec OpenCV en Python. C’est la magie d’openCV qui nous permet de faire des miracles. Nous vous suggérons de créer votre propre éditeur de photos et d’essayer différents effets.