====== Présentation du projet ====== **vintage** est un programme qui a pour but d'appliquer un effet vintage; vieilli à une image donnée ou prise.\\ Ce programme n'a pas d'autre but que de divertir.\\ \\ Exemple d'exécution de ce programme:\\ {{:diy:projets:lama.jpg?345|Avant}} {{:diy:projets:amal.jpg?345|Après}} ---- ====== Outils requis et Librairies Python ====== ====Le programme==== Le programme se trouve en intégralité [[https://github.com/JsuisUnLama/BE-2018---PHILIBERT-Thomas/blob/master/code/vintage.py|ici]].\\ \\ ====Outils requis==== Aucun outil spécifique n'est requis pour faire fonctionner ce programme. ====Librairies==== Ce programme utilise les librairies suivantes: import cv2 import numpy as np import copy import random as rng import sys //lignes 1 à 5//.\\ \\ * ''cv2'' référence la librairie [[https://opencv.org/|OpenCV]] permettant de nombreuses opérations sur les images en Python et C++\\ * ''numpy'' référence la librairie [[http://www.numpy.org/|NumPy]] prodiguant une meilleure création/utilisation/gestion des objets à plusieurs dimensions.\\ * ''copy'' référence la librairie [[https://docs.python.org/2/library/copy.html|copy]] en sa qualité de duplicateur de données.\\ * ''random'' référence la librairie [[https://docs.python.org/2/library/random.html|random]] pour la génération de valeurs aléatoires.\\ * ''sys'' référence la librairie [[https://docs.python.org/3/library/sys.html|sys]] pour la gestion des paramètres en Python. ---- ====== Explication du programme ====== ====Le contrôle d'erreur==== Il s'agit d'un pan entier de code contrôlant l'arbre d'argument suivant:\\ {{ :diy:projets:arbre_des_arguments_vintage_1_.png |}}\\ Ce n'est pas nécessairement excitant mais ça permet au programme de détecter différentes erreurs, de s'arrêter dans de meilleures conditions et en indiquant l'erreur en question. Si cela vous intéresse, le contrôle s'étend //lignes 163 à 196.//\\ ---- ====Les fonctions==== Ce programme contient 7 fonctions: * **generateCircleMask**(//img,center,radius//). * **cappedValue**(//value,least=0,most=255//). * **applyVariationToBGRPixel**(//pixel,var,withRNG=False,low_rng=0,high_rng=0//). * **maskDifference**(//mask1,mask2//). * **vintage**(//img//). * **options**(). * **optionTime**(//time//). **generateCircleMask**\\ def generateCircleMask(img,center,radius): h,w,v = img.shape sq_rad = radius**2 toThreshold = cv2.cvtColor(img.astype('uint8',copy=False),cv2.COLOR_BGR2GRAY) for i in range (0,h): for j in range (0,w): if(((i-center[0])**2 + (j-center[1])**2) <= sq_rad): toThreshold[i,j] = 0 else: toThreshold[i,j] = 255 ret, circleMask = cv2.threshold(toThreshold, 10, 255, cv2.THRESH_BINARY) return circleMask //lignes 9 à 21//\\ \\ La fonction prend une image //img// et renvoie un masque circulaire de centre //center// et de rayon //radius// de la même taille que celle de l'image donnée.\\ \\ **cappedValue**\\ def cappedValue(value,least=0,most=255): if(value < least): value = least elif(value > most): value = most return value //lignes 25 à 31//\\ \\ La fonction prend une valeur //value// et s'assure qu'elle appartienne à l'intervalle [//least,most//].\\ \\ **applyVariationToBGRPixel**\\ def applyVariationToBGRPixel(pixel,var,withRNG=False,low_rng=0,high_rng=0): b,g,r = pixel if(withRNG): b = cappedValue(b - (var + rng.randint(low_rng,high_rng))) g = cappedValue(g - (var + rng.randint(low_rng,high_rng))) r = cappedValue(r - (var + rng.randint(low_rng,high_rng))) else: b = cappedValue(b - var) g = cappedValue(g - var) r = cappedValue(r - var) pixel = [b,g,r] return pixel //lignes 35 à 47//\\ \\ La fonction prend un pixel \\pixel\\ (i.e. un objet à trois dimensions), une valeur \\var\\ et soustrait cette valeur uniformément aux valeurs que contient //pixel//.\\ Si le booléen //withRNG// est vrai, à chaque instance de var lui sera ajouté une valeur aléatoire comprise dans l'intervalle [//low_rng,high_rng//].\\ \\ **maskDifference**\\ def maskDifference(mask1,mask2): h,w = mask1.shape mask_diff = copy.copy(mask1) for i in range (0,h): for j in range (0,w): if(mask1[i,j] == 0 and mask2[i,j] == 255): mask_diff[i,j] = 255 else: mask_diff[i,j] = 0 return mask_diff //lignes 51 à 61//\\ \\ La fonction prend deux masques //mask1// et //mask2// de même taille et renvoie le masque résultant de leur différence.\\ \\ **vintage**\\ def vintage(img): # Useful datas height, width, value = img.shape # Get Canny edges grayimg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edgyimg = cv2.Canny(grayimg,50,200,apertureSize=3) # Dilate edges rect = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3)) thicc_img = cv2.dilate(edgyimg,rect,iterations=1) # Thickening edges invertimg = cv2.bitwise_not(thicc_img) colorgray = cv2.cvtColor(invertimg,cv2.COLOR_GRAY2BGR) ret, mask = cv2.threshold(grayimg, 10, 255, cv2.THRESH_BINARY) mask_inv = mask roi = grayimg[0:height, 0:width] img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv) img2_fg = cv2.bitwise_and(img,img,mask = mask) img1_bg = cv2.cvtColor(img1_bg,cv2.COLOR_GRAY2BGR) addimg = cv2.add(img1_bg,img2_fg) img[0:height, 0:width] = addimg # Speckle Noise gauss = np.random.randn(height,width,value) gauss = gauss.reshape(height,width,value) img = img + (img * gauss)/16 # Sepia filter I sepia = np.matrix([[0.272,0.534,0.131],[0.349,0.686,0.168],[0.393,0.769,0.189]]) img = cv2.transform(img,sepia) # Toneing center_h = int(height/2) center_w = int(width/2) mask = generateCircleMask(img,[center_h,center_w],center_h-5) outer_mask = generateCircleMask(img,[center_h,center_w],center_h+5) outer_mask = maskDifference(outer_mask,mask) var = 5 unb = 1 low = -2 high = 2 for i in range (0,height): for j in range (0,width): if(mask[i,j] == 255): img[i,j] = applyVariationToBGRPixel(img[i,j],var,True,low,high) elif(outer_mask[i,j] == 255): img[i,j] = applyVariationToBGRPixel(img[i,j],int(var/2),True,low,high) # Sepia filter II sepia = np.matrix([[0.272,0.534,0.131],[0.349,0.686,0.168],[0.393,0.769,0.189]]) img = cv2.transform(img,sepia) # Gaussian blur img = cv2.GaussianBlur(img,(5,5),0) # Border bs = 10 img = cv2.copyMakeBorder(img,bs,bs,bs,bs,cv2.BORDER_CONSTANT,value=(230,245,245)) # Return result return img //lignes 65 à 128//\\ \\ La fonction prend une image //img//.\\ La partie ''#Useful Datas'' récupère la taille, la hauteur et la valeur de //img// et les place dans des variables.\\ La partie ''Get Canny edges'' trace les contours de l'image en niveaux de gris.\\ La partie ''Dilate edges'' [[https://fr.wikipedia.org/wiki/Morphologie_math%C3%A9matique#Dilatation_et_%C3%A9rosion|dilate]] ses contours.\\ La partie ''Thickening edge'' les désagrège de l'image originale.\\ La partie ''Speckle noise'' rajoute du [[https://fr.wikipedia.org/wiki/Bruit_num%C3%A9rique|bruit]] sur l'image.\\ La partie ''Sepia filter I'' applique un [[https://fr.wikipedia.org/wiki/S%C3%A9pia#Photographie|filtre sépia]] sur l'image.\\ La partie ''Toneing'' éclaircit une zone centrale de l'image.\\ La partie ''Sepia filter II'' applique à nouveau un filtre sépia.\\ La partie ''Gaussian Blur'' applique un [[https://fr.wikipedia.org/wiki/Lissage_d%27images#Filtre_gaussien|flou Gaussien à l'image]].\\ La partie ''Border'' génère des bordures à l'image.\\ La partie ''Return result'' retourne l'image ainsi changée.\\ \\ **options**\\ def options(): print("Nom ou chemin de l'image à traiter (avec l'extension)") name_i = input("-> ") print("") image = cv2.imread(name_i) try: dtype_debug = image.dtype except AttributeError as ae: print("Erreur,",name_i,"ne peut pas être lu (fichier invalide ou absent)\n") raise ae print("Quel nom donner à l'image en sortie (avec l'extension)?") name_o = input("-> ") print("") return image,name_o //lignes 132 à 147//\\ Communique avec l'utilisateur pour récupérer certaines informations (notamment l'image à traiter).\\ __Remarque:__ uniquement si le programme a été lancé avec l'argument ''-o''.\\ \\ **optionTime**\\ def optionTime(time): # See Execution time of vintage function print("Voulez-vous connaître le temps d'execution de la fonction vintage? [y/n]") know = input("-> ") if(know == 'y'): print("Temps d'execution du programme:",time,"secondes") //lignes 151 à 156//\\ Demande à afficher le temps //time// d'exécution du programme.\\ __Remarque:__ uniquement si le programme a été lancé avec l'argument ''-o''.\\ \\ ====Le main==== Le main ne s'occupe que des options (si argument ''-o'') et de la fonction principale. ---- =====Problèmes et améliorations===== ====Problèmes==== Le programme est particulièrement lent (~4s). ====Solutions possibles et améliorations==== Doter le programme d'une meilleure algorithmie pour notamment virer des opérations en trop/peu utiles. Le réécrire en C++ serait aussi un bon moyen pour booster ses performances.\\ \\ Pour les améliorations possibles, on pourrait envisager du temps réel et une meilleure gestion d'arguments.\\ \\ \\ \\ ---- //Merci pour votre attention.//\\ //[[thomas.philibert@univ-tlse3.fr|Philibert Thomas]]//