"""
Programme   : TP-images-partie2-correction-exo2.py
Langage     : Python 3.7.3
Modules     : Pillow 7.1.2
Auteur      : Mathieu Pons
Description : manipulation d'images, application de différents filtres
Pillow      : https://pillow.readthedocs.io/en/stable/

Exercice à réaliser :
	L'objet de cet exercice est de créer un programme qui, à partir d'une image de votre choix, applique
	les différents filtres détaillés dans le TP. Si vous les testez sur notre perroquet, vous devriez
	obtenir les images des figures 1 à 6.
"""

# IMPORT =========================================================================================
from PIL import Image

# FONCTIONS =======================================================================================
def afficher_info(pimg):
	"""
	Description : 	affiche des informations sur l'image passée en paramètre
	Paramètres 	: 	type(pimg) => Image.PIL
	Retour 		: 	aucun
	"""
	try:
		_name = pimg.filename # on essaye de lire l'attribut filename de l'image (c'est le nom lors son ouverture avec open)
		_format = pimg.format # on essaye de lire l'attribut format de l'image (c'est son extension : JPEG, PNG, ...)
	except AttributeError: # si une des deux opérations échouent, c'est que l'image a été créée
		# on indique alors que c'est une image stockée en mémoire centrale et non lue à partir du disque
		_name = "<Image en mémoire>"
		_format = "<Image en mémoire>"
		
	_mode = pimg.mode
	_nbpixel = pimg.width * pimg.height # nombre de pixel de l'image
	if _mode == "1":
		_txtmode = "Noir et blanc"
		_poids = _nbpixel / 8 # pixel codé sur un bit (1/8ième d'octet) -> blanc ou noir
	elif _mode == "L":
		_txtmode = "Niveaux de gris"
		_poids = _nbpixel # pixel codé sur un octet (8 bits) -> 256 niveaux de gris
	elif _mode == "RGB":
		_txtmode = "Couleurs RGB"
		_poids = _nbpixel * 3 # pixel codé sur 3 octets (24 bits), un par couleur -> 16 millions de couleurs
	elif _mode == "RGBA":
		_txtmode = "Couleurs RGB + Alpha"
		_poids = _nbpixel * 3 # pixel codé sur 4 octets (32 bits), un par couleur + un canal de transparence
	else:
		_txtmode = _mode

	print("+ {:-^56} +".format(""))
	print("| {:<56} |".format(_name))
	print("+ {:-^56} +".format(""))
	print("| {:<30} : {:<23} |".format("FORMAT", _format))
	print("| {:<30} : {:<23} |".format("MODE", _txtmode))
	print("| {:<30} : {:<23} |".format("TAILLE", str(pimg.width) + " x " + str(pimg.height)))
	print("| {:<30} : {:<23} |".format("NB PIXELS", _nbpixel))
	print("| {:<30} : {:<23.2f} |".format("POIDS(non compressé) en Mo ", _poids / (1024 * 1024)))
	print("+ {:-^56} +".format(""))
	print("")

def negatif(pimg):
	"""
	Description : 	calcule et renvoit le négatif de pimg
	Paramètres 	: 	type(pimg) => Image.PIL
	Retour 		: 	PIL.Image
	"""
	_width, _height = pimg.width, pimg.height
	_imgn = Image.new("RGB", (_width, _height))
	print("Création du négatif de l'image en cours...")
	for y in range(_height):
		for x in range(_width):
			r, g, b = pimg.getpixel((x, y))
			# on inverse chaque composante de couleurs par rapport à son maximum
			_imgn.putpixel((x, y), (255 - r, 255 - g, 255 - b))
	print("Traitement terminé avec succès.")
	return _imgn

