You are on page 1of 34

Sveuilite u Mostaru

Fakultet Prirodoslovno-Matematikih i odgojnih znanosti


Odjel za Informatiku

PROJEKTNI ZADATAK

GEOMETRIJSKE TRANSFORMACIJE NA
SLIKAMA
ROTACIJA, ZRCALJENJE I SKALIRANJE

MENTOR:
Dr.sc Sven Gotovac

STUDENTI:
Ivan Kasalo 7647
Ana Vila 7649/I
Branimir Boras 6860

Mostar, 2015

SADRAJ
1.

UVOD.................................................................................................................................1

2.

LINEARNE GEOMETRIJSKE TRANSFORMACIJE NA SLIKAMA............................2

3.

2.1

Zrcaljenje......................................................................................................................3

2.2

Rotacija.........................................................................................................................4

2.3

Skaliranje......................................................................................................................5

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 IZVOENJA PROGRAMA.....................................................15

5.

ZALJUAK......................................................................................................................17

6.

LITERATURA..................................................................................................................18

1. UVOD

Obrada fotografija je oblik obrade signala gdje se kao ulazni podatak koristi
fotografija a izlaz moe biti nova slika ili bilo koja druga vrsta relevantnih podataka. Rotirati
sliku za odreeni broj stupnjeva ili joj pak smanjiti veliinu za odreeni faktor su primjeri
uobiajene obrade fotografije.
U veini sluajeva obrada se vri na cijeloj fotografijji i iste operacije se izvravaju
nad svakim pikselom to zapravo znai mnogo ponavljanja istog posla. Napredovanjem
tehnologije fotografije postaju kvalitetnije to dovodi do veih 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 bra nego na nego slina implementacija
na CPU.
U ovom radu obraena su tri esto koritene transformacije digitalnih fotografija:
rotacija, zrcaljenje i smanjivanje. Transformacije su odraene koritenjem tri verzije koda za
transformaciju: klasini sekvencijalni C/C++ kod, C/C++ kod sa C11 standardom i upotrebom
dretvi te CUDA C kod za izvedbu na masivno paralelnim procesorima.

2. LINEARNE GEOMETRIJSKE TRANSFORMACIJE NA


SLIKAMA

Geometrijske transformacije su esto koriten postupak za registraciju slika i


uklanjanje geometrijskih distorzija na slikama. este primjene ukljuuju stvaranje mozaika i
geografsko mapiranje a jedan od najee koritenih procesa su linearne geometrijske
transformacije.
Baza geometrijskih transformacija je mapiranje jednog koordinatnog sustava na drugi.
Ovo mapiranje se definira pomou prostornih transformacija funkcije mapiranja koja
uspostavlja linearnu prostornu vezu izmeu svih toaka ulazne i izlazne fotografije. Za zadanu
prostornu transformaciju svaka toka u izlaznoj slici zauzima vrijednost odgovarajue toke
ulazne fotografije. Veza izmeu koordinata ulazne i izlazne slike pronalazi se primjenom
odgovarajue funkcije prostorne transformacije.
Prema tome geometrijska transformacija je funkcija koja preslikava orginalnu toku ili
vektor u njenu sliku, znai da se koordinate piksela orginalne slike A (x,y) mapiraju u toku
(u,v) novog kordinatnog sustava gdje su u i v funkcije koordinata toke (x,y): u=f1(x,y),
v=f1(x,y).
Ovisno o primjeni, funkcije linearne transformacije mogu poprimiti vie razliitih
oblika a one najjednostavnije su odreene analitikim izrazima ukljuujui afine,
polinominalne i projektivne transformacije.

slika 1. mapiranje iz toke (x,y) u (u,v)

Niz digitalne slike ima implicitnu mreu koja se mapira u toke nove domene. Na slici
1. moemo vidjeti da ove toke ne moraju pasti u mreu toaka nove domene. U neprekidnoj
domeni geometrijska transformacija je u potpunosti odreena 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 lee na mrei koja je u osnovi reetka integera. Problem se javlja jer se izlazna mrea
za razliku od ulazne ne mora nuno poklapati sa reetkom, upravo suprotno pozicije izlazne
mree mogu biti bilo koja vrijednost koja je rezultat funkcije mapiranja.
Poto je diskretni ulaz definiran samo vrijednostima integera provodi se interpolacija
da bi se neprekinuta povrina uklopila u uzorke. Popularne interpolacijske funkcije su kubna
bilinearna i funkcija najblieg susjeda no u ovom radu radi preglednosti i tonijeg mjerenja
interpolacija nije koritena.

