Formato dei file. Definizione del formato immagine pbm. Il formato immagine pbm

Documenti analoghi
Programmi con argomenti

9 - Array. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Programmazione orientata agli oggetti. Ivan Lanese

- int nbrani = 0; //numero di brani presenti in archivio. int creaplaylist(tipobrano[] playlist, int n, TipoGenere g, TipoFile t);

CODIFICA IMMAGINI IN BIANCO E NERO

Programmazione orientata agli oggetti. Ivan Lanese e Cosimo Laneve

Esercitazione 4. Comandi iterativi for, while, do-while

Appunti di informatica. Lezione 8 anno accademico Mario Verdicchio

La codifica delle immagini

liste ogni nodo ha un successore, tranne l ultimo della lista che ne ha zero; alberi binari ogni nodo ha zero, uno oppure due figli

Definizione di classi. Walter Didimo

PROGRAMMAZIONE: I sottoprogrammi

Esercitazione di Laboratorio - 4

Operazioni su file di caratteri

Codice Gray. (versione Marzo 2007)

C: panoramica. Violetta Lonati

Lezione 21 e 22. Valentina Ciriani ( ) Laboratorio di programmazione. Laboratorio di programmazione. Lezione 21 e 22

IEIM Esercitazione VI Puntatori e Struct. Alessandro A. Nacci -

Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018

Assegnazione di una variabile

Introduzione al linguaggio C Puntatori

Rappresentazione di dati come testo e in forma binaria

Informatica B

ESERCIZIO: SHIFT ARRAY (1)

La copia di un file. contare di quanti caratteri sia composto il file (e quindi determinare la dimensione del file di origine)

Scritto di Algoritmi e s.d. (1o anno) 5 Luglio 2005

IEIM Esercitazione VI A cosa servono le funzioni, la ricorsione e l enum

La codifica delle immagini

Informatica B

Esercizio 1: parole nel testo

Esercitazione 11. Liste semplici

Unità A1 Funzioni MODULO Java 2

CORSO DI PROGRAMMAZIONE

Programmazione Orientata agli Oggetti. Emilio Di Giacomo e Walter Didimo

APPELLO SCRITTO DI PROGRAMMAZIONE 1 CORSO DI LAUREA IN MATEMATICA UNIVERSITÀ DEGLI STUDI DI MILANO VI.2015

Stringhe e allocazione dinamica della memoria

Introduzione al linguaggio C Puntatori

File binari e file di testo

Strutture Dati per Inserimento Ordinato. Luca Abeni

perror: individuare l errore quando una system call restituisce -1

Si usano quando serve accesso rapido sia in lettura che in scrittura su un insieme non ordinato

14 - Metodi e Costruttori

Esercizi di riepilogo (Fondamenti di Informatica 1 Walter Didimo)

Corso di Fondamenti di Programmazione canale E-O. Strutture. Strutture. Definizione di strutture (2) Definizione di strutture (1)

Sistemi di Elaborazione delle Informazioni

Implementazione dell albero binario in linguaggio C++

Laboratorio di Matematica e Informatica 1

Linguaggio C. Esercizio 1

Introduzione al linguaggio C Puntatori

Scheme: struttura del programma e campo di azione

7. Settima esercitazione autoguidata: alberi binari

Istruzioni di controllo

#include<stdio.h> #include<stdlib.h> #include<string.h> typedef char Griglia [3][3];

Array di array. Walter Didimo

Capitolo 10 - Strutture

Gestione dei File. Credits Prof. Campi

Gli Array. Dichiarazione di un array

C array. Problema: scrivere un programma che, ricevuto in input un intero n ed n interi positivi, li stampi in ordine inverso.

Sunto della scorsa lezione Il linguaggio C. Un Programma con Errori. Il preprocessore. Linguaggi di Programmazione I.

FUNZIONI che operano su LISTE e TEMI d ESAME

Digitalizzazione delle immagini

Cosa serve per cominciare?

Algoritmi di Ricerca. Esempi di programmi Java

