Gràfics simples en Python amb PIL
Aquest document explica amb dos exemples les instruccions bàsiques que permeten resoldre els problemes del curs "Graphic Problems" de Jutge.org. Les solucions a aquests problemes s'han de codificar usant el llenguatge de programació Python3 amb la llibreria PIL.
Instal·lació 💻
Per poder seguir aquest tutorial haureu d'instal·lar primer la llibreria gràfica Pillow (una branca de PIL):
pip3 install pillow
Segons el vostre sistema, potser haureu d'utilitzar pip
enlloc de pip3
.
Primer exemple: Bandera del Japó 🇯🇵
Comencem presentant una solució possible del problema P95451 (Bandera del Japó). Recordem que la sortida ha de ser, amb les mides indicades a l'enunciat, senzillament un cercle de color vermell intens sobre un fons blanc:
Aquest és el codi:
from PIL import Image, ImageDraw
img = Image.new('RGB', (600, 400), 'White')
dib = ImageDraw.Draw(img)
dib.ellipse([150, 50, 449, 349], 'Crimson')
img.save('output.png')
A continuació expliquem els seus elements:
La primera línia s'ha d'incloure sempre, i indica els dos únics mòduls de
PIL
que ens caldrà usar:Image
iImageDraw
.La primera instrucció crea un objecte anomenat
img
que conté una imatge (un mapa de punts). Té tres paràmetres:El primer indica el mode de la imatge. Nosaltres usarem sempre
'RGB'
.El segon indica les dimensions de la imatge, les quals podrien dependre de les dades d'entrada. En aquest programa, la imatge té sempre
600
punts d'amplada i400
punts d'alçada. El punt de la cantonada superior esquerra és el, i el punt de la cantonada inferior dreta és el . El tercer paràmetre, que és opcional, dóna un color inicial a tots els punts de la imatge, en aquest cas el
'White'
.
Amb PIL es poden usar, directament amb el seu nom, molts dels colors que es poden trobar en aquesta llista de colors. També es poden fer servir triplets d'enters entre
i , que en defineixen les intensitats de vermell, verd i blau, respectivament. Per exemple, el es correspon al negre ( 'Black'
), elal blanc ( 'White'
), elal vermell ( 'Red'
), etc. Però el color'Green'
, per exemple, no és pas, com algú podria pensar. Per això, quan l'enunciat d'algun problema mencioni algun nom de color, simplement useu el nom donat. La segona instrucció crea un objecte anomenat
dib
que permet dibuixar sobre la imatgeimg
. Sempre s'ha de posar tal qual.A continuació ve la primera (i única) instrucció de dibuix pròpiament dita d'aquest programa. Usant el mètode
ellipse
, dibuixem una el·lipse de diàmetre horitzontali diàmetre vertical , deixant punts de marge a esquerra i dreta, i punts de marge per sobre i per sota. De fet, com que aquí els dos diàmetres són iguals, l'el·lipse és un cercle. L'últim paràmetre indica amb quin color s'ha d'omplir el cercle, en aquest cas el 'Crimson'
, com indica l'enunciat del problema.L'última instrucció sempre ha de ser exactament la d'aquest programa. El que fa és guardar la imatge en format PNG en un fitxer anomenat
output.png
. Aquest nomoutput.png
és obligatori per als problemes del Jutge.D'altra banda, si voleu veure la imatge actual en un o més moments del programa, directament per pantalla i sense haver de desar-la en cap fitxer, useu la instrucció
img.show()
.
Com a conclusió, si executeu aquest programa i visualitzeu el fitxer output.png
, veureu una magnífica bandera dels pais del sol naixent!
Segon exemple: Un mostruari de figures 🖼️
Aquí considerem el problema P85894 (Problema de mostra). Si l'entrada és
Blue
40
la sortida ha de ser
Aquesta n'és una solució possible:
from PIL import Image, ImageDraw
def rect(x1, y1, x2, y2, col):
dib.polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)], col)
col = input()
n = int(input())
img = Image.new('RGB', (400, 4*n), 'Beige')
dib = ImageDraw.Draw(img)
rect(0, 3*n, 149, 4*n - 1, col)
dib.ellipse([250, n, 399, 3*n - 1], 'Yellow', 'Red')
dib.polygon([(399, 4*n - 1), (200, 4*n - 1), (399, 3*n)], 'Orange')
for x in range (0, 200):
for y in range(0, 2*n):
dib.point((x, y), (0, x, 0))
img.save('output.png')
A continuació comentem les novetats que mostra aquest programa:
El procediment
rect()
ens serà útil, en aquest problema i altres, per dibuixar rectangles de colorcol
, amb lesentre x1
ix2
, i lesentre y1
iy2
, els extrems inclosos. Usa el mètode polygon, el qual permet dibuixar altres polígons, com ara triangles.La primera instrucció llegeix una línia cridant a
input()
, i la guarda a la variablecol
, la qual contindrà el nom del color que s'ha d'usar posteriorment.La segona instrucció llegeix una línia, n'obté l'enter que conté cridant a
int()
, i el desa a la variablen
. També es podria utilitzar el mòdulyogi
.Fixeu-vos que, ara, les dimensions de la imatge
img
són sempre400
punts d'amplada, però són4*n
punts d'alçada. És a dir, aquí l'alçada depèn de l'entrada del programa. A la majoria de problemes del Jutge, les dues mides de la imatge depenen de les dades d'entrada.Respecte a les instruccions per pintar pròpiament dites, primer dibuixem un rectangle de color
col
, d'amplada, d'alçada , i amb l'extrem superior esquerra a . Després, dibuixem una el·lipse de diàmetre horitzontal
, diàmetre vertical , enganxada al marge dret (els punts més a la dreta tenen ), i amb punts tant per sobre com per sota, és a dir, centrada verticalment. Com que cridem el mètode ellipse
amb dos colors, el primer ('Yellow'
) s'usa per omplir-la, i el segon ('Red'
) per perfilar-la.Després, dibuixem un triangle amb punts
, i , de color 'Orange'
. L'ordre en què es donen els tres punts no és important.Segueix un bucle
for
dins d'un altre buclefor
, on per a tot parell (x
,y
) ambx
entre0
i199
iy
entre0
i2n
1
, es pinta aquell punt amb el color(0, x, 0)
, tal i com indica l'enunciat. Com que el segon paràmetre del triplet es correspon al color primari verd, i tant el vermell com el blau són0
, l'efecte és una transició del negre fins a un verd relativament intens (sobre un màxim de ). Lògicament, aquests dos bucles es podrien haver fet d'altres maneres: per exemple, el
for
per a lesx
podria estar dins delfor
per a lesy
. O es podrien pintar els punts en altres ordres diferents, més o menys artificials.
Com a comentari final, com que els quatre dibuixos d'aquest problema no se superposen, l'ordre en què es fan és irrellevant. Per exemple, podríem dibuixar primer els punts verds, després l'el·lipse, després el triangle i al final el rectangle, i el resultat seria el mateix.
Salvador Roura
Lliçons.jutge.org
© Universitat Politècnica de Catalunya, 2024