## TP12 -- Traitement d'images



## Exercise 12.5

## question 1) b)

# Voilà une première solution:
def imageTest():
	image = []
	for i in range(16):
		ligne = []
		for j in range(16):
			ligne.append(i*j)
		image.append(ligne)
	return image

# Voilà une deuxième solution:
def imageTest2():
	image = []
	for i in range(16):
		ligne = [i*j for j in range(16)]
		image.append(ligne)
	return image

# Voilà une troisième solution:
def imageTest3():
	return [ [i*j for j in range(16)] for i in range(16) ]



## Exercise 12.7 -- Nombres de lignes et de colonnes d'une image

## question 1) b)

def nombreLignes(image):
	return len(image)

## question 2) b)

def nombreColonnes(image):
	return len(image[0])



## Exercise 12.8 -- Copie d'une image

## question 2) b)

# Voici une première solution :
def imageCopieee(image):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	M = []
	for i in range(n):
		L = []
		for j in range(p)=
			pixel = image[i][j]
			L.append(pixel)
		M.append(L)
	return M
	
# Voici une deuxième solution :
def imageCopieee2(image):
	M = []
	for ligne in image:
		ligneCopiee = ligne[:]
		M.append(ligneCopiee)
	return M
	
# Voici une troisième solution :
def imageCopieee3(image):
	return [ ligne[:] for ligne in image ]
	
# Voici une quatrième solution :
def imageCopieee4(image):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	return [ [ image[i][j] for j in range(p) ] for i in range(n) ]



## Exercise 12.9 -- Enregistrement d'une image

## question 1) b)

def codageEnTexte(image):
	texte = ""
	
	nbLignes = nombreLignes(image)
	nbColonnes = nombreColonnes(image)
	
	texte = texte + str(nbLignes) + "\n"
	texte = texte + str(nbColonnes) + "\n"
	
	for i in range(nbLignes):
		for j in range(nbColonnes):
			texte = texte + str(image[i][j]) + "\n"
			
	return texte

## question 2) b)

def enregistrer(image, nomFichier):
	sortie = open(nomFichier + ".txt", "w", encoding = "UTF-8")
	texte = codageEnTexte(image)
	sortie.write(texte)
	sortie.close()



## Exercise 12.10 -- Chargement d'une image

## question 4) b)

def imageDepuisFichier(nomFichier):
	entree = open(nomFichier, "r", encoding = "UTF-8")
	lignesTexte = entree.readlines()
	nbLignes = int(lignesTexte[0])
	nbColonnes = int(lignesTexte[1])
	indice = 2
	image = []
	for i in range(nbLignes):
		ligne = []
		for j in range(nbColonnes):
			ligne.append(int(lignesTexte[indice]))
			indice = indice + 1
		image.append(ligne)
	return image



## Exercise 12.12 -- Négatif d'une image

## question 4) b)

def imageNegative(image):
	M = imageCopiee(image)
	n = nombreLignes(image)
	p = nombreColonnes(image)
	for i in range(n):
		for j in range(p):
			M[i][j] = 255 - M[i][j]
	return M



## Exercise 12.13 -- Modifier la luminosité d'une image

## question 2) b)

def f(pixel, alpha):
	x = pixel/255
	y = x**alpha
	return int(255*y)

## question 3) b)

# Voici une première solution :
def imageAvecLuminosite(image, alpha):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	M = []
	for i in range(n):
		ligne = []
		for j in range(p):
			pixel = image[i][j]
			pixelNew = f(pixel, alpha)
			ligne.append(pixelNew)
		M.append(ligne)
	return M
	
# Voici une deuxième solution :
def imageAvecLuminosite2(image, alpha):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	return [ [ f(image[i][j], alpha) for j in range(p) ] for i in range(n) ]



## Exercise 12.14 -- Augmenter le contraste d'une image

## question 2) b)

def psi(x, alpha):
	if 0 <= x <= 0.5:
		return (2*x)**alpha*0.5
	else:
		return 1 - 0.5*(2*(1-x))**alpha

## question 3) c)

def g(pixel, alpha):
	x = pixel/255
	y = psi(x, alpha)
	newPixel = 255*y
	return int(newPixel)

## question 4) c)

# Voici une première solution :
def imageContrastee(image, alpha):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	M = []
	for i in range(n):
		ligne = []
		for j in range(p):
			pixel = image[i][j]
			pixelNew = g(pixel, alpha)
			ligne.append(pixelNew)
		M.append(ligne)
	return M
	
# Voici une deuxième solution :
def imageContrastee2(image, alpha):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	return [ [ g(image[i][j], alpha) for j in range(p) ] for i in range(n) ]



## Exercise 12.15 -- Créer du bruit

