Upload
ana-vila
View
44
Download
3
Embed Size (px)
DESCRIPTION
Geometrijske transformacije na fotografijama, projektni zadatak iz paralelnog programiranjaAutori: Ana, Ivan, Branimir
Citation preview
Sveučilište u Mostaru
Fakultet Prirodoslovno-Matematičkih i odgojnih znanosti
Odjel za Informatiku
PROJEKTNI ZADATAK
GEOMETRIJSKE TRANSFORMACIJE NA SLIKAMA
ROTACIJA, ZRCALJENJE I SKALIRANJE
MENTOR: STUDENTI:Dr.sc Sven Gotovac Ivan Kasalo 7647
Ana Vila 7649/I Branimir Boras 6860
Mostar, 2015
SADRŽAJ
1. UVOD.................................................................................................................................1
2. LINEARNE GEOMETRIJSKE TRANSFORMACIJE NA SLIKAMA...........................2
2.1 Zrcaljenje......................................................................................................................3
2.2 Rotacija.........................................................................................................................4
2.3 Skaliranje......................................................................................................................5
3. IMPLEMENTACIJA ALGORITAMA ZA ROTACIJU, SKALIRANJE I ZRCALJENJE6
3.1 Sekvencijalni kod.........................................................................................................7
3.2 Paralelni kod po c11 standardu..................................................................................10
3.3 Paralelna izvedba upotrebom cuda programskog modela..........................................13
4. USPOREDBA BRZINE IZVOĐENJA PROGRAMA....................................................15
5. ZALJUČAK......................................................................................................................17
6. LITERATURA.................................................................................................................18
1
1. UVOD
Obrada fotografija je oblik obrade signala gdje se kao ulazni podatak koristi
fotografija a izlaz može biti nova slika ili bilo koja druga vrsta relevantnih podataka. Rotirati
sliku za određeni broj stupnjeva ili joj pak smanjiti veličinu za određeni faktor su primjeri
uobičajene obrade fotografije.
U većini slučajeva obrada se vrši na cijeloj fotografijji i iste operacije se izvršavaju
nad svakim pikselom što zapravo znači mnogo ponavljanja istog posla. Napredovanjem
tehnologije fotografije postaju kvalitetnije što dovodi do većih datoteka a samim tim i
produljenja vremena potrebnog za njihovu obradu. Zbog velikog broja ponavljanja istih
operacija obrada fotografija spada u kategoriju visoko paralelnih zadataka te je pogodna za
implementaciju na GPU a njihova izvedba je uvelike brža nego na nego slična implementacija
na CPU.
U ovom radu obrađena su tri često korištene transformacije digitalnih fotografija:
rotacija, zrcaljenje i smanjivanje. Transformacije su odrađene korištenjem tri verzije koda za
transformaciju: klasični sekvencijalni C/C++ kod, C/C++ kod sa C11 standardom i upotrebom
dretvi te CUDA C kod za izvedbu na masivno paralelnim procesorima.
2
2. LINEARNE GEOMETRIJSKE TRANSFORMACIJE NA SLIKAMA
Geometrijske transformacije su često korišten postupak za registraciju slika i
uklanjanje geometrijskih distorzija na slikama. Česte primjene uključuju stvaranje mozaika i
geografsko mapiranje a jedan od najčešće korištenih procesa su linearne geometrijske
transformacije.
Baza geometrijskih transformacija je mapiranje jednog koordinatnog sustava na drugi.
Ovo mapiranje se definira pomoću prostornih transformacija – funkcije mapiranja koja
uspostavlja linearnu prostornu vezu između svih točaka ulazne i izlazne fotografije. Za zadanu
prostornu transformaciju svaka točka u izlaznoj slici zauzima vrijednost odgovarajuće točke
ulazne fotografije. Veza između koordinata ulazne i izlazne slike pronalazi se primjenom
odgovarajuće funkcije prostorne transformacije.
Prema tome geometrijska transformacija je funkcija koja preslikava orginalnu točku ili
vektor u njenu sliku, znači da se koordinate piksela orginalne slike A (x,y) mapiraju u točku
(u,v) novog kordinatnog sustava gdje su u i v funkcije koordinata točke (x,y): u=f1(x,y),
v=f1(x,y).
Ovisno o primjeni, funkcije linearne transformacije mogu poprimiti više različitih
oblika a one najjednostavnije su određene analitičkim izrazima uključujući afine,
polinominalne i projektivne transformacije.
3
slika 1. mapiranje iz točke (x,y) u (u,v)
Niz digitalne slike ima implicitnu mrežu koja se mapira u točke nove domene. Na slici
1. možemo vidjeti da ove točke ne moraju pasti u mrežu točaka nove domene. U neprekidnoj
domeni geometrijska transformacija je u potpunosti određena prostornom transformacijom, a
uzrok ovome je činjenica da je mapiranje bijektivno, no u domeni u kojoj mi radimo dolazi do
komplikacija zbog prirode digitalnih slika. Kod digitalnih slika, njeni diskretni elementi,
pikseli leže na mreži koja je u osnovi rešetka integera. Problem se javlja jer se izlazna mreža
za razliku od ulazne ne mora nužno poklapati sa rešetkom, upravo suprotno pozicije izlazne
mreže mogu biti bilo koja vrijednost koja je rezultat funkcije mapiranja.
Pošto je diskretni ulaz definiran samo vrijednostima integera provodi se interpolacija
da bi se neprekinuta površina uklopila u uzorke. Popularne interpolacijske funkcije su kubna
bilinearna i funkcija najbližeg susjeda no u ovom radu radi preglednosti i točnijeg mjerenja
interpolacija nije korištena.
2.1 Zrcaljenje
Zrcaljene slike je operacija rotiranja slike oko neke osi odnosno mjenjanje njenog
horizontalnog ili vertikalnog smjera. Refleksija odnosno zrcaljenje može se vršiti oko točke ili
osi slike. U slučaju zrcaljenja oko osi najčešće korištene transformacije su:
-refleksija oko vertikalne osi x0 ulazne slike:
x2=-x1+(2*x0)
y2=y1
-refleksija oko horizontalne osi y0 ulazne slike:
x2=x1
y2=-y1+(2*x0)
Navedene formule vrše prebacivanje svakog piksela izvorne slike na suprotnu stranu gdje pri
4
vertikalnom zrcaljenju horizontalna koordinata ostaje jednaka a pri horizontalnom ne mjenja
se vertikalna koordinata.
Najčešće se koristi radi vizualnog efekta i pri analizi simetričnosti ali svoje primjene nalazi i
kao operator pri zahtjevnijim operacijama. Primjer vertikalnog zrcaljenja oko osi može se
vidjeti na slici 2.
Slika 2. Orginal lena i nakon vertikalnog zrcaljenja
2.2 Rotacija
Rotacija fotografija je uobičajen proces kod digitalne obrade slika. Pošto sliku rotiramo oko
središta za zadani kut za računanje novih pozicija piksela korištene su formule:
r’ = r0 + (r – r0)cos(theta) – (c – c0)sin(theta)
c’ = c0 + (r - r0)sin(theta) + (c – c0)cos(theta)
gdje su r0 i c0 koordinate središta slike.
5
Na slici ispod možemo vidjeti sliku lena rotiranu za 45 stupnjeva oko njenog središta.
Slika 3. Lena rotirana za 45 stupnjeva
Rotacija je znači funkcija definirana kutom rotacije i središtem rotacije te iako korisna sama
po sebi često se koristi u početnim stadijima sofisticiranijih operacija nad slikama. Na primjer
postoje mnogobrojni operatori smjera kao što su na primjer operatori kod detekcije rubova
koji rade samo na limitiranom skupu smjerova. Rotacijom slike za određeni kut prije primjene
operatora za detekciju rubova može se konstruirati hibridni operator koji će koji će djelovati u
smjeru u kojem želimo.
2.3 Skaliranje
Skaliranje fotografija je osnovna operacija bilo kojeg sustava za obradu fotografija. To je
takva transformacija kojom se može povećavati ili smanjivati širina ili visina slika, pa time
možemo dobiti razne efekte izduživanja ili zbijanja slika, ili pak smanjenje ili povećanje
canvas slika ako i visinu i širinu slika smanjujemo odnosno povećavamo proporcionalno.
Smanjivanje fotografija odnosno subsampling provodi se mijenjanjem vrijednosti grupe
6
piksela jednom vrijednosti iz te grupe ili interpolacijom vrijednosti svih susjednih piksela.
Slika 4. subsampling metodom odabira piksela i interpolacijom
Na slici 4. možemo vidjeti dvije navedene metode za skaliranje fotografija. Dok je
interpolacija susjednih piksela točnija i vodi do kvalitetnijih izlaznih fotografija, odabir
piksela koji će predstavljati svoje okruženje je manje zahtjevna u pogledu izračuna te je
samim tim i brža.
U nastavku rada obrađene su rotacija, zrcaljenje i skaliranje slike u tri izvedbe: C/C++
sekvencijalno, C/C++ paralelno (C11 standard) i Cuda C za paralelnu izvedbu na GPU. Za
svaku izvedu napravljena su mjerenja vremena izvođenja te rezultati prikazani u tablici
3. IMPLEMENTACIJA ALGORITAMA ZA ROTACIJU, SKALIRANJE I ZRCALJENJE
Sa tisućama piksela kojima treba pratiti pozicije obrada digitalnih fotografija može se
činiti kao zahtjevan zadatak ali nakon kraćeg razmatranja možemo vidjeti da je svaka
fotografija zapravo jednostavna i dobro poznata struktura podataka. Za početak uzmimo
7
jednostavnu fotografiju u sivim tonovima. Svaki piksel ovakve fotografije ima jednu
vrijednost a to je siva. Ako razmotrimo piksele kao obične brojeve a ne tonove boje dobijemo
2D niz koji je spremljen u .txt datoteku pa je sve što je potrebno uraditi zapravo učitati taj niz
i obaviti neke osnovne operacije nad njim.
Kada se slika pogleda kao dvodimenzionalni niz odnosno matrica izvršavanje osnovnih
geometrijskih transformacija zapravo se svodi na preslagivanje redaka i stupaca u matrici.
Tako se promjene u matrici kod zrcaljenja slike mogu vidjeti na slici ispod:
Slika 5. 4x4 matrica prije i poslije vertikalnog zrcaljenja
Za testiranje koda i mjerenje brzine korištena je slika lena.pgm dimenzija 2400x3000 px u
sivim tonovima.
3.1 Sekvencijalni kod
U sekvencijalnom kodu nakon učitavanja slike alociramo prostor u memoriji za slike koju
ćemo dobti rotacijom, zrcaljenjem i smanjivanje, te pozivamo funkcije:
void rotiraj(int theta, PGMImage &izvorna, PGMImage &rot);
8
void smanji(int faktor, PGMImage &izvorna, PGMImage &resize);
void mirror (bool flag, PGMImage &izvorna,PGMImage &mirr);
a) rotacija
Funkcija rotiraj kroz dvije petlje pomoću već spomenute formule određuje novu poziciju
piksela te kopira vrijednosti piksela iz orginalne slike na nove pozicije.
for(int c = 0; c < stupac; c++)
for(int r = 0; r < red; r++)
{
r1 = (int) (r0 + ((r - r0) * cos(rads)) - ((c - c0) * sin(rads)));
c1 = (int) (c0 + ((r - r0) * sin(rads)) + ((c - c0) * cos(rads)))
mirr.grey[c1][r1] = izvorna.grey[c][r];
}
Nakon kopiranja piksela pokreće se nova petlja koja ispunjava “propadnute” piksele, koji se
javljaju zbog bijektivnosti mapiranja, vrijednostima iz njihovih desnih susjeda.
for(int i = 0; i < red; i++)
for(int j = 0; j < stupac; j++)
if(mirr.grey[i][j] == 0)
mirr.grey[i][j] = mirr.grey[i][j+1];
9
b) zrcaljanje
Kod zrcaljenja fotografije na osnovu izbora korisnika vrši se vertikalni ili horizontalni mirror.
Kod horizontalnog mirrora vertikalne pozicije piksela se mjenjaju u odnosu na središnju
horizontalnu os:
for(int i = 0; i < red; i++)
for(int j = 0; j < stupac; j++)
mirr.grey[red - (i+1)][j] =izvorna.grey[i][j];
Kod vertikalnog zrcaljenja vrši se isti postupak samo što se mijenjaju horizontalne pozicije:
for(int i = 0; i < red; i++)
for(int j = 0; j < stupac; j++)
mirr.grey[i][stupac - (j + 1)] = izvorna.grey[i][j];
c) skaliranje
Kod skaliranja fotografija za određeni faktor nakon računanja novih dimenzija slike pikseli iz
orginalne slike se kopiraju na nove pozicije s tim da se npr za faktor 2 preskače svaki drugi
redak i stupac te se tako postiže efekt smanjivanja.
for(int i = 0; i < red; i++)
for(int j = 0; j < stupac; j++)
mirr.grey[i][j] = izvorna.grey[i * faktor][j * faktor];
10
po završetku izvođenja slike se spremaju u .pgm fileove i zauzeta memorija se oslobađa.
11
3.2 Paralelni kod po c11 standardu
C11 standard je je trenutni standard C programskog jezika. Uključuje detaljan memorijski
model za bolju podršku pri izvedbi programa upotrebom više dretvi. Funkcija za rotaciju po
C11 standardu malo se razlikuje od sekvencijalnog koda. Korištenjem podatkovne
dekompozicije promjena se dešava pri pozivu funkcije gdje sliku dijelimo na onoliko dijelova
koliko imamo dretvi.
a) rotacija
Funkcija za rotaciju fotografija u C11 standardu zapravo se ne razlikuje previše od
sekvencijalne verzije, u isječku koda ispod možemo vidjeti njen poziv na dretvi sa indexom 0.
if (i == 0)
{
poc = 0;
kraj = okvirna.y / 4;
t[i] = std::thread(rotiraj, kut, poc, kraj, okvirna, rotirana);
}
Glavna razlika je u tome što pri pozivu funkciji prosljeđujemo samo prvu četvrtinu slike te
ova dretva obavlja samo rotaciju samo ove četvrtine. Važno je napomenuti da iako rotaciju
obavljamo samo na četvrtini slike, nove pozicije piksela se računaju na osnovu pozicija
piksela kompletne slike a ne samo te četvrtine.
for (int r = poc; r < kraj; r++)
{
for (int c = 0; c < stupac; c++)
{
r1 = (int)(r0 + ((r - r0) * cos(rads)) - ((c - c0) * sin(rads)));
12
c1 = (int)(c0 + ((r - r0) * sin(rads)) + ((c - c0) * cos(rads)));
if (r1 > 0 && r1 < red && c1 > 0 && c1 < stupac)
rotirana.grey[r1][c1] = okvirna.grey[r][c];
}
}
b) zrcaljenje
Funkcija za zrcaljenje fotografija upotrebom C11 standarda
void mirror(bool flag, int poc, int kraj, PGMImage &izvorna, PGMImage &mirr);
zrcali sliku vertikalno ili horizontalno na osnovu unosa korisnika te je skoro identična funkciji
za zrcaljenje kod sekvencijalne izvedbe. Glavna razlika je u parametrima jer da bi se
realizirala podatkovna dekompozicija pri pozivu funkcije prosljeđuje joj se samo četvrtina
slike na kojoj će radit dretva u kojoj je funkcija pozvana.
c) skaliranje
Pokretanjem programa na četiri dretve i korištenjem podatkovne dekompozicije može se
znatno ubrzati izvođenje programa. Umjesto pokretanja funkcije na cijeloj fotografiji svaka
dretva pokreće funkciju na jednoj četvrtini fotografije što se može vidjeti na isječku koda za
smanjivanje fotografije.
for (int i = 0; i<num_threads; i++){
if (i == 0){
poc = 0;
kraj = izvorna.y / 4
t[i] = thread(smanji, faktor, poc, kraj, izvorna, mirr);
13
}
if (i == 1){
poc = izvorna.y / 4;
kraj = izvorna.y / 2;
t[i] = thread(smanji, faktor, poc, kraj, izvorna, mirr);
}
if (i == 2){
poc = izvorna.y / 2;
kraj = izvorna.y * (3 / 4.);
t[i] = thread(smanji, faktor, poc, kraj, izvorna, mirr);
}
if (i == 3){
poc = izvorna.y * (3 / 4.);
kraj = izvorna.y;
t[i] = thread(smanji, faktor, poc, kraj, izvorna, mirr);
}
}
Nakon završetka izvođenja dreve se ponovo spajaju u jednu te se program završava.
14
3.3 Paralelna izvedba upotrebom cuda programskog modela
GPU je moderna računalna grafička tehnologija kod koje grafički procesor preuzima zadatke
visokog paralelizma i tako smanjuje opterećenje CPU-a. Zadatke pogodne za izvedbu na GPU
karakterizira visoki paralelizam, niska ovisnost između različitih elemenata i numerički
karakter s minimalnim grananjem a zbog velikog broja ponavljanja istih operacija nad
različitim pikselima obrada fotografija je baš takav zadatak. CUDA (Compute Unified Device
Arhitecture), je najpoznatiji programski model i arhitektura za visokoparalelne procesore.
Kod izrade paralelnih programa korištenjem CUDA-e, razlikujemo dvije vrste programskog
koda: kod koji se izvršava na centralnom procesoru , te kod koji se izvršava na grafičkom
procesoru. Osnovna ideja kod dizajniranja sustava je dio posla koji se mora sekvencijalno
izvršavati prilagoditi za izvođenje na centralnom procesoru, a dio posla koji uključuje
izvršavanje aritmetičkih operacija nad velikom količinom podataka istovremeno, prilagoditi
za izvođenje na grafičkom procesoru.
Izvođenje na CPU odnosno hostu sastoji se od učitavanja fotografije za obradu te kopiranja
podataka sa hosta na device. Samo kopiranje je jednostavno i intuitivno te se provodi
ugrađenom funkcijom cudaMemcpy.
cudaMemcpy(d_izvorna.grey, h_izvorna.grey, size, cudaMemcpyHostToDevice);
akon kopiranja podataka poziva se kernel pri čijem pozivu definiramo veličinu i broj blokova
dretvi za obradu podataka
rotirajKernel<<<numBlock,blkSize>>>(theta, d_okvirna, d_rotiraj);
Nakon poziva kernela posao se vrši na device-u odnosno GPU. Glavna razlika između kernela
i prije navedenih funkcija je to da ulogu petlje u CUDI preuzimaju dretve te svaka od njih vrši
zadanu operaciju nad jednim pikselom slike.
a) rotacija
Pri rotaciji fotografija korištenjem CUDA programskog modela posebnu pažnju trebalo je
posvetiti na postavljanje blokada, odnosno sinkronizaciju dretvi koja osigurava da se podatci
ne čitaju prije upisivanja. Kao što je prije spomenuto ulogu petlje preuzimaju dretve te je u
kodu samo potrebno provjeriti da ne dolazi do čitanja podataka kojima nemamo pristup što je
odrađeno jednostavnim uvjetima.
15
__syncthreads();
if(row<d_izvorna.y && col<d_izvorna.x&&row>0&&col>0){
int r1 = ceil (r0 + ((row - r0) * cos(rads)) - ((col - c0) * sin(rads)));
int c1 = ceil (c0 + ((row - r0) * sin(rads)) + ((col - c0) * cos(rads)));
__syncthreads();
if (r1<d_izvorna.y&&r1>0&&c1<d_izvorna.x&&c1>0){
d_rotiraj[r1*num+c1] = d_izvorna.grey[row*num+col];
b) zrcaljenje
Za razliku od rotacije kod zrcaljenja fotografije sinkronizacija dretvi nije potrabna pošto nove
pozicije piksela ovise samo o dretvi koja radi s tim pikselom a ne o drugim dretvama.
__global__ void mirrorKernel(Image1D d_izvorna,float *d_mirror){
int row=blockIdx.y*blockDim.y+threadIdx.y;
int col=blockIdx.x*blockDim.x+threadIdx.x;
int num=d_izvorna.x;
int m=row*num+col, k= (d_izvorna.y - (row+1)) * num + col;
if(col<d_izvorna.x && row<d_izvorna.y&&col>0&&row>0)
d_mirror[k]=d_izvorna.grey[m];
}
c) skaliranje
U kernelu za smanjivanje fotografija kao i kod zrcaljenja nije potrebno raditi sinkronizaciju te
se pomoću uvjeta provjerava jeli potrebno brisati piksel na čijoj se poziciji nalazimo.
if(row<d_izvorna.y&&col<d_izvorna.x)
16
if(col%faktor==0 && row%faktor==0)
{
int k=row/faktor*num/faktor+col/faktor;
d_smanji[k]=d_izvorna.grey[m];
}
Na primjer za faktor 2 to je svaki drugi redak i stupac, dok bi za faktor 3 to bio svaki treći
redak i stupac i tako dalje.
4. USPOREDBA BRZINE IZVOĐENJA PROGRAMA
U ovom dijelu će mo napraviti usporedbu performansi kodova za rotaciju, zrcaljenje i
skaliranje fotografije lena.pgm već spomenutih dimenzija 2400x3000 px. Sekvencijalna i
paralelna C11 implementacija transformacija su testirane na procesoru Intel Core i7
frekvencije 2.4 GHz sa osam jezgri. CUDA izvedba je testirana na Nvidia Geforce 740
GDDR5 grafičkoj kartici preko testnog servera. Veličina memorije na kartici je 1024 Mb
GDDR5 sa 1072 MHz frekvencijom. Grafička kartica sastoji se od 384 jezgre, sa baznim
taktom od 993 Mhz.
SEKVENCIJALNO C11 CUDA
rotacija 9,21709 s 5,566576 s 0,579
skaliranje 0,666859 s 0,34922 s 0,500
zrcaljenje 2,472057 s 2,337600 s 0,569
Tablica 1. Brzine izvođenja
17
rotacija skaliranje zrcaljenje0
1
2
3
4
5
6
7
8
9
10
SekvencijalnoC11CUDA
Graf 1. Brzine izvođenja
Na grafu 1 i tablici 1 prikazana su mjerenja vremena izvedbe. Može se vidjeti da je kod
rotacije koja je procesorski najzahtjevnija transformacija, izvođenje na grafičkom procesoru je
deset puta kraće od izvođenja koda na četiri dretve napravljeno napravljeno upotrebom C11
standarda i čak dvadeset puta brže od sekvencijalne izvedbe.
Kod zrcaljenja fotografija paralelna izvedba na četiri dretve zapravo ne skraćuje pretjerano
vrijeme izvedbe u odnosu na sekvencijalnu izvdbu dok je kod CUDA verzije vrijeme
izvođenja oko pet puta kraće u odnosu na prethodne.
18
5. ZALJUČAK
U današnjem svijetu programeri se konstantno suočavaju sa zahtjevima za poboljšanjem
performansi i bržim rješavanjem problema. Korištenjem jezika visoke razine CUDA
omogućuje paralelno programiranje bez potrebe za prevelikim prilagođavanjem koda. GPU
programiranje danas je moguće jer su današnji grafički procesori u mogućnosti odraditi
mnogo više od iscrtavanja grafike.
Zahvaljujući teraflopima floating point performansi svoju primjenu nalaze u svemu od
financija do medicine. Naš primjer geometrijskih transformacija samo je mali dio CUDA eko
sustava koji svakodnevno raste zahvaljujući tvrtkama koje stvaraju vrhunske alate, usluge i
rješenja te pretvraju GPU računarstvo u računarstvo budućnosti.
19
6. LITERATURA
www.gpucomputing.net/sites/default/files/papers/5207/AMR.216.708.pdf
www.kky.zcu.cz/en/publications/1/SudhakarSah_2012_GPUAcceleratedReal.pdf
www.nvidia.com/object/cuda_home_new
CUDA by Example: An Introduction to General-purpose GPU Programming
20
DODATAK
Sekvencijalno:
#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include "loading.h"#include "mjerenje.h"#include <math.h>#include <thread>using namespace std;
#define BLACK 0 //definiramo konstante 0 - crna boja#define WHITE 255 // 255 je bijela boja - korisno kod funkcije praznaSlika()
void rotiraj(int theta, PGMImage &izvorna, PGMImage &mirr);void smanji(int faktor, PGMImage &izvorna, PGMImage &resize);void mirror(bool flag, PGMImage &izvorna, PGMImage &mirr); //dodan parametar koji će vratiti mirror sliku
void praznaSlika(PGMImage &empty, int rowN, int colN, int color);void pasteSlika(PGMImage &empty, PGMImage &izvorna);//pomoćna funkcija koja stvara sliku određene boje dimenzije rowN * colN//služi da spasite frame kod smanjivanja slike i kod proizvoljnog rotiranja
int main(){
PGMImage izvorna, mirr, resize, rotirana, okvirna; //struktura u koju ucitavamo sliku
resize.grey = NULL; //ako želimo prenijeti neinicijaliziranu strukturu u funkciju moramo dodati NULL
char filename[1024] = "C:\Users\Amy\Documents\Visual Studio 2012\Projects\sek\sek\Lena.pgm";
ucitajPGM("Lena.pgm", &izvorna); //funkcija alocira memoriju i èita sliku fpmoz01.pgm u strukturu izvorna
mirr.x = izvorna.x;mirr.y = izvorna.y;alloc_matrix(&mirr.grey, mirr.y, mirr.x); //alociramo memoriju za mirror
sliku
int dim = ceil(sqrt(izvorna.y*izvorna.y + izvorna.x*izvorna.x));//diagonala izvorne slike
int faktor = 2;
praznaSlika(okvirna, dim, dim, BLACK);//stvaramo praznu okvirnu sliku s dimenzijama diagonale izvorne i alociramo prostor za nju
praznaSlika(rotirana, dim, dim, BLACK);//stvaramo praznu okvirnu sliku s dimenzijama diagonale izvorne i alociramo prostor za nju
praznaSlika(resize, izvorna.y / faktor, izvorna.x / faktor, BLACK);
pasteSlika(okvirna, izvorna);//lijepi piksele iz izvorne slike na sredinu okvirne
//printf("Pritisnite 1 za rotiranje slike, 2 za umanjenje slike, 3 za zrcaljenje slike ili \nneku drugu tipku za izlaz: ");
int c, kut = 75, z;
21
bool provjera = false;//std::cin >> c;
double wall0 = get_wall_time();//Početak mjerenja
//rotiraj(kut, okvirna, rotirana);//zapisiPGM("rotirana.pgm", &rotirana); //funkcija zapisuje
piksele iz strukture mirror u novu sliku fpmoz02.pgm
smanji(faktor, izvorna, resize);zapisiPGM("resize.pgm", &resize); //funkcija zapisuje
piksele iz strukture mirror u novu sliku fpmoz02.pgm
//mirror(provjera, izvorna, mirr);//zapisiPGM("mirror.pgm", &mirr); //funkcija zapisuje
piksele iz strukture mirror u novu sliku fpmoz02.pgm
double wall1 = get_wall_time();//Završetak mjerenja
//funkcija dealocira memoriju format(matrica, broj redaka ,broj stupaca)disalloc_matrix(izvorna.grey, izvorna.y, izvorna.x);disalloc_matrix(okvirna.grey, okvirna.y, okvirna.x);disalloc_matrix(rotirana.grey, rotirana.y, rotirana.x);printf("Vrijeme izvodenja Sekvencijalno. %lf s\n", wall1 - wall0);printf("Press any key...");int k;std::cin >> k;
}
void pasteSlika(PGMImage &okvirna, PGMImage &izvorna){int pocx = ((okvirna.x - izvorna.x) / 2);int pocy = ((okvirna.y - izvorna.y) / 2);
for (int j = 0; j < izvorna.x; j++)for (int i = 0; i < izvorna.y; i++)
okvirna.grey[i+pocy][j+pocx] = izvorna.grey[i][j];
}//funkcija formira sliku proizvoljne dimenzije i bojevoid praznaSlika(PGMImage &empty, int rowN, int colN, int color){
alloc_matrix(&empty.grey, rowN, colN);
empty.y = rowN;empty.x = colN;for (int i = 0; i<rowN; i++)for (int j = 0; j<colN; j++)
empty.grey[i][j] = color;
}
void mirror(bool flag, PGMImage &izvorna, PGMImage &mirr)// zrcali sliku na osnovu unosa korisnika{
int red = mirr.y; //y koordinata označava broj redakaint stupac = mirr.x; // x koordinata označava broj stupaca
//printf("%d %d",red,stupac);if (flag == true) //horizontalni mirror{
22
for (int i = 0; i < red; i++){
for (int j = 0; j < stupac; j++)mirr.grey[red - (i + 1)][j] = izvorna.grey[i][j];
}cout << "Broj redaka = " << red << " ili " << mirr.y;
}else //vertikalni mirror{
for (int i = 0; i < red; i++){
for (int j = 0; j < stupac; j++)mirr.grey[i][stupac - (j + 1)] = izvorna.grey[i][j];
}}
}
void smanji(int faktor, PGMImage &izvorna, PGMImage &resize){
int red = izvorna.y;int stupac = izvorna.x;
//dovoljno je iterirati petlje za određeni faktorfor (int i = 0; i < red-1; i=i+faktor){
for (int j = 0; j < stupac-1; j=j+faktor){//resize.grey[i / faktor][j / faktor] = izvorna.grey[i][j];
int k=i/faktor;int m=j/faktor;resize.grey[k][m] = izvorna.grey[i][j];
}}
}
void rotiraj(int theta, PGMImage &okvirna, PGMImage &rotirana)// na osnovu unosa korisnika za kut rotiranja, rotira sliku oko njenog sredista{
int r0, c0; //srediste slikeint r1, c1;int red, stupac;red = okvirna.y;stupac = okvirna.x;
float rads = (theta * 3.14159265) / 180.0;
r0 = red / 2;c0 = stupac / 2;
for (int r = 0; r < red; r++){
for (int c = 0; c < stupac; c++){
r1 = (int)(r0 + ((r - r0) * cos(rads)) - ((c - c0) * sin(rads))); c1 = (int)(c0 + ((r - r0) * sin(rads)) + ((c - c0) * cos(rads)));
if (r1 > 0 && r1 < red && c1 > 0 && c1 < stupac)rotirana.grey[r1][c1] = okvirna.grey[r][c];
}}
23
for (int i = 0; i < red; i++){
for (int j = 0; j < stupac; j++){
if (rotirana.grey[i][j] == 0)rotirana.grey[i][j] = rotirana.grey[i][j + 1];
}}
}
Paralelno C11:
#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <stdio.h>#include "loading.h"#include "mjerenje.h"#include "rotate.h"#include "resize.h"#include "mirror.h"#include <thread>//#include <math.h>
#define BLACK 0#define WHITE 255
void praznaSlika(PGMImage &empty, int rowN, int colN, int color);void pasteSlika(PGMImage &empty, PGMImage &izvorna);int main(){
PGMImage izvorna, mirr, rotirana, okvirna, resize; resize.grey = NULL;char filename[1024] = "C:\Users\Amy\Documents\Visual Studio 2012\
Projects\PAR_11\PAR_11\Lena.pgm";
ucitajPGM("Lena.pgm", &izvorna); mirr.x = izvorna.x;mirr.y = izvorna.y;
int dim = ceil(sqrt(izvorna.y*izvorna.y + izvorna.x*izvorna.x));int faktor = 2;
alloc_matrix(&mirr.grey, mirr.y, mirr.x); praznaSlika(okvirna, dim, dim, BLACK);praznaSlika(rotirana, dim, dim, BLACK);praznaSlika(resize, izvorna.y / faktor, izvorna.x / faktor, BLACK);
pasteSlika(okvirna, izvorna);int c, kut = 75, z;
bool provjera = false;double wall0 = get_wall_time();//Početak mjerenja
rotirajsliku(kut, okvirna, rotirana);zapisiPGM("rotirana.pgm", &rotirana); //smanjisliku(faktor, izvorna, resize);//zapisiPGM("resize.pgm", &resize);
24
//zrcalisliku(provjera, izvorna, mirr);//zapisiPGM("mirror.pgm", &mirr);
double wall1 = get_wall_time();//Završetak mjerenja//funkcija dealocira memoriju format(matrica, broj redaka ,broj stupaca)disalloc_matrix(izvorna.grey, izvorna.y, izvorna.x);disalloc_matrix(resize.grey, resize.y, resize.x);disalloc_matrix(okvirna.grey, okvirna.y, okvirna.x);disalloc_matrix(mirr.grey, mirr.y, mirr.x);disalloc_matrix(rotirana.grey, rotirana.y, rotirana.x);printf("Vrijeme izvodenja paralelno. %lf s\n", wall1 - wall0);printf("Press any key...");int k;std::cin >> k;
}
void pasteSlika(PGMImage &okvirna, PGMImage &izvorna){int pocx = ((okvirna.x - izvorna.x) / 2);int pocy = ((okvirna.y - izvorna.y) / 2);
for (int j = 0; j < izvorna.x; j++)for (int i = 0; i < izvorna.y; i++)
okvirna.grey[i + pocy][j + pocx] = izvorna.grey[i][j];
}void praznaSlika(PGMImage &empty, int rowN, int colN, int color){
alloc_matrix(&empty.grey, rowN, colN);
empty.y = rowN;empty.x = colN;for (int i = 0; i<rowN; i++)for (int j = 0; j<colN; j++)
empty.grey[i][j] = color;
}
Miror.h:
#include <math.h>#include <thread>void mirror(bool flag, int poc, int kraj, PGMImage &izvorna, PGMImage &mirr{
int red = mirr.y; int stupac = mirr.x; if (flag == true) {
for (int i = poc; i < kraj; i++){
for (int j = 0; j < stupac; j++)mirr.grey[red - (i + 1)][j] = izvorna.grey[i][j];
}}else {
for (int i = 0; i < red; i++){
for (int j = poc; j < kraj; j++)mirr.grey[i][stupac - (j + 1)] = izvorna.grey[i][j];
}}
}void zrcalisliku(bool flag, PGMImage izvorna, PGMImage mirr){
25
int poc1, kraj1, poc, kraj;std::thread t[4];for (int i = 0; i < 4; i++){
if (i == 0){
poc = 0;kraj = izvorna.x / 4;poc1 = 0;kraj1 = izvorna.y / 4;if (flag == true)
t[i] = std::thread(mirror, flag, poc1, kraj1, izvorna, mirr);
elset[i] = std::thread(mirror, flag, poc, kraj, izvorna,
mirr);}if (i == 1){
poc = izvorna.x / 4;kraj = izvorna.x / 2;poc1 = izvorna.y / 4;kraj1 = izvorna.y / 2;if (flag == true)
t[i] = std::thread(mirror, flag, poc1, kraj1, izvorna, mirr);
elset[i] = std::thread(mirror, flag, poc, kraj, izvorna,
mirr);}if (i == 2){
poc = izvorna.x / 2;kraj = izvorna.x * (3 / 4.);poc1 = izvorna.y / 2;kraj1 = izvorna.y * (3 / 4.);if (flag == true)
t[i] = std::thread(mirror, flag, poc1, kraj1, izvorna, mirr);
elset[i] = std::thread(mirror, flag, poc, kraj, izvorna,
mirr);}if (i == 3){
poc = izvorna.x * (3 / 4.);kraj = izvorna.x;poc1 = izvorna.y * (3 / 4.);kraj1 = izvorna.y;if (flag == true)
t[i] = std::thread(mirror, flag, poc1, kraj1, izvorna, mirr);
elset[i] = std::thread(mirror, flag, poc, kraj, izvorna,
mirr);}
}
for (int i = 0; i < 4; i++)t[i].join();
}
Resize.h:
26
#include <math.h>#include <thread>void smanji(int faktor, int poc, int kraj, PGMImage &izvorna, PGMImage &resize){
int red = izvorna.y;int stupac = izvorna.x;
for (int i = poc; i < kraj - 1; i = i + faktor){
for (int j = 0; j < stupac - 1; j = j + faktor){
int k = i / faktor;int m = j / faktor;resize.grey[k][m] = izvorna.grey[i][j];
}}
}void smanjisliku(int faktor, PGMImage izvorna, PGMImage resize){
int poc1, kraj1;std::thread t[4];for (int i = 0; i < 4; i++){
if (i == 0){
poc1 = 0;kraj1 = izvorna.y / 4;t[i] = std::thread(smanji, faktor, poc1, kraj1, izvorna,
resize);}if (i == 1){
poc1 = izvorna.y / 4;kraj1 = izvorna.y / 2;t[i] = std::thread(smanji, faktor, poc1, kraj1, izvorna,
resize);}if (i == 2){
poc1 = izvorna.y / 2;kraj1 = izvorna.y * (3 / 4.);t[i] = std::thread(smanji, faktor, poc1, kraj1, izvorna,
resize);}if (i == 3){
poc1 = izvorna.y * (3 / 4.);kraj1 = izvorna.y;t[i] = std::thread(smanji, faktor, poc1, kraj1, izvorna,
resize);}
}
for (int i = 0; i < 4; i++)t[i].join();
}
Rotate.h:
#include <math.h>#include <thread>void rotiraj(int theta, int poc, int kraj, PGMImage &okvirna, PGMImage
27
&rotirana){
int r0, c0; //srediste slikeint r1, c1;int red, stupac;red = okvirna.y;stupac = okvirna.x;
float rads = (theta * 3.14159265) / 180.0;
r0 = red / 2;c0 = stupac / 2;
for (int r = poc; r < kraj; r++){
for (int c = 0; c < stupac; c++){
r1 = (int)(r0 + ((r - r0) * cos(rads)) - ((c - c0) * sin(rads)));
c1 = (int)(c0 + ((r - r0) * sin(rads)) + ((c - c0) * cos(rads)));
if (r1 > 0 && r1 < red && c1 > 0 && c1 < stupac)rotirana.grey[r1][c1] = okvirna.grey[r][c];
}}for (int i = 0; i < red; i++){
for (int j = 0; j < stupac; j++){
if (rotirana.grey[i][j] == 0)rotirana.grey[i][j] = rotirana.grey[i][j + 1];
}}
}void rotirajsliku(int kut, PGMImage okvirna, PGMImage rotirana){
std::thread t[4];int poc, kraj;for (int i = 0; i < 4; i++){
if (i == 0){
poc = 0;kraj = okvirna.y / 4;t[i] = std::thread(rotiraj, kut, poc, kraj, okvirna,
rotirana);
}if (i == 1){
poc = okvirna.y / 4;kraj = okvirna.y / 2;t[i] = std::thread(rotiraj, kut, poc, kraj, okvirna,
rotirana);}if (i == 2){
poc = okvirna.y / 2;kraj = okvirna.y * (3 / 4.);t[i] = std::thread(rotiraj, kut, poc, kraj, okvirna,
rotirana);}if (i == 3)
28
{poc = okvirna.y * (3 / 4.);kraj = okvirna.y;t[i] = std::thread(rotiraj, kut, poc, kraj, okvirna,
rotirana);}
}
for (int i = 0; i < 4; i++)t[i].join();
}
CUDA:
ZRCALJENJE I SKALIRANJE
#include "loading.h"#include "cuda_runtime.h"#include "device_launch_parameters.h"#include "mjerenje.h"#include <cmath>
const int xSize=32;const int ySize=32;
typedef struct { int x, y;
float *grey; } Image1D;
__global__ void mirrorKernel(Image1D d_izvorna,float *d_mirror, bool flag){
int row=blockIdx.y*blockDim.y+threadIdx.y;int col=blockIdx.x*blockDim.x+threadIdx.x;int num=d_izvorna.x; //broj elemenata u jednom retku
int m=row*num+col;int i = row*num+(d_izvorna.x-(col+1)); //nove lokacije piksela
za vertikalni mirrorint k= (d_izvorna.y - (row+1)) * num + col; //nove lokacije za
horizontalni
if(col<d_izvorna.x && row<d_izvorna.y&&col>0&&row>0){if (flag) // horizontalni mirror
d_mirror[k]=d_izvorna.grey[m];else //inače vertikalni
d_mirror[i]=d_izvorna.grey[m];}
}
__global__ void smanjiKernel(int faktor, Image1D d_izvorna, float *d_smanji){
int row=blockIdx.y*blockDim.y+threadIdx.y;int col=blockIdx.x*blockDim.x+threadIdx.x;
29
int num=d_izvorna.x;
int m=row*num+col;
if(row<d_izvorna.y&&col<d_izvorna.x){
if(col%faktor==0 && row%faktor==0){
int k=row/faktor*num/faktor+col/faktor;d_smanji[k]=d_izvorna.grey[m];
}}
}
int main(){
PGMImage mirror, izvorna;PGMImage smanji;
Image1D d_izvorna,h_izvorna;float *d_mirror;float *d_smanji;int faktor=2;
float *pom;int theta=45;char file1[1024] ="lena_org.pgm";char file2[1024] ="fpmoz02.pgm";char file3[1024] ="fpmoz03.pgm";
ucitajPGM(file1,&izvorna);
alloc_matrix(&(mirror.grey),izvorna.y,izvorna.x);alloc_matrix(&(smanji.grey),izvorna.y/faktor, izvorna.x/faktor);
mirror.x=izvorna.x;mirror.y=izvorna.y;
smanji.x=izvorna.x/faktor;smanji.y=izvorna.y/faktor;
h_izvorna.grey=(float *)malloc(izvorna.x*izvorna.y*sizeof(float));pom=(float *)malloc(mirror.x*mirror.y*sizeof(float));
d_izvorna.x=izvorna.x;d_izvorna.y=izvorna.y;
h_izvorna.x=izvorna.x;h_izvorna.y=izvorna.y;
for(int i=0;i<izvorna.y;i++)for(int j=0;j<izvorna.x;j++)
h_izvorna.grey[i*izvorna.x+j]=izvorna.grey[i][j];
int size=mirror.x*mirror.y*sizeof(float);
cudaMalloc((void **)& d_izvorna.grey, size);cudaMalloc((void **)& d_mirror,size);cudaMalloc((void **)& d_smanji,size);
30
cudaMemcpy(d_izvorna.grey, h_izvorna.grey, size, cudaMemcpyHostToDevice);
dim3 blkSize(xSize,ySize);dim3 numBlock(ceil((float)izvorna.x/xSize),ceil((float)izvorna.y/ySize));double wall0 = get_wall_time(); //pocetak mjerenja
mirrorKernel<<<numBlock,blkSize>>>(d_izvorna, d_mirror, false);double wall1 = get_wall_time(); //pocetak mjerenjacudaThreadSynchronize();cudaMemcpy(pom, d_mirror, size, cudaMemcpyDeviceToHost);
for(int i=0;i<mirror.y;i++)for(int j=0;j<mirror.x;j++)
mirror.grey[i][j]=pom[i*mirror.x+j];double wall2 = get_wall_time(); //pocetak mjerenja
smanjiKernel<<<numBlock,blkSize>>>(2,d_izvorna,d_smanji);double wall3 = get_wall_time(); //pocetak mjerenja
cudaThreadSynchronize();cudaMemcpy(pom,d_smanji, size, cudaMemcpyDeviceToHost);
for(int i=0;i<smanji.y;i++)for(int j=0;j<smanji.x;j++)
smanji.grey[i][j]=pom[i*smanji.x+j];printf("Vrijeme izvodjenja kernel funkcije mirror iznosi %f \n", (wall1 -
wall0) * 1000);printf("Vrijeme izvodjenja kernel funkcije resize iznosi %f ", (wall3 -
wall2) * 1000);zapisiPGM(file2,&mirror);zapisiPGM(file3,&smanji);
cudaFree(d_izvorna.grey);cudaFree(d_mirror);cudaFree(d_smanji);
free(pom);disalloc_matrix(izvorna.grey,izvorna.y,izvorna.x);disalloc_matrix(smanji.grey,smanji.y,smanji.x);disalloc_matrix(mirror.grey,mirror.y,mirror.x);
printf("Press any key..."); getchar();}
ROTACIJA
#include "loading.h"#include "cuda_runtime.h"#include "device_launch_parameters.h"#include "gputimer.h"#include "mjerenje.h"#include <cmath>
#define BLACK 0 //definiramo konstante 0 - crna boja#define WHITE 255 // 255 je bijela boja - korisno kod funkcije praznaSlika()
const int xSize=32;const int ySize=32;
31
typedef struct { int x, y;
float *grey; } Image1D;
void pasteSlika(PGMImage &okvirna, PGMImage &izvorna){int pocx = ((okvirna.x - izvorna.x) / 2);int pocy = ((okvirna.y - izvorna.y) / 2);
for (int j = 0; j < izvorna.x; j++)for (int i = 0; i < izvorna.y; i++)
okvirna.grey[i+pocy][j+pocx] = izvorna.grey[i][j];
}
void praznaSlika(PGMImage &empty, int rowN, int colN, int color){
alloc_matrix(&empty.grey, rowN, colN);
empty.y = rowN;empty.x = colN;for (int i = 0; i<rowN; i++)for (int j = 0; j<colN; j++)
empty.grey[i][j] = color;
}
__global__ void rotirajKernel(int theta, Image1D d_izvorna, float *d_rotiraj){
int row=blockIdx.y*blockDim.y+threadIdx.y;int col=blockIdx.x*blockDim.x+threadIdx.x;float rads = (theta * 3.14159265)/180.0;int num=d_izvorna.x;int r0=d_izvorna.y/2;int c0=num/2;
__syncthreads();if(row<d_izvorna.y && col<d_izvorna.x&&row>0&&col>0){
int r1 = ceil (r0 + ((row - r0) * cos(rads)) - ((col - c0) * sin(rads))); //racuna novu poziciju piksela
int c1 = ceil (c0 + ((row - r0) * sin(rads)) + ((col - c0) * cos(rads)));
__syncthreads();if (r1<d_izvorna.y&&r1>0&&c1<d_izvorna.x&&c1>0){
d_rotiraj[r1*num+c1] = d_izvorna.grey[row*num+col];
__syncthreads();if(d_rotiraj[row*num+col] == 0){
float temp = d_rotiraj[row*num+col+1];__syncthreads();d_rotiraj[row*num+col] = temp;}
} }
}
32
int main(){
PGMImage izvorna, rotirana;PGMImage okvirna;
Image1D d_izvorna,h_izvorna;float *d_rotiraj;float *pom;int theta=90;char file1[1024] ="lena_org.pgm";char file2[1024] ="fpmoz02a.pgm";ucitajPGM(file1,&izvorna);
int dim = ceil(sqrt(izvorna.y*izvorna.y + izvorna.x*izvorna.x));//diagonala izvorne slike
praznaSlika(okvirna, dim, dim, BLACK);praznaSlika(rotirana, dim, dim, BLACK);//dvije praznw slike za rotaciju
pasteSlika(okvirna, izvorna);//lijepi piksele iz izvorne slike na sredinu okvirne
h_izvorna.grey=(float *)malloc(dim*dim*sizeof(float));pom=(float *)malloc(dim*dim*sizeof(float));
d_izvorna.x=dim;d_izvorna.y=dim;
h_izvorna.x=dim;h_izvorna.y=dim;
//kopira piksele iz 2D okvirne u 1D izvornufor(int i=0;i<dim;i++)
for(int j=0;j<dim;j++)h_izvorna.grey[i*dim+j]=okvirna.grey[i][j];
int size=dim*dim*sizeof(float);
cudaMalloc((void **)& d_izvorna.grey, size);cudaMalloc((void **)& d_rotiraj,size);
cudaMemcpy(d_izvorna.grey, h_izvorna.grey, size, cudaMemcpyHostToDevice);
dim3 blkSize(xSize,ySize);dim3 numBlock(ceil((float)dim/xSize),ceil((float)dim/ySize));
double wall0 = get_wall_time(); //pocetak mjerenjarotirajKernel<<<numBlock,blkSize>>>(theta, d_izvorna, d_rotiraj);double wall1 = get_wall_time(); //pocetak mjerenjacudaThreadSynchronize();cudaMemcpy(pom, d_rotiraj, size, cudaMemcpyDeviceToHost);
for(int i=0;i<rotirana.y;i++)for(int j=0;j<rotirana.x;j++)
rotirana.grey[i][j]=pom[i*dim+j];
33
zapisiPGM(file2,&rotirana);cudaFree(d_izvorna.grey);cudaFree(d_rotiraj);
printf("Vrijeme izvodjenja kernel funkcije rotiraj iznosi %f \n", (wall1 - wall0) * 1000);
free(pom);disalloc_matrix(izvorna.grey,izvorna.y,izvorna.x);disalloc_matrix(rotirana.grey,rotirana.y,rotirana.x);disalloc_matrix(okvirna.grey,okvirna.y,okvirna.x);
printf("Press any key..."); getchar();}
34