2.1 Zrcaljenje
Zrcaljene slike je operacija rotiranja slike oko neke osi odnosno mjenjanje njenog
horizontalnog ili vertikalnog smjera. Refleksija odnosno zrcaljenje moe se vriti oko toke ili
osi slike. U sluaju zrcaljenja oko osi najee koritene 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 vre prebacivanje svakog piksela izvorne slike na suprotnu stranu gdje pri

vertikalnom zrcaljenju horizontalna koordinata ostaje jednaka a pri horizontalnom ne mjenja


se vertikalna koordinata.
Najee se koristi radi vizualnog efekta i pri analizi simetrinosti ali svoje primjene nalazi i
kao operator pri zahtjevnijim operacijama. Primjer vertikalnog zrcaljenja oko osi moe se
vidjeti na slici 2.

Slika 2. Orginal lena i nakon vertikalnog zrcaljenja

2.2 Rotacija
Rotacija fotografija je uobiajen proces kod digitalne obrade slika. Poto sliku rotiramo oko
sredita za zadani kut za raunanje novih pozicija piksela koritene 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 sredita slike.

Na slici ispod moemo vidjeti sliku lena rotiranu za 45 stupnjeva oko njenog sredita.

Slika 3. Lena rotirana za 45 stupnjeva

Rotacija je znai funkcija definirana kutom rotacije i sreditem rotacije te iako korisna sama
po sebi esto se koristi u poetnim 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 odreeni kut prije primjene
operatora za detekciju rubova moe 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 moe poveavati ili smanjivati irina ili visina slika, pa time
moemo dobiti razne efekte izduivanja ili zbijanja slika, ili pak smanjenje ili poveanje
canvas slika ako i visinu i irinu slika smanjujemo odnosno poveavamo proporcionalno.
Smanjivanje fotografija odnosno subsampling provodi se mijenjanjem vrijednosti grupe

piksela jednom vrijednosti iz te grupe ili interpolacijom vrijednosti svih susjednih piksela.

Slika 4. subsampling metodom odabira piksela i interpolacijom

Na slici 4. moemo vidjeti dvije navedene metode za skaliranje fotografija. Dok je


interpolacija susjednih piksela tonija i vodi do kvalitetnijih izlaznih fotografija, odabir
piksela koji e predstavljati svoje okruenje je manje zahtjevna u pogledu izrauna te je
samim tim i bra.
U nastavku rada obraene 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 izvoenja te rezultati prikazani u tablici

3. IMPLEMENTACIJA ALGORITAMA ZA ROTACIJU,


SKALIRANJE I ZRCALJENJE
Sa tisuama piksela kojima treba pratiti pozicije obrada digitalnih fotografija moe se
initi kao zahtjevan zadatak ali nakon kraeg razmatranja moemo vidjeti da je svaka
fotografija zapravo jednostavna i dobro poznata struktura podataka. Za poetak uzmimo

jednostavnu fotografiju u sivim tonovima. Svaki piksel ovakve fotografije ima jednu
vrijednost a to je siva. Ako razmotrimo piksele kao obine brojeve a ne tonove boje dobijemo
2D niz koji je spremljen u .txt datoteku pa je sve to je potrebno uraditi zapravo uitati taj niz
i obaviti neke osnovne operacije nad njim.
Kada se slika pogleda kao dvodimenzionalni niz odnosno matrica izvravanje 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 koritena je slika lena.pgm dimenzija 2400x3000 px u


sivim tonovima.

3.1 Sekvencijalni kod


U sekvencijalnom kodu nakon uitavanja 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);

void smanji(int faktor, PGMImage &izvorna, PGMImage &resize);


void mirror (bool flag, PGMImage &izvorna,PGMImage &mirr);

a) rotacija
Funkcija rotiraj kroz dvije petlje pomou ve spomenute formule odreuje 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 pokree 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];

b) zrcaljanje
Kod zrcaljenja fotografije na osnovu izbora korisnika vri se vertikalni ili horizontalni mirror.
Kod horizontalnog mirrora vertikalne pozicije piksela se mjenjaju u odnosu na sredinju
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 vri 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 odreeni faktor nakon raunanja novih dimenzija slike pikseli iz
orginalne slike se kopiraju na nove pozicije s tim da se npr za faktor 2 preskae svaki drugi
redak i stupac te se tako postie 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];

po zavretku izvoenja slike se spremaju u .pgm fileove i zauzeta memorija se oslobaa.

3.2 Paralelni kod po c11 standardu