## question 1) c)

# On procède modulairement en créant des fonctions auxiliaires.
def pixelAleatoire():
	return int(rd.random()*255)

def indiceLigneAleatoire(image):
	n = nombreLignes(image)
	alea = rd.random()
	return int(n*alea)
	
def indiceColonneAleatoire(image):
	p = nombreColonnes(image)
	alea = rd.random()
	return int(p*alea)

def modifiePixelAleatoire(image):
	i = indiceLigneAleatoire(image)
	j = indiceColonneAleatoire(image)
	image[i][j] = pixelAleatoire()


# Voici une première solution :
def imageBruitee(image, ratio):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	nbPixels = n*p
	nbModifications = round(nbPixels*ratio)

	M = imageCopiee(image)

	for _ in range(nbModifications):
		modifiePixelAleatoire(M)
		
	return M
	
# Voici une deuxième solution :
def imageBruitee2(image, ratio):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	M = imageCopiee(image)
	for i in range(n):
		for j in range(p):
			if rd.random() <= ratio:
				M[i][j] = pixelAleatoire()
				
	return M
	



## Exercise 12.16 -- Débarrasser une image de son \glm {bruit}

## question 2) c)

def pixelGeneralise(image, i, j):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	
	if i < 0:
		i = 0
	elif i >= n:
		i = n-1
	if j < 0:
		j = 0
	elif j >= p:
		j = p-1
		
	return image[i][j]

## question 3) a)

# Voilà une première solution :
def pixelsAutour(image, i0, j0):
	liste = []
	for i in range(i0-1, i0+2):
		for j in range(j0-1, j0+2):
			pixel = pixelGeneralise(image, i, j)
			liste.append(pixel)
	return liste

# Voilà une deuxième solution :
def pixelsAutour2(image, i0, j0):
	liste = []
	for delta1 in [-1, 0, 1]:
		for delta2 in [-1, 0, 1]:
			(i, j) = (i0 + delta1, j0 + delta2)
			pixel = pixelGeneralise(image, i, j)
			liste.append(pixel)
	return liste

## question 3) b)

# Voici d'abord une fonction auxiliaire :
def elementMedian(liste):
	listeTriee = sorted(liste)
	return listeTriee[4]
	
def imageSansBruit(image):
	M = imageCopiee(image)
	n = nombreLignes(image)
	p = nombreColonnes(image)
	for i in range(n):
		for j in range(p):
			pixels = pixelsAutour(image, i, j)
			pixelMedian = elementMedian(pixels)
			M[i][j] = pixelMedian
			
	return M



## Exercise 12.17 -- Énergie d'un pixel

## question 5) b)

def energie(image, i, j):
	diff1 = abs(pixelGeneralise(image, i+1, j) - pixelGeneralise(image, i-1, j))/2
	diff2 = abs(pixelGeneralise(image, i, j+1) - pixelGeneralise(image, i, j-1))/2
	
	return round(diff1 + diff2)



## Exercise 12.18

## question 1) b)

# Voici une première solution :
def matriceDesEnergies(image):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	M = []
	for i in range(n):
		L = []
		for j in range(p):
			E = energie(image, i, j)
			L.append(E)
		M. append(ligne)
	return M

# Voici une deuxième solution :
def matriceDesEnergies2(image):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	return [ [ energie(image, i, j) for j in range(p) ] for i in range(n) ]



## Exercise 12.19

## question 1) b)

# On commence par coder une fonction auxiliaire :
def indiceElementMinimal(L):
	iMinimal = 0
	xMinimal = L[0]
	n = len(L)
	for i in range(n):
		x = L[i]
		if x < xMinimal:
			iMinimal = i
	return iMinimal

# Voici maintenant la fonction voulue :
def reduit(image):
	M = imageCopie(image)
	energies = matriceDesEnergies(image)
	n = nombreLignes(image)
	for i in range(n):
		indiceEnergieMin = indiceElementMinimal(energies[i])
		ligne = image[i]
		ligne.pop(indiceEnergieMin)
	return M



## Exercise 12.20 -- Chemins

## question 1) b)

def estUnChemin(chemin):
	n = len(chemin)
	for i in range(n-1):
		if abs(chemin[i+1] - chemin[i]) >= 2:
			return False
	return True	

## question 2) b)

def imageSansChemin(image, chemin):
	M = imageCopiee(image)
	longueurChemin = len(chemin)
	for i in range(longueurChemin):
		ligne = image[i]
		indiceAEnvelver = chemin[i]
		ligne.pop(indiceAEnvelver)
	return M



## Exercise 12.21 -- Coût énergétique d'un chemin

## question 1) b)