Liste concatenate e allocazione dinamica

Algoritmi di Ricerca. Esempi di programmi Java

Array e arimetica dei puntatori

Corso sul linguaggio Java

I tipi strutturati e i record in C++

Linguaggio C. Generalità sulle Funzioni. Variabili locali e globali. Passaggio di parametri per valore.

Fondamenti di Programmazione

I Metodi. Fondamenti di Informatica A-K

Il linguaggio C. Prof. E. Occhiuto INFORMATICA 242AA a.a. 2010/11 pag. 1

Lezione 8. Sottoprogrammi

Unità Didattica 4 Linguaggio C. Vettori. Puntatori. Funzioni: passaggio di parametri per indirizzo.

La codifica delle immagini

Mini-dispensa sui puntatori in C

Sistemi Operativi Teledidattico

Esercizi. La funzione swapint() primo tentativo

Informazione multimediale

Corso sul PACCHETTO OFFICE. Modulo Excel

Dati aggregati. Violetta Lonati

Un tipo di dato astratto (ADT) definisce una categoria concettuale con le sue proprietà: una definizione di tipo

Funzioni. (Passaggio dei parametri per riferimento) Passaggio dei parametri

Permutazioni. 1 Introduzione

INTRODUZIONE ALLA PROGRAMMAZIONE AD ALTO LIVELLO IL LINGUAGGIO JAVA. Fondamenti di Informatica - D. Talia - UNICAL 1. Fondamenti di Informatica

INTRODUZIONE ALLA PROGRAMMAZIONE AD ALTO LIVELLO IL LINGUAGGIO JAVA. Fondamenti di Informatica - Programma

Stringhe in C. Alessandra Giordani Lunedì 23 aprile 2012

A. Veneziani - Le variabili puntatore

Approfondimento : numeri pseudocasuali

Definizione di metodi in Java

Esercizi su array di array

Input/output da file I/O ANSI e I/O UNIX FLUSSI E FILE FLUSSI FLUSSI di TESTO FLUSSI BINARI FILE

Tipi definiti dall'utente

Transcript:

Formato dei file Il formato immagine pbm Il formato immagine pbm consente di rappresentare immagini in bianco e nero. Ogni file rappresenta una immagine come un file testo. Definizione del formato immagine pbm Definizione del formato immagine pbm In questa pagina vediamo come è possibile, con un programma C, scrivere un file che contiene una immagine. Per prima cosa, i file immagini sono file come tutti gli altri, per cui possono venire aperti (in lettura oppure in scrittura) come un qualsiasi altro file. Quello che li rende diversi è solo il modo in cui sono organizzati, ossia il tipo di informazioni che contengono. Esistono numerosi modi di rappresentare una immagine su file, e ognuno di questi dà luogo a un differente formato. Formati esistenti sono per esempio: JPEG, GIF, BMP, PCX, PNG, TIFF, TGA, RAS/SUN, ICO, PBM, PPM, ecc. In questa pagina vediamo il solo formato plain pbm, che è il più facile da realizzare usando un programma C. Cominciamo quindi a leggere la specifica del formato pbm. Per prima cosa, si tratta di un formato raster, ossia rappresenta una immagine pixel per pixel. Inoltre, si tratta di un formato per rappresentare solo immagini in bianco e nero. È quindi chiaro che, per ogni pixel dell immagine che si va a rappresentare, il pixel può essere solo bianco o nero. In particolare, un file pbm è fatto come segue: 1. Il formato specifica che il file deve cominciare con la stringa P1. Se quindi vogliamo realizzare un programma che scrive un file in formato pbm, occorre aprire un file in scrittura, e scrivere subito la stringa P1. 2. La seconda cosa che il file deve contenere è la sua dimensione. In altre parole, per scrivere un file pbm, dobbiamo scrvivere larghezza e altezza di seguito alla stringa P1, tutti separati da spazi. 3. La cosa che segue nel file è la sequenza dei pixel della immagine. Come si è detto, ogni pixel può essere colorato solo in nero oppure in bianco. In particolare, il nero si rappresenta come 1, mentre il bianco è 0. Una cosa importante da tenere conto è la disposizione di questi numeri su file. Infatti, si deve partire dalla posizione in alto a sinistra, spostandosi ogni volta a destra fino alla fine della riga e poi in basso. I primi due punti sono facili da interpretare: si scrive su file la stringa P1, poi la larghezza dell immagine e poi l altezza. Il terzo punto risulta subito chiaro se si disegna la griglia dei pixel, con le corrispondenti coordinate: 1

