Εδώ βλέπετε τις διαφορές μεταξύ της επιλεγμένης έκδοσης και της τρέχουσας έκδοσης της σελίδας.
Προηγούμενος έλεγχος και από τις δύο πλευρές Προηγούμενη αναθεώρηση Επόμενη αναθεώρηση | Προηγούμενη αναθεώρηση | ||
c_image_processing [2014/02/26 12:23] chiossif |
c_image_processing [2020/11/21 09:52] (τρέχουσα) |
||
---|---|---|---|
Γραμμή 2: | Γραμμή 2: | ||
Στο wiki αυτό θα μάθουμε να διαχειριζόμαστε και γενικότερα επεξεργαζόμαστε πολυκάναλες εικόνες με την [[https:// | Στο wiki αυτό θα μάθουμε να διαχειριζόμαστε και γενικότερα επεξεργαζόμαστε πολυκάναλες εικόνες με την [[https:// | ||
+ | |||
Γραμμή 9: | Γραμμή 10: | ||
Έτσι, προκειμένου η διαδικασία να γίνει πιο εύκολη, | Έτσι, προκειμένου η διαδικασία να γίνει πιο εύκολη, | ||
- | Έχοντας λοιπόν την [[ https:// | + | Έχοντας λοιπόν την [[https:// |
wget https:// | wget https:// | ||
και μέσω της gdal την μετατρέπουμε σε ers πρότυπο | και μέσω της gdal την μετατρέπουμε σε ers πρότυπο | ||
Γραμμή 194: | Γραμμή 195: | ||
-> Ο συνδυασμός των παραπάνω τεχνικών δήλωσης και κατάληψης μνήμης επιτρέπει την άμεση διευθυνσιοδότηση χωρίς αριθμητική δεικτών. | -> Ο συνδυασμός των παραπάνω τεχνικών δήλωσης και κατάληψης μνήμης επιτρέπει την άμεση διευθυνσιοδότηση χωρίς αριθμητική δεικτών. | ||
+ | |||
+ | |||
Γραμμή 224: | Γραμμή 227: | ||
και την χρησιμοποιώ έτσι: | και την χρησιμοποιώ έτσι: | ||
IMAGE_PIXEL(i, | IMAGE_PIXEL(i, | ||
+ | |||
+ | Παράδειγμα [[http:// | ||
+ | #define IMAGE_PIXEL(x, | ||
+ | |||
+ | και παράδειγμα συνάρτησης: | ||
+ | char image_pixel(int x, int y, int z){ | ||
+ | | ||
+ | } | ||
Προσοχή θέλουν εδώ τα ονόματα των εμπλεκομένων μεταβλητών καθώς και η [[http:// | Προσοχή θέλουν εδώ τα ονόματα των εμπλεκομένων μεταβλητών καθώς και η [[http:// | ||
Γραμμή 378: | Γραμμή 389: | ||
-> Προσέξτε πως ελέγχουμε την απόδοση τιμής στο αποτέλεσμα. Μην ξεχνάμε πως προδιαγραφή (και άρα περιορισμός) είναι η εικόνα αποτέλεσμα να είναι 8bit όπως και η εικόνα εισόδου. Αυτό το λογισμικό αντιμετωπίζει μόνο εικόνες 8bit και έτσι πρέπει να έχει τιμές στο εύρος [0,255] (unsigned char). | -> Προσέξτε πως ελέγχουμε την απόδοση τιμής στο αποτέλεσμα. Μην ξεχνάμε πως προδιαγραφή (και άρα περιορισμός) είναι η εικόνα αποτέλεσμα να είναι 8bit όπως και η εικόνα εισόδου. Αυτό το λογισμικό αντιμετωπίζει μόνο εικόνες 8bit και έτσι πρέπει να έχει τιμές στο εύρος [0,255] (unsigned char). | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
====Ερωτήσεις==== | ====Ερωτήσεις==== | ||
- | Αναμένω τις ερωτήσεις σας :-) | + | * Ερ.: Έχουμε δύο εικόνες με ένα κανάλι και ίδιες διαστάσεις από το ίδιο αντικείμενο/ |
+ | * Απ.: Χρησιμοποιώντας τον παραπάνω κώδικα έγιναν δύο βασικές αλλαγές. Η πρώτη αφορά στα δεδομένα τα οποία χρειάζονται και πρέπει να διαβαστούν και η δεύτερη στη συνθήκη η οποία θα υλοποιήσει το επιθυμητό αποτέλεσμα. Συγκεκριμένα, | ||
+ | |||
+ | Το πρόγραμμα διαβάζει τις εικόνες σε τυποποίηση ERS. Για τη μετατροπή αυτή έχει χρησιμοποιηθεί η ακόλουθη εντολή από τερματικό : | ||
+ | gdal_translate -of ers -ot byte -strict -scale -b 1 input.tif input.ers | ||
+ | |||
+ | σε σύστημα με εγκατεστημένη την βιβλιοθήκη gdal. | ||
+ | |||
+ | Την υλοποίηση θα την βρείτε στον σύνδεσμο [[https:// | ||
+ | |||
+ | image_out[k]=((i/ | ||
+ | |||
+ | όπου με τον τελεστή ()?: αποφασίζεται και αποθηκεύεται στην εικόνα αποτέλεσμα image_out στην θέση k (πρόκειται για ενδιάμεση μνήμη αποθήκευσης μιας γραμμής) είτε η image_in1 είτε η image_in2. Η απόφαση λαμβάνεται από το λογικό (i/ | ||
====Ασκήσεις==== | ====Ασκήσεις==== | ||
Γραμμή 398: | Γραμμή 427: | ||
- | =====Φιλτράρισμα σε εικόνα με ενδιάμεση αποθήκευση===== | ||
- | Τώρα με την | + | |
+ | =====Φιλτράρισμα σε εικόνα===== | ||
+ | |||
+ | Στην | ||
#include < | #include < | ||
Γραμμή 550: | Γραμμή 582: | ||
} | } | ||
- | Μόλις είδαμε μια εφαρμογή για φιλτράρισμα εικόνας με κυλιόμενη ενδιάμεση μνήμη προσωρινής αποθήκευσης. Μελετήστε το και επανέρχομαι ;-) | + | Μόλις είδαμε μια εφαρμογή για φιλτράρισμα εικόνας με κυλιόμενη ενδιάμεση μνήμη προσωρινής αποθήκευσης. Ας το δούμε πιο προσεκτικά (αν και όχι τόσο αναλυτικά - μελετήστε...): |
+ | |||
+ | -> Χρησιμοποίησα πίνακα δεικτών για να αποθηκεύσω το φίλτρο στην μνήμη αλλά για την εικόνα εισόδου και εξόδου έναν πίνακα για κάθε μία και αριθμητική δεικτών για την προσπέλαση. | ||
+ | |||
+ | -> Δεν διαβάζω όλη την εικόνα στην μνήμη αλλά αντίθετα κρατάω μόνο το πλήθος των γραμμών που είναι απαραίτητες για να εκτελεστεί το φίλτρο. Έτσι υπολογίζω μία γραμμή στην εικόνα εξόδου κάθε φορά και αυτήν αποθηκεύω γραμμή γραμμή. Με αυτό τον τρόπο πετυχαίνω οικονομία μνήμης και παράλληλα | ||
+ | |||
+ | -> Προσέξτε τον τρόπο με τον οποίο «κυλάω» τις παλαιότερες γραμμές στην μνήμη προς τα «πάνω» και διαβάζω μία νέα γραμμή στην θέση της τελευταίας. Έτσι και ενώ σαρώνω γραμμή γραμμή το αρχείο εισόδου ελαχιστοποιώ την επαφή | ||
+ | |||
+ | Ομολογουμένως έχει μια πολυπλοκότητα αλλά είναι τόσα τα πλεονεκτήματα χρήσης που πραγματικά αξίζει τον κόπο. Τέτοιες τεχνικές, | ||
Το αρχείο φίλτρου με το οποίο δοκιμάσαμε τον παραπάνω κώδικα έχει σαν περιεχόμενα: | Το αρχείο φίλτρου με το οποίο δοκιμάσαμε τον παραπάνω κώδικα έχει σαν περιεχόμενα: | ||
Γραμμή 570: | Γραμμή 610: | ||
Μελετήστε τον κώδικα και αναμένουμε τις ερωτήσεις σας :-) | Μελετήστε τον κώδικα και αναμένουμε τις ερωτήσεις σας :-) | ||
+ | |||
Γραμμή 583: | Γραμμή 624: | ||
Θυμίζουμε ξανά την χρήση του προτύπου ers και της gdal για την μετατροπή των εικόνων. :-) | Θυμίζουμε ξανά την χρήση του προτύπου ers και της gdal για την μετατροπή των εικόνων. :-) | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | =====Παράλληλος προγραμματισμός στην επεξεργασία εικόνας===== | ||
+ | |||
+ | Μαθαίνουμε επεξεργασία εικόνας σε C και είδαμε μέχρι εδώ πως να δουλεύουμε με τεράστιες εικόνες διαβάζοντας μόνο ότι είναι απαραίτητο σε κάθε ανακύκλωση. Αν όμως η εικόνα χωράει στην μνήμη; Πως μπορούμε να κάνουμε επεξεργασίες με την μεγαλύτερη δυνατή ταχύτητα; | ||
+ | |||
+ | Οι σύγχρονοι επεξεργαστές περιέχουν πολλούς πυρήνες και έτσι έχουν την δυνατότητα να εκτελέσουν πολλές διεργασίες ταυτόχρονα αρκεί η μία να μην εξαρτάται από την άλλη. Στην περίπτωση των εικόνων αυτό είναι εφικτό με την τμηματική εκτέλεση μιας εικόνας. Αν για παράδειγμα έχουμε τετραπύρηνο επεξεργαστή μπορούμε να κόψουμε μια εικόνα στα τέσσερα (σταυρό) και να δώσουμε κάθε κομμάτι σε κάθε πυρήνα. Φυσικά ο προγραμματισμός σε αυτή την περίπτωση θα ήταν περίπλοκος. | ||
+ | |||
+ | Σήμερα χάρη στην [[http:// | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | | ||
+ | int main(int argc, char **argv){ | ||
+ | FILE *fp; | ||
+ | int rows, cols, rows_1, cols_1, kirsch, a[15], t; | ||
+ | unsigned char *image_in, *image_out; | ||
+ | register i, j, k; | ||
+ | | ||
+ | if (argc!=5){ | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if ((fp=fopen(argv[1]," | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | rows=atoi(argv[2]); | ||
+ | cols=atoi(argv[3]); | ||
+ | if (rows< | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if (cols< | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if ((image_in=malloc(rows*cols*sizeof(char)))==NULL){ | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if ((image_out=malloc(rows*cols*sizeof(char)))==NULL){ | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if ((image_in=malloc(rows*cols*sizeof(char)))==NULL){ | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | | ||
+ | if (fread(image_in, | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if (fclose(fp)==EOF){ | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | | ||
+ | rows_1=rows-1; | ||
+ | cols_1=cols-1; | ||
+ | | ||
+ | { // START parallel | ||
+ | #pragma omp parallel for private(i, j, k, a, t, kirsch) | ||
+ | for (i=1; | ||
+ | for (j=1; | ||
+ | a[0]=image_in[ (i-1) * cols + j-1 ]; | ||
+ | a[1]=image_in[ (i-1) * cols + j ]; | ||
+ | a[2]=image_in[ (i-1) * cols + j+1 ]; | ||
+ | a[3]=image_in[ | ||
+ | a[4]=image_in[ (i+1) * cols + j+1 ]; | ||
+ | a[5]=image_in[ (i+1) * cols + j ]; | ||
+ | a[6]=image_in[ (i+1) * cols + j-1 ]; | ||
+ | a[7]=image_in[ | ||
+ | a[8] =a[0]; | ||
+ | a[9] =a[1]; | ||
+ | a[10]=a[2]; | ||
+ | a[11]=a[3]; | ||
+ | a[12]=a[4]; | ||
+ | a[13]=a[5]; | ||
+ | a[14]=a[6]; | ||
+ | kirsch=0; | ||
+ | for (k=0; | ||
+ | t=(5.0*(a[k]+a[k+1]+a[k+2])-3.0*(a[k+3]+a[k+4]+a[k+5]+a[k+6]+a[k+7]))*256.0/ | ||
+ | if (t> | ||
+ | kirsch=t; | ||
+ | } | ||
+ | image_out [ i * cols + j ] = kirsch; | ||
+ | } | ||
+ | image_out[i*cols+0]=image_out[i*cols+1]; | ||
+ | image_out[i*cols+cols_1]=image_out[i*cols+cols-2]; | ||
+ | } | ||
+ | } // END parallel | ||
+ | memcpy(& | ||
+ | memcpy(& | ||
+ | | ||
+ | if ((fp=fopen(argv[4]," | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if (fwrite(image_out, | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | if (fclose(fp)==EOF){ | ||
+ | printf(" | ||
+ | exit(1); | ||
+ | } | ||
+ | free(image_in); | ||
+ | free(image_out); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | |||
+ | Για το φίλτρο [[https:// | ||
+ | Αλλά ούτε και για την παραλληλοποίηση της εκτέλεσης του φίλτρου θα γράψουμε κάτι. Απλά δείτε: τι κάνει το πρόγραμμά μας παράλληλο; | ||
+ | |||
+ | ... | ||
+ | #include < | ||
+ | ... | ||
+ | { // START parallel | ||
+ | #pragma omp parallel for private(i, j, k, a, t, kirsch) | ||
+ | ... | ||
+ | } // END parallel | ||
+ | ... | ||
+ | και φυσικά μεταγλώττιση με την παράμετρο -fopenmp στην gcc ;-) | ||
+ | Δοκιμάστε το στην Lenna_Grey.ers με την εντολή: | ||
+ | ./a.out Lenna_Grey 512 512 Lenna_Kirsch | ||
+ | Στιγμιαίο έτσι; Όπως θα ήταν και χωρίς τις παραπάνω παράλληλες ρυθμίσεις. Ναι αλλά η Lenna_Grey είναι μικρή. Για δοκιμάστε με μία εικόνα 5120x5120 ή μεγαλύτερη και τα συζητάμε ;-) | ||
+ | |||
+ | Περισσότερα για παράλληλο προγραμματισμό θα βρείτε στον ιστοχώρο της [[https:// | ||
+ | |||
+ | Ελπίζω η σπίθα να άναψε ;-) | ||
+ | |||
+ | Καλή παραλληλοποίηση ;-) | ||
=====Επίλογος===== | =====Επίλογος===== |