def permuter(pimg):
	"""
	Description : 	renvoit une copie de l'image de pimg dans laquelle les couleurs ont été permutées
	Paramètres 	: 	type(pimg) => Image.PIL
	Retour 		: 	PIL.Image
	"""
	_width, _height = pimg.width, pimg.height
	_imgp = Image.new("RGB", (_width, _height))
	print("Permutation des couleurs de l'image en cours...")
	for y in range(_height):
		for x in range(_width):
			r, g, b = pimg.getpixel((x, y))
			_imgp.putpixel((x, y), (b, r, g)) # on permuteles couleurs, ici :  (r, g, b) -> (b ,r, g)
	print("Traitement terminé avec succès.")
	return _imgp

def moduler(pimg):
	"""
	Description : 	modulation des couleurs de pimg
	Paramètres 	: 	type(pimg) => Image.PIL
	Retour 		: 	PIL.Image
	"""
	_width, _height = pimg.width, pimg.height
	_imgm = Image.new("RGB", (_width, _height))
	print("Modulation des couleurs de l'image en cours...")
	for y in range(_height):
		for x in range(_width):
			r, g, b = pimg.getpixel((x, y))
			_imgm.putpixel((x, y), (2 * r % 256, 3 * g % 256, 4 * b % 256)) # vous pouvez tester d'autres coefficients
	print("Traitement terminé avec succès.")
	return _imgm

def luminosite(pimg, pval):
	"""
	Description : 	augmente ou diminue la luminosite d'un pixel
	Paramètres 	: 	type(pimg) => Image.PIL
					type(pval) => int (positif ou négatif)
	Retour 		: 	PIL.Image
	"""
	_width, _height = pimg.width, pimg.height
	_imgl = Image.new("RGB", (_width, _height))
	print("Variation de luminosité de l'image en cours...")
	for y in range(_height):
		for x in range(_width):
			r, g, b = pimg.getpixel((x, y))
			r, g, b = r + pval, g + pval, b + pval
			if pval >= 0: # si on augmente la luminosité
				# la valeur peut dépasser 255, on la fixe à 255 dans ce cas
				_imgl.putpixel((x, y), (min(r, 255), min(g, 255), min(b, 255))) # si la valeur dépasse 255
			else: # sinon
				# la valeur peut descendre en-dessous de 0, on la fixe à 0 dans ce cas
				_imgl.putpixel((x, y), (max(r, 0), max(g, 0), max(b, 0)))
	# REMARQUE : 1) min(a, b) et max(a, b) sont deux fonctions pratiques du langage Python
	#			 2) on peut omettre ici de vérifier l'intervalle des valeurs car Pillow fixera automatiquement
	#				la valeur à 0 si elle est en-dessous ou à 255 si elle est au-dessus
	print("Traitement terminé avec succès.")
	return _imgl

def contraste(pimg):
	"""
	Description : 	augmente le contraste de l'image passée en paramètre
	Paramètres 	: 	type(pimg) => Image.PIL
	Retour 		: 	PIL.Image
	"""
	_width, _height = pimg.width, pimg.height
	_imgc = Image.new("RGB", (_width, _height))
	print("Augmentation du contraste de l'image en cours...")
	for y in range(_height):
		for x in range(_width):
			r, g, b = pimg.getpixel((x, y))
			luminance = int((0.2126 * r + 0.7152 * g + 0.0722 * b)) # calcul de la luminance (ici, le niveau de gris du pixel)
			_val = 60 # valeur d'augmentation ou de diminution de luminosité
			if luminance > 195: # seuil de luminance pour décider si un pixel est clair
				r, g, b = r + _val, g + _val, b + _val
			elif luminance < 60: # seuil de luminance pour décider si un pixel est sombre
				r, g, b = r - _val, g - _val, b - _val
			_imgc.putpixel((x, y), (r, g, b))
	print("Traitement terminé avec succès.")
	return _imgc

# PROGRAMME PRINCIPAL =============================================================================
img_source = Image.open("perroquet.png")
negatif(img_source).show()
permuter(img_source).show()
moduler(img_source).show()
luminosite(img_source, +100).show()
luminosite(img_source, -100).show()
contraste(img_source).show()