coordinata(1,h-1) coordinata (2,h-1) coordinata (0,h-1) coordinata (0,h-2) punto di coordinate (0,0) punto di coordinate (1,0) Il terzo punto della specifica dice semplicemente che i pixel devono apparire sul file come se venissero letti normalmente da sinistra a destra e dall alto in basso. In altre parole, il file si ottiene leggendo i valori dei pixel come se fossero parole di un testo. È quindi chiaro che si deve partire dal pixel in alto a sinistra, leggendo una riga per volta. Si deve quindi stampare prima il pixel di coordinate 0,h-1 poi 1,h-1 poi 2.h-2, ecc e solo quando la riga è finita si può passare alla coordinata y=h-2. Supponiamo quindi di voler stampare una matrice in cui tutti i punti siano bianchi tranne quelli all interno del rettangolo compreso fra i punti (10,10) e (40,20). Dal momento che dobbiamo stampare una riga per volta, e che il valore della coordinata y che indica la riga deve andare da h-1 a 0, il ciclo più esterno sarà: for(y=h-1; y>=0; y--) Per ogni riga, vanno stampati tutti i suoi punti, quindi occorre fare un secondo ciclo sulla coordinata x: for(x=0; x<=w-1; x++) Per ogni pixel, possiamo decidere se deve essere bianco o nero a seconda se si trova o meno all interno del rettangolo. In particolare, il punto è nero solo se la sue coordinata x è compresa fra le x dei due punti estremi del rettangolo, e lo stesso per la y. Quindi, per ogni punto facciamo: if( x>=10 && x<=40 && y>=10 && y<=20 ) fprintf(fd, "1\n"); else fprintf(fd, "0\n"); 2

Il programma completo di stampa del rettangolo rettangolo.c è riportato qui sotto. Scrive un rettangolo su file. #include<stdlib.h> #include<stdio.h> int main() { int w=200, h=100; FILE *fd; apre il file fd=fopen("rettangolo.pbm", "w"); if( fd==null ) { perror("errore in apertura del file"); exit(1); scrive il rettangolo fprintf(fd, "P1\n"); fprintf(fd, "%d %d\n", w, h); for(y=h-1; y>=0; y--) for(x=0; x<=w-1; x++) if( x>=10 && x<=40 && y>=10 && y<=20 ) fprintf(fd, "1\n"); else fprintf(fd, "0\n"); fclose(fd); chiude il file return 0; L immagine che viene prodotta da questo programma è riportata qui sotto. Una immagine random Una immagine random Il seguente programma random.c scrive una immagine random su un file. La istruzione srandom(time(null)); è necessaria all inizio per inizializzare il generatore di numeri random. Dopo aver aperto il file, si determinano altezza e larghezza dell immagine sempre come numeri random. Per ogni punto dell immagine, si decide se deve essere nero o bianco semplicemente usando il generatore di numeri casuali. Si ricorda che il risultato della espressione random()%n, in cui n è un numero intero, ritorna un numero casuale compreso fra 0 ed n-1. 3

Scrive una bitmap random. #include<stdlib.h> #include<stdio.h> #include<time.h> int main() { FILE *fd; int w, h; inizializza il generatore casuale srandom(time(null)); apre il file in scrittura fd=fopen("test.pbm", "w"); if(fd==null) { perror("errore in apertura del file"); exit(1); determina larghezza e altezza w=random()%300; h=random()%300; intestazione fprintf(fd, "P1 %d %d\n", w, h); scrive la bitmap su file for(y=h-1; y>=0; y--) for(x=0; x<=w-1; x++) if(random()%2) fprintf(fd, "1\n"); else fprintf(fd, "0\n"); fclose(fd); chiude il file return 0; Un esempio di immagine prodotta è qui sotto. Usare una struttura per rappresentare una immagine 4