C11 standard je je trenutni standard C programskog jezika. Ukljuuje detaljan memorijski
model za bolju podrku pri izvedbi programa upotrebom vie dretvi. Funkcija za rotaciju po
C11 standardu malo se razlikuje od sekvencijalnog koda. Koritenjem podatkovne
dekompozicije promjena se deava 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 previe od
sekvencijalne verzije, u isjeku koda ispod moemo 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 prosljeujemo samo prvu etvrtinu slike te
ova dretva obavlja samo rotaciju samo ove etvrtine. Vano je napomenuti da iako rotaciju
obavljamo samo na etvrtini slike, nove pozicije piksela se raunaju 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)));

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 identina funkciji
za zrcaljenje kod sekvencijalne izvedbe. Glavna razlika je u parametrima jer da bi se
realizirala podatkovna dekompozicija pri pozivu funkcije prosljeuje joj se samo etvrtina
slike na kojoj e radit dretva u kojoj je funkcija pozvana.

c) skaliranje
Pokretanjem programa na etiri dretve i koritenjem podatkovne dekompozicije moe se
znatno ubrzati izvoenje programa. Umjesto pokretanja funkcije na cijeloj fotografiji svaka
dretva pokree funkciju na jednoj etvrtini fotografije to se moe vidjeti na isjeku 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);

}
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 zavretka izvoenja dreve se ponovo spajaju u jednu te se program zavrava.

3.3 Paralelna izvedba upotrebom cuda programskog modela


GPU je moderna raunalna grafika tehnologija kod koje grafiki procesor preuzima zadatke
visokog paralelizma i tako smanjuje optereenje CPU-a. Zadatke pogodne za izvedbu na GPU
karakterizira visoki paralelizam, niska ovisnost izmeu razliitih elemenata i numeriki
karakter s minimalnim grananjem a zbog velikog broja ponavljanja istih operacija nad
razliitim 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 koritenjem CUDA-e, razlikujemo dvije vrste programskog
koda: kod koji se izvrava na centralnom procesoru , te kod koji se izvrava na grafikom
procesoru. Osnovna ideja kod dizajniranja sustava je dio posla koji se mora sekvencijalno
izvravati prilagoditi za izvoenje na centralnom procesoru, a dio posla koji ukljuuje
izvravanje aritmetikih operacija nad velikom koliinom podataka istovremeno, prilagoditi
za izvoenje na grafikom procesoru.
Izvoenje na CPU odnosno hostu sastoji se od uitavanja fotografije za obradu te kopiranja
podataka sa hosta na device. Samo kopiranje je jednostavno i intuitivno te se provodi
ugraenom funkcijom cudaMemcpy.
cudaMemcpy(d_izvorna.grey, h_izvorna.grey, size, cudaMemcpyHostToDevice);
akon kopiranja podataka poziva se kernel pri ijem pozivu definiramo veliinu i broj blokova
dretvi za obradu podataka
rotirajKernel<<<numBlock,blkSize>>>(theta, d_okvirna, d_rotiraj);
Nakon poziva kernela posao se vri na device-u odnosno GPU. Glavna razlika izmeu kernela
i prije navedenih funkcija je to da ulogu petlje u CUDI preuzimaju dretve te svaka od njih vri
zadanu operaciju nad jednim pikselom slike.
a) rotacija
Pri rotaciji fotografija koritenjem CUDA programskog modela posebnu panju 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
odraeno jednostavnim uvjetima.

__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 poto 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 pomou uvjeta provjerava jeli potrebno brisati piksel na ijoj se poziciji nalazimo.

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];
}

Na primjer za faktor 2 to je svaki drugi redak i stupac, dok bi za faktor 3 to bio svaki trei
redak i stupac i tako dalje.

4. USPOREDBA BRZINE IZVOENJA 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 grafikoj kartici preko testnog servera. Veliina memorije na kartici je 1024 Mb
GDDR5 sa 1072 MHz frekvencijom. Grafika kartica sastoji se od 384 jezgre, sa baznim
taktom od 993 Mhz.

SEKVENCIJALNO
rotacija

C11

CUDA

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 izvoenja

10
9
8
7
6
Sekvencijalno

C11

CUDA

3
2
1
0
rotacija

skaliranje

zrcaljenje

Graf 1. Brzine izvoenja

Na grafu 1 i tablici 1 prikazana su mjerenja vremena izvedbe. Moe se vidjeti da je kod


rotacije koja je procesorski najzahtjevnija transformacija, izvoenje na grafikom procesoru je
deset puta krae od izvoenja koda na etiri dretve napravljeno napravljeno upotrebom C11
standarda i ak dvadeset puta bre od sekvencijalne izvedbe.