def matriceCoutMinimal(image):
	energies = matriceDesEnergies(image)
	resultat = []
	n = nombreLignes(image)
	p = nombreColonnes(image)
	premiereLigne = energies[0][:]
	resultat.append(premiereLigne)
	for i in range(1, n):
		ligne = []
		for j in range(p):
			chemin1 = energies[i][j] + pixelGeneralise(resultat, i-1, j-1)
			chemin2 = energies[i][j] + pixelGeneralise(resultat, i-1, j)
			chemin3 = energies[i][j] + pixelGeneralise(resultat, i-1, j+1)
			cheminMinimal = min(chemin1, chemin2, chemin3)
			ligne.append(cheminMinimal)
		resultat.append(ligne)
	return resultat

## question 2) a)

# Voici une petite fonction auxiliaire,
# qui retourne une liste donnée
def listeRetournee(L):
	resultat = []
	n = len(L)
	for i in range(n):
		x = L[-i-1]
		resultat.append(x)
	return resultat

# Voici une autre petite fonction auxiliaire,
# qui renvoie l'élément minimal d'une liste
def indiceElementMinimal(L):
	iMinimal = 0
	xMinimal = L[0]
	n = len(L)
	for i in range(n):
		x = L[i]
		if x < xMinimal:
			iMinimal = i
	return iMinimal

# Voici la fonction demandée :
def cheminDeCoutMinimal(image):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	coutsMinimaux = matriceCoutMinimal(image)
	indiceCoutFinalMin = indiceElementMinimal(coutsMinimaux[-1])
	chemin = [indiceCoutFinalMin]
	for k in range(1, n):
		i = -1-k
		j0 = chemin[-1]
		bestJ = j0
		e = coutsMinimaux[i][j0]
		if j0-1 >= 0 and coutsMinimaux[i][j0-1] < e:
			e = coutsMinimaux[i][j0-1]
			bestJ = j0-1
		if j0+1 < p and coutsMinimaux[i][j0+1] < e
			e = coutsMinimaux[i][j0-1]
			bestJ = j0+1
		chemin.append(bestJ)
	return listeRetournee(chemin)



## Exercise 12.22 -- Réduction d'une image

## question 1) b)

# Commençons par une fonction qui renvoie une nouvelle image
# en enlevant le chemin de coût minimal.
def imageReduiteUneFois(image):
	M = imageCopiee(image)
	chemin = cheminDeCoutMinimal(image)
	n = nombreLignes(image)
	for i in range(n):
		ligne = M[i]
		j = chemin[i]
		ligne.pop(j)
	return M

# Voici une première solution :
def imageReduite(image, k):
	M = imageCopiee(image)
	for _ in range(k):
		M = imageReduiteUneFois(M)
	return M
	
# Voici une deuxième solution, récursive :
def imageReduite2(image, k):
	if k == 0:
		return imageCopiee(image)
	else:
		M = imageReduite2(image, k-1)
		return imageReduiteUneFois(M)



## Exercise 12.23 -- Pixélisation d'une image

## question 1) a)

def moyenne(liste):
	n = len(liste)
	S = 0
	for x in liste:
		S = S+x
	return S/n

## question 2) b)

# Voici une fonction auxiliaire qui renvoie
# la liste des pixels sur un bloc k par k,
# ayant pour origine le point (i0, j0)
def pixelsBlocs(image, i0, j0, k):
	listePixels = []
	for i in range(i0, i0+k):
		for j in range(j0, j0+k):
			pixel = image[i][j]
			listePixels.append(pixel)
	return listePixels

# Voici une fonction auxiliaire qui fait
# ce qui est demandé mais en contractant
# un bloc de k pixels en un seul pixel :
def imagePixeliseePetite(image, k):
	n = nombreLignes(image)
	p = nombreColonnes(image)
	nNew = n//k
	pNew = p//k
	petiteImageNew = []
	for i in range(nNew):
		ligne = []
		for j in range(pNew):
			i0 = i*k
			j0 = j*k
			listePixels = pixelsBlocs(image, i0, j0, k)
			m = moyenne(listePixels)
			ligne.append(int(m))
		petiteImageNew.append(ligne)
	return petiteImageNew
	
# Voici une fonction auxiliaire qui agrandit
# une image en répétant N fois ses pixels :
def imageApresRepetition(image, N):
	M = []
	n = nombreLignes(image)
	for ligne in image:
		L = []
		for x in ligne:
			for _ in range(N):
				L.append(x)
		for _ in range(N):
			L_copie = L[:]
			M.append(L_copie)
	return M
	
# Voici enfin la fonction demandée :
def imagePixelisee(image, k):
	M = imagePixeliseePetite(image, k)
	return imageApresRepetition(M, k)
