Outils pour utilisateurs

Outils du site


diy:projets:vintage

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:
Avant Après


Outils requis et Librairies Python

Le programme

Le programme se trouve en intégralité 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 OpenCV permettant de nombreuses opérations sur les images en Python et C++
  • numpy référence la librairie NumPy prodiguant une meilleure création/utilisation/gestion des objets à plusieurs dimensions.
  • copy référence la librairie copy en sa qualité de duplicateur de données.
  • random référence la librairie random pour la génération de valeurs aléatoires.
  • sys référence la librairie 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:

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 dilate ses contours.
La partie Thickening edge les désagrège de l'image originale.
La partie Speckle noise rajoute du bruit sur l'image.
La partie Sepia filter I applique un 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 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.
Philibert Thomas

diy/projets/vintage.txt · Dernière modification : 2018/06/01 10:58 de tphilibert