Kod zrcaljenja fotografija paralelna izvedba na etiri dretve zapravo ne skrauje pretjerano
vrijeme izvedbe u odnosu na sekvencijalnu izvdbu dok je kod CUDA verzije vrijeme
izvoenja oko pet puta krae u odnosu na prethodne.

5. ZALJUAK
U dananjem svijetu programeri se konstantno suoavaju sa zahtjevima za poboljanjem
performansi i brim rjeavanjem problema. Koritenjem jezika visoke razine CUDA
omoguuje paralelno programiranje bez potrebe za prevelikim prilagoavanjem koda. GPU
programiranje danas je mogue jer su dananji grafiki procesori u mogunosti odraditi
mnogo vie od iscrtavanja grafike.
Zahvaljujui 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 zahvaljujui tvrtkama koje stvaraju vrhunske alate, usluge i
rjeenja te pretvraju GPU raunarstvo u raunarstvo budunosti.

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

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);
//pomona funkcija koja stvara sliku odreene boje dimenzije rowN * colN
//slui 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;
bool provjera = false;
//std::cin >> c;
double wall0 = get_wall_time();//Poetak 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();//Zavretak 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 boje
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;
}
void mirror(bool flag, PGMImage &izvorna, PGMImage &mirr)// zrcali sliku na osnovu unosa
korisnika
{
int red = mirr.y; //y koordinata oznaava broj redaka
int stupac = mirr.x; // x koordinata oznaava broj stupaca
//printf("%d %d",red,stupac);
if (flag == true) //horizontalni mirror
{
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 odreeni faktor
for (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 slike
int 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];
}
}
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();//Poetak mjerenja
rotirajsliku(kut, okvirna, rotirana);
zapisiPGM("rotirana.pgm", &rotirana);
//smanjisliku(faktor, izvorna, resize);
//zapisiPGM("resize.pgm", &resize);
//zrcalisliku(provjera, izvorna, mirr);
//zapisiPGM("mirror.pgm", &mirr);
double wall1 = get_wall_time();//Zavretak 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){
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);
else
t[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,
else
t[i] = std::thread(mirror,
}
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,
else
t[i] = std::thread(mirror,
}
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,
else
t[i] = std::thread(mirror,
}

flag, poc1, kraj1, izvorna, mirr);


flag, poc, kraj, izvorna, mirr);

flag, poc1, kraj1, izvorna, mirr);


flag, poc, kraj, izvorna, mirr);

flag, poc1, kraj1, izvorna, mirr);


flag, poc, kraj, izvorna, mirr);

}
for (int i = 0; i < 4; i++)
t[i].join();
}

Resize.h:
#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,
}
if (i == 1)
{
poc1 = izvorna.y / 4;
kraj1 = izvorna.y / 2;
t[i] = std::thread(smanji, faktor, poc1, kraj1,
}
if (i == 2)
{
poc1 = izvorna.y / 2;
kraj1 = izvorna.y * (3 / 4.);
t[i] = std::thread(smanji, faktor, poc1, kraj1,
}
if (i == 3)
{
poc1 = izvorna.y * (3 / 4.);
kraj1 = izvorna.y;
t[i] = std::thread(smanji, faktor, poc1, kraj1,
}
}

izvorna, resize);

izvorna, resize);

izvorna, resize);

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 &rotirana)
{
int r0, c0;
//srediste slike
int 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)
{
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
#include
#include
#include

"cuda_runtime.h"
"device_launch_parameters.h"
"mjerenje.h"
<cmath>

const int xSize=32;


const int ySize=32;
typedef struct {
int x, y;
float *grey;
} Image1D;
__global__
{
int
int
int

void mirrorKernel(Image1D d_izvorna,float *d_mirror, bool flag)


row=blockIdx.y*blockDim.y+threadIdx.y;
col=blockIdx.x*blockDim.x+threadIdx.x;
num=d_izvorna.x; //broj elemenata u jednom retku

int m=row*num+col;
int i = row*num+(d_izvorna.x-(col+1));
mirror

//nove lokacije piksela za vertikalni

int 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 //inae 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;
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);
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 mjerenja
cudaThreadSynchronize();
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
#include
#include
#include
#include
#include

"loading.h"
"cuda_runtime.h"
"device_launch_parameters.h"
"gputimer.h"
"mjerenje.h"
<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;
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;
}
}

}
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 izvornu
for(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 mjerenja
rotirajKernel<<<numBlock,blkSize>>>(theta, d_izvorna, d_rotiraj);
double wall1 = get_wall_time(); //pocetak mjerenja
cudaThreadSynchronize();
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];

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();

You might also like