Usare una struttura per rappresentare una immagine La strategia seguita nelle pagine precedenti per scrivere un file immagine andava bene per immagini semplici. Infatti, quello che si faceva era un doppio ciclo, in cui per ogni punto, si controllava se doveva essere bianco o nero in base alle specifiche di come doveva essere fatta l immagine. Questo va bene per disegnare un singolo rettangolo, ma si provi a pensare come va modificato il codice se i rettangoli da disegnare sono quattro: l istruzione di test se un punto è all interno di essi diventa molto lunga. Si tenga anche presente che l immagine con quattro rettangoli è molto semplice, e che può servire scrivere immagini molto più complicate. A meno che l immagine non sia realmente molto semplice, si usa di solito un altra strategia: si memorizza l immagine in una matrice, e ogni volta che si vuole disegnare qualcosa si cambiano gli elementi di questa matrice. Ogni volta che si vuole il file immagine, si stampa la matrice su file. Per essere più precisi, per rappresentare l immagine servono due interi che identificano larghezza e altezza, più una matrice grande abbastanza per contenere tutti i pixel. Dal momento che queste tre variabili descrivono complessivamente la stessa cosa, usiamo una struttura. Per la matrice, ipotizziamo che l immagine non sia più larga di 400 400. struct ImmagineBW { int larghezza; int altezza; int mat[400][400]; ; Con questa struttura possiamo memorizzare immagini di diverse dimensioni, a condizioni che entrambe le dimensioni siano comprese fra 0 e 400. Il primo passo da fare è quello di definire una variabile di tipo immagine per rappresentare l immagine che stiamo costruendo. La seconda cosa da fare è quella di dire quanto è grande l immagine, ossia mettere i valori giusti di larghezza e altezza nei campi della variabile immagine. A questo punto, mettiamo 0 in tutti gli elementi significativi della matrice. Questo equivale a cancellare l immagine, ossia a definire inizialmente tutti i punti come bianchi. Se non si compie questa operazione la matrice contiene valori non inizializzati, per cui i suoi valori sono indeterminati. Abbiamo ora una lavagna su cui disegnare. Se per esempio vogliamo disegnare il punto di coordinate x,y, basta mettere a uno l elemento della matrice di indici x e y. Per il rettangolo, facciamo un doppio ciclo, e in questo modo riusciamo a fare questa operazione per tutti i punti del rettangolo. Il programma completo matrice.c è qui sotto. Definisce il tipo immagine come una matrice. #include<stdlib.h> #include<stdio.h> definizione del tipo struct ImmagineBW { int larghezza; int altezza; 5

int mat[400][400]; ; crea un file a partire da una matrice void CreaFileImmagine(char *nomefile, struct ImmagineBW img) { FILE *fd; apre il file fd=fopen(nomefile, "w"); if( fd==null ) { perror("errore in apertura del file"); exit(1); stampa la matrice fprintf(fd, "P1\n"); fprintf(fd, "%d %d\n", img.larghezza, img.altezza); for(y=img.altezza-1; y>=0; y--) for(x=0; x<=img.larghezza-1; x++) if( img.mat[x][y] ) fprintf(fd, "1\n"); else fprintf(fd, "0\n"); fclose(fd); chiude il file main int main() { struct ImmagineBW q; inizializza la matrice q.larghezza=200; q.altezza=100; for(x=0; x<=q.larghezza-1; x++) for(y=0; y<=q.altezza-1; y++) q.mat[x][y]=0; disegna il rettangolo nella matrice for(x=10; x<=40; x++) for(y=10; y<=20; y++) q.mat[x][y]=1; scrive l immagine su file 6

CreaFileImmagine("matrice.pbm", q); return 0; L immagine che viene generata da questo programma è riportata qui sotto. Grafico di una funzione Grafico di una funzione Il programma che segue realizza il grafico di una funzione in formato pbm. Il problema non presenta nessuna difficoltà: si dichiara una struttura per rappresentare l immagine, e la si inizializza. Alla fine, va scritta su file. Questo schema è fisso: quello che manca è la specifica di quello che si fa in mezzo, ossia il disegno vero e proprio che viene fatto sulla matrice. Nel nostro caso, per ogni valore di x determiniamo il valore di y (il risultato della funzione), e mettiamo a 1 il pixel di coordinate x,y della matrice. L unica cosa da tenere in considerazione è il fatto che il valore di y non è necessariamente compreso negli indici ammissibili della matrice. Prima di effettuare l operazione di mettere 1 nella matrice, occorre quindi controllare che il punto sia compreso all intero delle coordinate dell immagine. Il programma grafico.c è qui sotto. Grafico di una funzione in formato pbm #include<stdlib.h> #include<stdio.h> definizione del tipo struct ImmagineBW { int larghezza; int altezza; int mat[400][400]; ; crea un file a partire da una matrice void CreaFileImmagine(char *nomefile, struct ImmagineBW img) { FILE *fd; apre il file fd=fopen(nomefile, "w"); if( fd==null ) { perror("errore in apertura del file"); 7

exit(1); stampa la matrice fprintf(fd, "P1\n"); fprintf(fd, "%d %d\n", img.larghezza, img.altezza); for(y=img.altezza-1; y>=0; y--) for(x=0; x<=img.larghezza-1; x++) if( img.mat[x][y] ) fprintf(fd, "1\n"); else fprintf(fd, "0\n"); fclose(fd); chiude il file main int main() { struct ImmagineBW q; inizializza la matrice q.larghezza=200; q.altezza=100; for(x=0; x<=q.larghezza-1; x++) for(y=0; y<=q.altezza-1; y++) q.mat[x][y]=0; realizza il grafico della funzione for(x=0; x<=q.larghezza-1; x++) { y=x/4+(x/8)%20-10; if(y>=0 && y<=q.altezza-1) q.mat[x][y]=1; scrive l immagine su file CreaFileImmagine("funzione.pbm", q); return 0; L immagine che viene generata è qui sotto: Funzioni di disegno 8

Funzioni di disegno Nel programma precedente abbiamo visto una funzione che permette di scrivere una figura su file. È però possibile anche scrivere delle funzioni che realizzano disegni sulla struttura che rappresenta l immagine. Una cosa sicuramente utile è scrivere una funzione che inizializza l immagine, cioè mette i valori di altezza e larghezza nei corrispondenti campi, e riempie la matrice di zeri. Usiamo una funzione che restiuisce una immagine. È infatti possibile relizzare delle funzioni che hanno una struttura come valore di ritorno. struct ImmagineBW ImmagineVuota(int l, int a) { struct ImmagineBW img; img.larghezza=l; img.altezza=a; for(x=0; x<=l-1; x++) for(y=0; y<=a-1; y++) img.mat[x][y]=0; return img; Questa funzione mette nei campi larghezza e altezza i parametri passati. Inoltre, mette a zero tutti gli elementi significativi della matrice. Un altra funzione che può essere utile è quella che disegna una linea orizzontale fra due punti x1, y e x2, y. Questa funzione deve ricevere come parametri le tre coordinate x1, y2, x, ed inoltre la struttura su cui fare il disegno. Si noti che la funzione modifica l immagine, per cui questa va passata per riferimento. In altre parole, dobbiamo passare l indirizzo della struttura e non la struttura stessa. void DisegnaLineaOrizzontale(struct ImmagineBW *pi, int x1, int x2, int y) { int x; for(x=x1; x<=x2; x++) (*pi).mat[x][y]=1; Se si fosse passata l immagine per valore (ossia il parametro fosse stato struct ImmagineBW img) allora la funzione avrebbe operato con una copia locale della struttura, per cui le modifiche non avrebbero avuto effetto sulla struttura di partenza. In altre parole, chiamando DisegnaLineaOrizzontale(q,...) si sarebbe creata una nuova struttura immagine img, e le modifiche sarebbero state fatte su questa invece che su q. Passando l indirizzo, siamo sicuri che ogni modifica fatta su *pi viene fatta sulla struttura della quale abbiamo passato l indirizzo. È anche possibile definire in modo analogo funzioni di disegno di linee verticali, rettangoli, linee oblique, ecc. L immagine su cui si opera, dal momento che viene modificata dalla funzione, e vogliamo che le modifiche siano visibili al programma chiamante, va passata per riferimento. Una volta definite queste funzioni, possiamo realizzare un programma che scrive una immagine con due linee, in questo modo: 9

int main() { struct ImmagineBW q; inizializza l immagine q=immaginevuota(300, 200); disegna due linee DisegnaLineaOrizzontale(&q, 10, 180, 20); DisegnaLineaVerticale(&q, 150, 50, 150); scrive l immagine su file CreaFileImmagine("duelinee.pbm", q); return 0; Va notato come, in questo programma, non ci serve sapere né come sono rappresentate le immagini, e nemmeno in che modo funzionano le funzioni di inizializzazione, disegno e stampa su file. Tutto quello che ci serve sapere, di ognuna di queste funzioni, è cosa fa. In altre parole, se qualcun altro vuole scrivere un programma che fa un disegno differente, tutto quello che deve sapere è che prima va chiamata la funzione ImmagineVuota passando le dimensioni della figura, e poi che per disegnare una linea si usano le funzioni DisegnaLineaOrizzontale e DisegnaLineaVerticale, e che alla fine si chiama la funzione CreaFileImmagine per scrivere l immagine su file. Non gli serve sapere né come è fatta la struttura struct ImmagineBW e nemmeno in che modo sono realizzate le varie funzioni. Il programma completo confun.c è qui sotto. Funzioni per disegnare in una struttura. #include<stdlib.h> #include<stdio.h> definizione del tipo struct ImmagineBW { int larghezza; int altezza; int mat[400][400]; ; crea un file a partire da una matrice void CreaFileImmagine(char *nomefile, struct ImmagineBW img) { FILE *fd; apre il file fd=fopen(nomefile, "w"); if( fd==null ) { perror("errore in apertura del file"); 10

exit(1); stampa la matrice fprintf(fd, "P1\n"); fprintf(fd, "%d %d\n", img.larghezza, img.altezza); for(y=img.altezza-1; y>=0; y--) for(x=0; x<=img.larghezza-1; x++) if( img.mat[x][y] ) fprintf(fd, "1\n"); else fprintf(fd, "0\n"); fclose(fd); chiude il file Crea una immagine vuota. struct ImmagineBW ImmagineVuota(int l, int a) { struct ImmagineBW img; img.larghezza=l; img.altezza=a; for(x=0; x<=l-1; x++) for(y=0; y<=a-1; y++) img.mat[x][y]=0; return img; Disegna una linea orizzontale. void DisegnaLineaOrizzontale(struct ImmagineBW *pi, int x1, int x2, int y) { int x; for(x=x1; x<=x2; x++) (*pi).mat[x][y]=1; Disegna una linea verticale. void DisegnaLineaVerticale(struct ImmagineBW *pi, int x, int y1, int y2) { int y; for(y=y1; y<=y2; y++) (*pi).mat[x][y]=1; 11

main int main() { struct ImmagineBW q; inizializza l immagine q=immaginevuota(300, 200); disegna due linee DisegnaLineaOrizzontale(&q, 10, 180, 20); DisegnaLineaVerticale(&q, 150, 50, 150); scrive l immagine su file CreaFileImmagine("duelinee.pbm", q); return 0; Questo programma genera l immagine riportata qui sotto. 12