Editor Si Convertor de Imagini Folosind Limbajul C#
Editor și convertor de imagini folosind limbajul C#
„Învățătura este o comoară care își urmează stăpânul pretutindeni.”
Proverb popular
REZUMATUL PROIECTULUI
Editorul și convertorul de imagini este o aplicație compatibilă cu sistemul de operare Windows, realizată în scopul ajustării defectelor care pot apărea într-o imagine. Motivația care a stat la baza realizării sale o reprezintă importanța imaginilor în momentul actual, într-o perioadă în care mediul online a devenit parte integrantă din viața noastră. Fie că este vorba de rețele de socializare, fie că este vorba de magazine online al căror bun mers se bazează pe prezentarea vizuală a produselor, imaginile sunt de nelipsit, iar calitatea acestora trebuie să fie cât mai bună.
Fiind o aplicație orientată spre interacțiunea cu utilizatorul, are o interfață intuitivă, ușor de utilizat, care nu pune în dificultate utilizatorul.
Editorul și convertorul oferă opțiuni de bază în cadrul prelucrării imaginilor, precum redimensionarea, decuparea, rotirea, reflexia, dar și posibilitatea de a aplica diverse efecte, mai simple sau mai complexe, care să schimbe aspectul imaginii (grayscale, invert, detectare de contururi, substituția unei culori etc.). De asemenea, permite salvarea imaginilor într-un alt format decât cel original.
Procesarea imaginii în vederea obținerii diferitelor efecte se bazează, în cea mai mare parte, pe prelucrarea la nivel de pixel, fiecare pixel fiind prelucrat conform algoritmilor care stau la baza efectului dorit.
Aplicația a fost implementată în limbajul C#, folosind tehnologia Windows Presentation Foundation (WPF) din cadrul platformei .NET, care îmbină crearea interfeței grafice în fișere XAML și descrierea comportamentului acesteia prin intermediul limbajului C#.
Termenii cheie: imagine, editare, WPF, C#, pixel, efect, conversie.
CUPRINS
1 INTRODUCERE
1.1 Scopul
1.2 Motivația
2 unelte Și tehnologii folosite
2.1 Platforma .NET
2.2 Limbajul C#
2.3 C# și .NET
2.4 WPF
2.5 XAML
2.6 Microsoft Visual Studio
3 NOȚIUNI TEORETICE
3.1 Reprezentarea digitală a imaginilor
3.2 Reprezentarea RGB a culorilor
3.3 Reprezentarea RGBA a culorilor
3.4 Formate de imagini
3.4.1 BMP (Bitmap)
3.4.2 JPEG/JPG (Joint Photographic Experts Group)
3.4.3 GIF (Graphics Interchange Format)
3.4.4 PNG (Portable Networks Graphics)
3.4.5 TIFF (Tagged Image File Format)
4 STRUCTURA APLICAȚIEI
4.1 Descriere generală
4.2 Descrierea algoritmilor pentru obținerea efectelor
4.2.1 Grayscale
4.2.2 Invert
4.2.3 Filtrul de culoare
4.2.4 Luminozitatea
4.2.5 Sepia
4.2.6 Contrast
4.2.7 Distorsionare
4.2.8 Efectul de pictură in ulei
4.2.9 Sharpen
4.2.10 Substituția unei culori
4.2.11 Solarizarea imaginii
4.2.12 Detectarea contururilor (Edge Detection)
4.2.12.1 Convoluția
4.2.12.2 Detectarea contururilor folosind Laplacian
4.2.12.3 Detectarea de contururi folosind Laplacian de Gaussian
4.2.12.4 Detectarea de contururi folosind Sobel
4.2.12.5 Detectarea de contururi folosind Prewitt
4.2.12.6 Detectarea contururilor folosind Kirsch
4.2.13 Echilibrul culorilor
4.2.14 Imagine bi-tonala
4.2.15 Estomparea/neclaritatea imaginii (blur)
4.2.15.1 Estompare folosind filtrul de mediere
4.2.15.2 Estompare folosind filtrul Gaussian
4.2.15.3 Estompare folosind filtrul median
4.2.15.4 Estompare cu efect de mișcare
4.2.16 Redimensionarea imaginii
4.2.17 Rotirea și reflexia imaginii
4.3 Salvarea imaginii în alt format/conversia
4.4 Facilități implementate
5 MANUALUL UTILIZATORULUI
6 Concluzii
7 Referințe web
A. Codul sursă
LISTA FIGURILOR
Fig. 1 Relația dintre codul C#, .NET Framework și CLR 5
Fig. 2 Mediul de dezvoltare Microsoft Visual Studio 2010 7
Fig. 3 Modelul RGB de reprezentare a culorilor 9
Fig. 4 Modelul RGBA de reprezentare a culorilor 9
Fig. 5 Structura aplicației 12
Fig. 6 Fereastra Bitonal din cadrul aplicației 13
Fig. 7 Fereastra Color Balance din cadrul aplicației 13
Fig. 8 Fereastra Color Picker din cadrul aplicației 14
Fig. 9 Fereastra Color Substitution din cadrul aplicației 14
Fig. 10 Fereastra Laplacian of Gaussian din cadrul aplicației 15
Fig. 11 Fereastra Laplacian din cadrul aplicației 15
Fig. 12 Fereastra Resize din cadrul aplicației 16
Fig. 13 Fereastra Contrast din cadrul aplicației 16
Fig. 14 Fereastra principală a aplicației 17
Fig. 15 Modul de preluare a pixelilor dintr-un obiect Bitmap 18
Fig. 16 Modul de construire a unui Bitmap pornind de la un tablou de pixeli 19
Fig. 17 Prelucrarea unui pixel pentru obținerea efectului Grayscale 19
Fig. 18 Rezultatul efectului Grayscale 20
Fig. 19 Prelucrarea unui pixel pentru obținerea efectului Invert 20
Fig. 20 Rezultatul efectului Invert 21
Fig. 21 Prelucrarea unui pixel pentru obținerea efectului Color filter 21
Fig. 22 Rezultatul efectului Color filter 22
Fig. 23 Prelucrarea unui pixel pentru obținerea efectului Brightness 23
Fig. 24 Rezultatul efectului Brightness 23
Fig. 25 Prelucrarea unui pixel pentru obținerea efectului Sepia 24
Fig. 26 Rezultatul efectului Sepia 24
Fig. 27 Rezultatul efectului Contrast (pentru valoarea 20 a contrastului) 25
Fig. 28 Rezultatul efectului Distorsionare 26
Fig. 29 Rezultatul efectului Pictură în ulei 27
Fig. 30 Matricea de convoluție pentru Sharpen 28
Fig. 31 Rezultatul efectului Sharpen 28
Fig. 32 Prelucrarea unui pixel pentru substituția unei culori 29
Fig. 33 Rezultatul substituției unei culori 30
Fig. 34 Prelucrarea unui pixel pentru solarizarea imaginii 31
Fig. 35 Rezultatul solarizării imaginii 31
Fig. 36 Matrice Laplaciană 3×3 33
Fig. 37 Matrice Laplaciană 5×5 33
Fig. 38 Edge detection cu Laplacian 3×3 color 33
Fig. 39 Edge detection cu Laplacian 3×3 gri 33
Fig. 41 Edge detection cu Laplacian 5×5 color 34
Fig. 42 Edge detection cu Laplacian 5×5 gri 34
Fig. 43 Edge detection cu L(3×3) of G(3×3) 35
Fig. 43 Edge detection cu L(3×3) of G(5×5) Tip1 35
Fig. 44 Edge detection cu L(3×3) of G(5×5) Tip2 35
Fig. 45 Edge detection cu L(5×5) of G(3×3) 35
Fig. 46 Edge detection cu L(5×5) of G(5×5) Tip1 35
Fig. 47 Edge detection cu L(5×5) of G(5×5) Tip2 35
Fig. 48 Matrice Sobel orizontală 36
Fig. 49 Matrice Sobel verticală 36
Fig. 51 Edge detection cu Sobel color 36
Fig. 52 Edge detection cu Sobel gri 36
Fig. 52 Matrice Prewitt orizontală 37
Fig. 53 Matrice Prewitt verticală 37
Fig. 55 Edge detection cu Prewitt color 37
Fig. 56 Edge detection cu Prewitt gri 37
Fig. 56 Matrice Kirsch orizontală 38
Fig. 57 Matrice Kirsch verticală 38
Fig. 59 Edge detection cu Kirsch color 38
Fig. 60 Edge detection cu Kirsch gri 38
Fig. 60 Prelucrarea unui pixel pentru echilibrul culorilor 39
Fig. 61 Rezultatul aplicării echilibrului culorilor 40
Fig. 62 Prelucrarea unui pixel pentru obtinerea unei imagini bi-tonale 41
Fig. 63 Rezultatul aplicării efectului de imagine bi-tonală 41
Fig. 64 Estompare cu filtru de mediere 3×3 42
Fig. 65 Estompare cu filtru de mediere 5×5 42
Fig. 66 Estompare cu filtru de mediere 7×7 43
Fig. 67 Estompare cu filtru de mediere 9×9 43
Fig. 68 Matrice Gaussiană 3×3 43
Fig. 69 Matrice Gaussiană 5×5 43
Fig. 70 Estompare cu Gaussian 3×3 44
Fig. 71 Estompare cu Gaussian 5×5 44
Fig. 72 Estompare cu filtru median 3×3 45
Fig. 73 Estompare cu filtru median 5×5 45
Fig. 74 Estompare cu filtru median 7×7 45
Fig. 75 Estompare cu filtru median 9×9 45
Fig. 76 Estompare cu filtru median 11×11 45
Fig. 77 Motion Blur 5×5 46
Fig. 78 Motion Blur 5×5 spre stânga 46
Fig. 79 Motion Blur 5×5 spre dreapta 46
Fig. 80 Motion Blur 9×9 46
Fig. 81 Motion Blur 9×9 spre stânga 47
Fig. 82 Motion Blur 9×9 spre dreapta 47
Fig. 83 Redimensionarea imaginii 47
Fig. 84 Rotirea și reflexia imaginii 48
Fig. 85 Salvarea imaginii în alt format 48
INTRODUCERE
Scopul
Problema pe care urmărește să o rezolve proiectul este aceea de a implementa un editor și convertor de imagini cu o interfața ușor de utilizat, care nu necesită ca utilizatorul să aibă cunoștințe tehnice în domeniul editării foto. Editorul oferă posibilitatea de a aplica diverse efecte și filtre, ajustări de imagine, dar și salvarea imaginilor în alte formate (conversie).
Motivația
Ȋn ultimii ani, rețelele de socializare și marketing-ul online au cunoscut o expansiune continuă. Astfel, companiile din întreaga lume încearcă să își construiască și să își consolideze activitatea prin utilizarea beneficiilor aduse de mediul online. Folosirea imaginilor, în special în mediul online, a înregistrat o creștere permanentă, imaginile devenind o parte integrantă a rețelelor de socializare și a mediilor de afaceri.
Indiferent că fac parte dintr-un articol, o pagină web sau o postare pe o rețea de socializare, fotografiile/imaginile, pe lânga beneficiul estetic, pot contribui la o mai bună înțelegere a conținutului, iar, de cele mai multe ori, o imagine sugestivă poate explica mai mult decât orice text. Fotografiile și imaginile folosite în promovare sau în marketing oferă credibilitate și indicii importante despre produsele oferite și pot spori numărul vânzarilor.
Prin urmare, calitatea imaginilor nu poate fi compromisă. În obținerea unui rezultat foarte bun, editarea fotografiilor sau postprocesarea prezintă o mare importanță datorită capacității acesteia de a schimba atât viziunea, cât și unele apecte existente în imagini. Fie că sunt elemente inestetice precum firele de praf, fie că sunt mici greșeli tehnice precum subexpunerea, postprocesarea le poate “fixa”, eliminând elementele deranjante. Astfel, adevăratul potențial al unei fotografii poate fi atins printr-o îmbunătățire a imaginii, utilizand instrumentele de manipulare oferite de un software de editare a imaginilor.
unelte Și tehnologii folosite
Platforma .NET [1]
Platforma .NET este un cadru de dezvoltare software, sub care se pot realiza, distribui și rula aplicațiile de tip consolă, Windows Forms sau Windows Presentation Foundation, aplicații și servicii web.
.NET Framework constituie un nivel de abstractizare între aplicație și nucleulul sistemului de operare (sau alte programe), pentru a asigura portabilitatea codului; de asemenea, integrează tehnologii care au fost lansate de către Microsoft începând cu mijlocul anilor 90 (COM, DCOM, ActiveX, etc.) sau tehnologii actuale (servicii Web, XML).
Grupele de produse ale platformei .NET
1. Unelte de dezvoltare
un set de limbaje (C#, Visual Basic .NET, J#, C++/CLI, JScript.NET, Objective-C, Python, Smalltalk, Eiffel, Perl, Fortran, Cobol, Lisp, Haskell, Pascal, RPG, etc.);
un set de medii de dezvoltare (Visual Studio .NET, Visio);
o bibliotecă vastă de clase pentru crearea serviciilor web (Web Services), aplicațiilor web (Web Forms, ASP.NET MVC) și a aplicațiilor Windows (Windows Forms, Windows Presentation Foundation).
2. Servere specializate – un set de servere Enterprise .NET: SQL Server 2008, Exchange 2007, BizTalk Server 2006, SharePoint, etc, care pun la dispoziție funcționalități diverse pentru stocarea bazelor de date, email, aplicații B2B3.
3. Servicii Web – cel mai notabil exemplu este .NET Passport – un mod prin care utilizatorii se pot autentifica pe site-urile Web vizitate, folosind un singur nume și o parolă pentru toate. Alt exemplu este MapPoint, care permite vizualizarea, editarea și integrarea hărților.
4. Dispozitive – noi dispozitive non–PC, programabile prin .NET Compact Framework, o versiune redusă a lui .NET Framework: Pocket PC Phone Edition, Smartphone, Tablet PC, Smart Display, set-top boxes, etc.
Limbajul C# [2]
C# este un limbaj modern, simplu, orientat pe obiecte, dezvoltat de Microsoft în cadrul platformei .NET, iar apoi acceptat ca standard de către Ecma si de către ISO. C# este unul dintre limbajele create pentru CLI (Common Language Infrastructure). A fost realizat de o echipă de dezvolatatori condusă de Anders Hejlsberb, prima versiune apărând în ianuarie 2002, iar ultima (5.0) în august 2012. Din punct de vedere sintactic și semantic, limbajul C# se aseamănă cu limbajul C++ și Java, fiind din acest motiv relativ ușor de învăț
1. Unelte de dezvoltare
un set de limbaje (C#, Visual Basic .NET, J#, C++/CLI, JScript.NET, Objective-C, Python, Smalltalk, Eiffel, Perl, Fortran, Cobol, Lisp, Haskell, Pascal, RPG, etc.);
un set de medii de dezvoltare (Visual Studio .NET, Visio);
o bibliotecă vastă de clase pentru crearea serviciilor web (Web Services), aplicațiilor web (Web Forms, ASP.NET MVC) și a aplicațiilor Windows (Windows Forms, Windows Presentation Foundation).
2. Servere specializate – un set de servere Enterprise .NET: SQL Server 2008, Exchange 2007, BizTalk Server 2006, SharePoint, etc, care pun la dispoziție funcționalități diverse pentru stocarea bazelor de date, email, aplicații B2B3.
3. Servicii Web – cel mai notabil exemplu este .NET Passport – un mod prin care utilizatorii se pot autentifica pe site-urile Web vizitate, folosind un singur nume și o parolă pentru toate. Alt exemplu este MapPoint, care permite vizualizarea, editarea și integrarea hărților.
4. Dispozitive – noi dispozitive non–PC, programabile prin .NET Compact Framework, o versiune redusă a lui .NET Framework: Pocket PC Phone Edition, Smartphone, Tablet PC, Smart Display, set-top boxes, etc.
Limbajul C# [2]
C# este un limbaj modern, simplu, orientat pe obiecte, dezvoltat de Microsoft în cadrul platformei .NET, iar apoi acceptat ca standard de către Ecma si de către ISO. C# este unul dintre limbajele create pentru CLI (Common Language Infrastructure). A fost realizat de o echipă de dezvolatatori condusă de Anders Hejlsberb, prima versiune apărând în ianuarie 2002, iar ultima (5.0) în august 2012. Din punct de vedere sintactic și semantic, limbajul C# se aseamănă cu limbajul C++ și Java, fiind din acest motiv relativ ușor de învățat.
Principalele caracteristici
Limbajul C# suportă tipul de variabile implicite, a căror declarare se face prin cuvântul cheie var.
Sintaxa limbajului C# simplifică mult complexitatea limbajului C++.
Spre deosebire de Java, oferă caracteristici foarte utile, precum tipuri de date nulabile, enumerări, delegați, acces direct la memorie, expresii lambda.
Fiind un limbaj orientat pe obiecte, suportă încapsularea, moștenirea și polimorfismul.
Permite meta-programarea prin așa-numitele atribute. Multe dintre aceste atribute au rolul de a duplica funcționalitatea directivelor de preprocesare dependente de platformă regăsite în GCC și în Visual C++.
Ca în C++, limbajul C# cere utilizarea cuvântului cheie virtual pentru a suprascrie metode în cadrul subclaselor.
Limbajul conține așa-numitele metode de extensie, care permit scrierea unor metode statice și folosirea lor ca și când acestea ar face parte din tabelul de funcții ale unei clase.
Tipul dynamic asigură posibilitatea de binding la run-time.
Permite utilizarea delegaților (delegate), inspirați de pointerii la funcții din C++, ce permit programarea generică. Reprezintă versiunea sigură a pointerilor către funcții din C++ și sunt mecanismul prin care sunt tratate evenimentele.
Limbajul C# are suport pentru implementarea excluziei mutuale, folosind cuvântul cheie lock.
În cadrul limbajului nu sunt permise folosirea variabilelor sau a funcțiilor globale. Toți membrii și toate funcțiile trebuie declarate în cadrul claselor. Totuși, variabilele și funcțiile globale pot fi înlocuite prin folosirea membrilor statici ai claselor publice.
Un namespace oferă același nivel de izolare a codului precum namespace-urile din C++ și precum pachetele din Java, având reguli și funcționare asemănătoare cu pachetele.
Managementul memoriei este realizat automat de către garbage collector, care rezolvă problema eliberării memoriei, o problemă principală în cazul limbajului C++, unde memoria alocată trebuie dealocată de către programator.
Permite tratarea excepțiilor prin blocuri de tip try..catch, precum și prin blocuri try…finally, care asigură execuția codului aflat în blocul finally, chiar dacă o excepție este sau nu ridicată.
Moștenirea multiplă nu este permisă. O clasă poate moșteni o singură altă clasă, în schimb poate implementa orice număr de interfețe.
C# permite supraîncărcarea operatorilor.
Permite implementarea proprietăților; o proprietate reprezintă un tipar, format dintr-un setter și/sau un getter pentru un singur membru al unei clasei, astfel încat accesul la respectivul membru se poate face prin intermediul proprietății, eliminând necesitatea implementării unor funcții reduntante care ar avea același scop (precum geterii și seterii din Java).
Tipuri de date
C# prezintă două grupuri de tipuri de date:
tipuri valoare
tipuri referință
Tipurile valoare includ tipurile simple (ex. char, int, float), tipurile enumerare și structură și au ca principale caracteristici faptul că acestea conțin direct datele referite și sunt alocate pe stivă sau inline într–o structură.
Tipurile referință includ tipurile clasă, interfață, delegat și tablou, toate având proprietatea că variabilele de acest tip stochează referințe către obiectele conținute.
De remarcat este faptul că toate tipurile de date sunt derivate (direct sau nu) din tipul System.Object, punând astfel la dispoziție un mod unitar de tratare a lor.
Boxing și unboxing
Boxing-ul este operația de conversie de la un tip de date valoare la tipul de date referință corespunzător. Operația de boxing este implicită în limbajul C#.
Unboxing-ul reprezintă operația de conversie de la un tip de date referință la un tip de date valoare. În C#, operația de unboxing necesită o conversie explicită de tip.
Tipuri generice
Posibilitatea de utilizare a tipurilor generice a fost adăugată în versiunea 2.0 a limbajului C#. Tipurile generice folosesc parametri de tip, care oferă posibilitatea de a crea clase și funcții care nu specifică tipul de date folosit până în momentul în care clasa sau metoda este instanțiată.
C# și .NET [3]
Programele C# rulează pe platforma .NET, care include sistemul de execuție virtuală Common Language Runtime (CLR) și un set unificat de librării. Codul sursă scris în limbajul C# este compilat într-un limbaj intermediar. Codul și resursele codului intermediar, precum bitmapuri și șiruri de caractere, sunt stocate pe disc într-un fișier executabil numit assembly, având, de obicei, extensia .exe sau .dll. Atunci când programul C# este executat, assembly-ul este încărcat în CLR, care execută compilarea “just in time” (JIT) pentru a converti codul intermediar în instrucțiuni. CLR oferă și serviciile de tratare a erorilor, de dealocare automată și de gestionare a resurselor.
Fig. 1 Relația dintre codul C#, .NET Framework și CLR
Platforma .NET include și o bibliotecă ce conține peste 4000 de clase organizate în namespace-uri, clase care oferă o varietate imensă de funcționalități utile, de la fișiere de intrare/ieșire până la bitmap-uri, manipularea șirurilor de caractere, parsarea fișierelor XML, controale de tip Windows Forms și Windows Presentation Foundation etc.
WPF [4]
Windows Presentation Foundation este un subsistem grafic pentru randarea interfețelor grafice în cadrul aplicațiilor Windows, oferind efecte vizuale în concordanță perfectă cu cele ale sistemului de operare Windows. WPF a fost lansat prima dată în cadrul platformei .NET 3.0. O principală caracteristică a sa o reprezintă faptul că este construit direct pe platforma DirectX, care furnizează accelerare hardware și efecte grafice moderne: transparențe, gradienți, transformări. WPF presupune folosirea limbajului XAML, în care sunt definite diferitele elemente de interfața.
Folosind WPF, o aplicație este dezvoltată folosind limbajul de marcare (XAML) pentru definerea interfeței, și un limbaj de programare (C#) pentru implementarea comportamentului aplicației.
WPF furnizează un model de programare consistent, care separă interfața grafică de logica de programare. Această separare poate fi realizată datorită mecanismului de data binding din WPF, mecanism care permite asocierea de date cu elemente ale interfeței grafice din WPF. Un element WPF se poate lega la o sursă de date, astfel încat starea lui se va actualiza automat odată cu modificarea datelor asociate. Această asociere poate fi realizată in XAML sau în cod C#.
Comportamentul aplicației este definit în codul scris în limbajul de programare C#, în care se implementează funcționalitatea pentru a răspunde la interacțiunile utilizatorului cu interfața grafică, cuprinzând tratările evenimentelor (de exemplu, apăsarea unui buton, a unui meniu principal etc), stabilindu-se acțiunile întreprinse și răspunsul la aceste evenimente.
Platforma .NET, namespace-ul System.Windows, limbajul de marcare XAML și codul din spate constituie componentele unei aplicații Windows Presentation Foundation.
XAML [5]
XAML, prescurtat de la “Extensible Application Markup Language” este un limbaj de marcare bazat pe XML, folosit la implementarea declarativă a aspectului interfeței unei aplicații (ferestre, pagini, controale, forme, grafică). Este folosit pentru a crea interfețe în WPF, Silverlight, WF și documente electronice în XPS.
În cadrul XAML-ului se realizează instanțierea directă o obiectelor definite în assembly-uri prin declararea elementelor în cadrul unor tag-uri, în interiorul cărora sunt specificate și atributele (proprietățile) elementului respectiv. Specificația XAML definește reguli ce mapează spațiile de nume din .NET, tipuri, proprietăți și evenimente în spații de nume XML, elemente și atribute.
Un element are, de obicei, rolul de a declara o instanță a unui tip definit în assembly-uri.Din punct de vedere sintactic, declararea unui element începe printr-o paranteză unghiulară închisă, urmată de numele tipului de element ce se dorește a fi creat. Opțional, se pot declara atribute ale elementului. Finalizarea declarării se face prin folosirea unui slash și a unei paranteze unghiulare închise. Se poate observa că sintaxa este asemănătoare cu declararea elementelor într-o pagină html.
<Button Background="Blue" Foreground="Red" Content="This is a button"/>
Microsoft Visual Studio
Pentru dezvoltarea aplicației s-a folosit ca IDE Microsoft Visual Studio 2010, care oferă suport pentru dezvoltarea de aplicații în diferite limbaje de programare (C++, C#, Visual Basic, F# etc.) și de diferite tipuri (aplicații tip consolă, aplicații de tip WindowsForms și WPF, aplicații web etc.). Visual Studio 2010 vine cu versiunea 4.0 a .NET Framework.
Fig. 2 Mediul de dezvoltare Microsoft Visual Studio 2010
NOȚIUNI TEORETICE
Reprezentarea digitală a imaginilor
Pentru prelucrarea digitală a unei imagini, aceasta trebuie să fie reprezentată în mod digital. Acest lucru presupune împărțirea unei imagini în multe elemente mici, astfel încât fiecare element să aibă o singură culoare. Acest element, care reprezintă cea mai mică unitate dintr-o imagine digitală, poartă numele de pixel și este caracterizat de trei mărimi:
culoare
poziție
transparență
Astfel, o imagine poate fi considerată o matrice de pixeli, în care numărul de coloane reprezintă lățimea imaginii exprimată în număr de pixeli, iar numărul de linii reprezintă înălțimea imaginii. Poziția unui pixel în matrice reprezintă poziția acestuia în cadrul imaginii.
Numărul de pixeli ai unei imagini digitale definește rezoluția imaginii; împărțirea imaginii într-un număr mare de pixeli are ca rezultat obținerea unei calități ridicate a imaginii, având un grad mare de detaliere. Pe scurt, cu cât rezoluția este mai mare, cu atât calitatea imaginii este mai bună.
În cadrul prelucrării digitale a imaginilor, culoarea unui pixel este cea mai importantă componentă a sa. Într-un mod simplificat, se poate spune că aplicarea unui efect asupra unei imagini presupune schimbarea culorii sale pe baza unor algoritmi diferiți, în funcție de scopul dorit. De aceea, foarte important este modul în care culoarea unui pixel este reprezentată în mod digital. Cel mai uzual mod de reprezentare a culorilor este prin modelul RGB.
Reprezentarea RGB a culorilor [5]
Numele modelului RGB este format din inițialele celor trei culori primare aditive, roșu (Red), verde (Green) și albastru (Blue). Modelul RGB pentru reprezentarea culorilor se bazează pe principiul conform căruia fasciculele de lumină de culoare roșie, verde și albastră, sunt combinate, având diferite intensități, în scopul de a obține o gamă largă de culori. Fiecare fascicul reprezintă o componentă a culorii, iar fiecare componentă poate avea o intensitate în cadrul culorii obținute în urma amestecului componentelor.
O intensitate minimă pentru fiecare componentă, ceea ce înseamnă lipsa culorii, duce la obținerea negrului. O intensitate maximă a fiecărei componente duce la obținerea albului.
În reprezentarea digitală, fiecare componentă poate lua valori întregi în intervalul [0,255]. Astfel, un pixel a cărui culoare este reprezentată prin RGB = (0,0,0) este un pixel negru, iar un pixel a cărui culoare este reprezentată prin RGB = (255,255,255) este un pixel negru. Culorile primare aditive sunt reprezentate în spațiul RGB având componenta corespunzătoare de intensitate maximă, iar celelalte două componente de intensitate minimă. Astfel, (255,0,0) reprezintă culoarea roșu, (0,255,0) culoarea verde, iar (0,0,255) culoarea albastru. În cazul în care intensitățile de roșu, verde și albastru sunt egale, se obțin nuanțe de gri. Cu ajutorul modelului RGB se pot reprezenta 256^3, adică peste 16 milioane de culori. Un pixel a cărui culoare este reprezentată prin modelul RGB are 24 de biți, câte 8 pentru fiecare componentă.
Fig. 3 Modelul RGB de reprezentare a culorilor
Reprezentarea RGBA a culorilor
Reprezentarea RGBA a culorilor presupune existența a patru canale: cele trei specifice reprezentării RGB, roșu, verde și albastru, și, în plus, un canal alpha, care oferă informații despre transparența/opacitatea culorii. Ca și canalele roșu, verde și albastru, canalul alpha poate lua valori întregi în intervalul [0, 255]. Dacă valorea canalului alpha este 255, culoarea este complet opacă, iar în cazul în care este 0, culoarea este complet transparentă. În cazul reprezentării RGBA, un pixel este format din 32 de biți.
Fig. 4 Modelul RGBA de reprezentare a culorilor
Formate de imagini [7]
BMP (Bitmap)
BMP (bitmap) este un format standard pentru imagini folosit de Windows, care permite stocarea imaginilor înr-un mod independent de platformă sau de aplicație. În cazul imaginilor BMP, numărul de biți pentru fiecare pixel (1, 4, 8, 16, 24, 32, 64) este specificat într-o structură fixă, numită header. Cea mai comună reprezentare a unui pixel este cea pe 24 de biți. Folosirea imaginilor în format BMP ar trebui evitată, întrucât, de obicei, fișierele BMP nu sunt comprimate și au o dimensiune mare.
JPEG/JPG (Joint Photographic Experts Group)
JPEG este un format pentru imagini în care informația este comprimată. În cadrul procesului de comprimare, se pierd unele informații, însă pierderea este mică, fiindcă, de cele mai multe ori, ochiul uman nu o poate percepe. Nivelul comprimării se poate configura, astfel încat un nivel mai mare de comprimare duce la pierderea de mai multă informație. Reprezentarea unui pixel în cadrul imaginilor JPG se face pe 24 de biți, astfel încât pot fi reprezentate peste 16 milioane de culori. Imaginile în format JPG nu au canal alpha, așadar nu au transparență, și nici animații.
GIF (Graphics Interchange Format)
Imaginile în format GIF sunt folosite cu precădere în paginile web, datorită faptului că sunt comprimate. Compresia în cazul formatului GIF se face fără pierdere de informație, astfel încât nu există diferențe vizuale între imaginea comprimată și cea decomprimată. Sunt suportate și animațiile: o secvență de mai multe fișiere GIF poate fi stocată într-un singur GIF, formând o imagine animată. Reprezentarea unui pixel se face pe 8 biți, așadar paleta cromatică în cazul fișierelor GIF este redusă la 256 de culori alese din spațiul de culori pe 24 de biți RGB. De aceea, nu sunt recomandate pentru reproducerea imaginilor complexe, însă sunt perfecte pentru imagini simple reprezentând logo-uri, bannere etc.
PNG (Portable Networks Graphics)
Fișierele PNG sunt cele mai folosite în mediul online. Ca și fișierele în format GIF, fișierele PNG sunt comprimate fără a pierde din informația inițială. Un mare avantaj al acestora este faptul că pot avea un canal alpha pentru fiecare pixel, care specifică transparența culorii pixelului, mai exact gradul în care culoarea pixelului este amestecată cu cea a fundalului. Formatul PNG stochează pixelii în format RGB pe 24 de biți sau RGBA pe 32 de biți.
TIFF (Tagged Image File Format)
Este un format flexibil, suportat de o mare varietate de platforme și de aplicații de procesare de imagini. Imaginile în format TIFF stochează pixelii pe un număr arbitrar de biți și poate folosi mai mulți algoritmi de compresie.
STRUCTURA APLICAȚIEI
Descriere generală
Aplicația este structurată conform figurii de mai jos. Au fost implementate diverse ferestre cu roluri diferite în cadrul aplicației, a fost implementat comportamentul din spatele acestora, precum și clase care au ca scop procesarea imaginilor în vederea obținerii efectelor dorite. În continuare este prezentat rolul fiecărui fișier.
Fig. 5 Structura aplicației
BaseWindow.cs – conține clasa BaseWindow, care este derivată din clasa System.Windows.Window și implementează interfața INotifyPropertyChanged, al cărei scop este de a notifica atunci când o proprietate a clasei este modificată. Reprezintă clasa de bază a tuturor ferestrelor implementate.
BitonalWindow.xaml, BitonalWindow.xaml.cs – în fișierul .xaml este construită interfața grafică a ferestrei ce apare în cazul aplicării efectului “Bitonal”, iar în fișierul .xaml.cs este implementat comportamentul acesteia.
Fig. 6 Fereastra Bitonal din cadrul aplicației
ColorBalanceWindow.xaml, ColorBalanceWindow.xaml.cs – în fișierul .xaml este construită interfața grafică a ferestrei ce apare în momentul în care se dorește aplicarea filtrului “Color Balance”, iar fișierul .xaml.cs conține implementarea comportamentului acesteia.
Fig. 7 Fereastra Color Balance din cadrul aplicației
ColorPickerWindow.xaml, ColorPickerWindow.xaml.cs – în fișierul .xaml este construită interfața grafică a ferestrei care conține un control de tip ColorPicker și două butoane (OK, Cancel), iar în fișierul .xaml.cs este implementat comportamentul acesteia.
Fig. 8 Fereastra Color Picker din cadrul aplicației
ColorSubstitutionWindow.xaml, ColorSubstitutionWindow.xaml.cs – în fișierul .xaml este construită interfața grafică a ferestrei ce apare în momentul în care se dorește aplicarea efectului de înlocuire a unei culori, iar fișierul .xaml.cs conține implementarea comportamentului acesteia.
Fig. 9 Fereastra Color Substitution din cadrul aplicației
LaplacianOfGaussianWindow.xaml, LaplacianOfGaussianWindow.xaml.cs – în fișierul .xaml este construită interfața grafică a ferestrei ce apare atunci când utilizatorul dorește aplicarea filtrului de “edge detection” prin metoda Laplacian de Gaussian. Fereastra permite selectarea tipurilor de matrici folosite pentru acest efect. În fișierul .xaml.cs este implementat comportamentul acestei ferestre.
Fig. 10 Fereastra Laplacian of Gaussian din cadrul aplicației
LaplacianWindow.xaml, LaplacianWindow.xaml.cs – în fișierul .xaml este construită interfața grafică a ferestrei ce apare atunci când utilizatorul dorește aplicarea filtrului de “edge detection” prin metoda Laplacian. Fereastra permite selectarea tipurilor de matrici folosite pentru acest efect, precum și opțiunea ca imaginea rezultată să fie gri sau color. În fișierul .xaml.cs este implementat comportamentul acestei ferestre.
Fig. 11 Fereastra Laplacian din cadrul aplicației
ResizeWindow.xaml, ResizeWindow.xaml.cs – în fișierul .xaml este implementată interfața grafică a ferestrei ce apare atunci când utilizatorul dorește redimensionarea imaginii. Aceasta conține controale care permit alegerea noilor dimensiuni, precum și a calității imaginii redimensionate. În fișierul .xaml.cs se gasește implementarea comportamentului acestei ferestre.
Fig. 12 Fereastra Resize din cadrul aplicației
SliderWindow.xaml, SliderWindow.xaml.cs – fișierul .xaml conține implementarea interfeței grafice a unei ferestre ce conține un slider și este folosită în mai multe scopuri (pentru setarea nivelului contrastului dintr-o imagine, a luminozității, a gradului de distorsionare etc). Fișierul .xaml.cs conține implementarea comportamentului acesteia.
Fig. 13 Fereastra Contrast din cadrul aplicației
MainWindow.xaml, MainWindow.xaml.cs – reprezintă fereastra principală a aplicației, a cărei interfață grafică este construită în fișierul .xaml, iar comportament implementat în fișierul .xaml.cs.
Fig. 14 Fereastra principală a aplicației
SelectionCanvas.cs – conține implementarea clasei SelectionCanvas, care are ca scop oferirea posibilității de a realiza selecția vizuală a unei porțiuni rectangulare a imaginii, în vederea decupării sale.
DragCanvas.cs – conține implementarea clasei DragCanvas, care permite deplasarea zonei rectangulare selectate și mutarea acesteia pe suprafața imaginii prin utilizarea mouse-ului.
BitmapExtensions.cs – conține implementarea metodelor de extensie ale clasei bitmap care permit preluarea unui array de tip byte[] ce conține informații despre pixelii imaginii dintr-un obiect de tip System.Drawing.Bitmap, precum și o metodă statică pentru operația inversă, cea pentru obținerea unui obiect de tip System.Drawing.Bitmap, pornind de la un array de tip byte[].
ImageFilter.cs – conține clasa statică ImageFilters care implementează metode statice pentru toate filtrele de imagine pe care le deține aplicația.
Matrix.cs – conține toate matricile folosite pentru filtrele ce presupun aplicarea convoluției imaginii.
CustomBitmap.cs – în acest fișier este construită o clasă care conține un obiect de tip System.Drawing.Bitmap, precum și calea către imaginea încărcată, întrucât obiectul de tip Bitmap nu deține nici o metodă/proprietate din care se poate prelua calea imaginii încărcate.
UndoRedoObject.cs – conține implementarea pentru mecanismul de Undo/Redo.
Descrierea algoritmilor pentru obținerea efectelor
Pentru încărcarea imaginilor s-a folosit clasa Bitmap din namespace-ul System.Drawing oferit de platforma .NET.
Deși în cadrul clasei Bitmap se poate itera pe lungimea și lățimea imaginii și se pot accesa pixelii prin metodele GetPixel(int x, int y) pentru accesare și SetPixel(int x, int y) pentru modificare, aceste metode sunt foarte costisitoare din punct de vedere al performanței, întrucât se face access la imaginea respectivă de fiecare dată. Pentru a evita acest lucru, a fost implementată o metodă care primește un parametru de tip Bitmap și returnează un tablou unidimensional de tip byte[] care conține informațiile pixelilor imaginii. Metoda realizează următorii pași:
se blochează bitmap-ul;
se copiază bytii corespunzători imaginii într-un tablou unidimensional de tip byte[], în care fiecare pixel al imaginii este reprezentat prin 4 byti, corespunzători componentelor albastru, verde, roșu și alpha;
se deblochează bitmap-ul.
Fig. 15 Modul de preluare a pixelilor dintr-un obiect Bitmap
Toate prelucrările pentru obținerea efectelor dorite se vor face pe tabloul de byti obținut, și nu direct pe bitmap, îmbunătățind performanța mai mult decât considerabil.
Astfel, majoritatea algoritmilor implementați pentru realizarea efectelor/procesării imaginii apelează funcția GetByteArray() și apoi prelucrează corespunzător tabloul obținut. După procesare, trebuie să se obțină un nou bitmap, având ca date pixelii prelucrați. În acest scop, o altă metodă a fost implementată. Aceasta primește ca parametru un tablou unidimensional de tip byte[], precum și dimensiunile imaginii care se dorește a fi creată, și returnează ca rezultat bitmap-ul corespunzător:
Fig. 16 Modul de construire a unui Bitmap pornind de la un tablou de pixeli
În continuare vor fi prezentate câteva efecte și modul de obținere a lor.
Grayscale
O imagine grayscale este așa numita imagine alb-negru, în care fiecare pixel al imaginii reprezintă o nuanță de gri. Astfel, realizarea efectului grayscale presupune conversia fiecărui pixel al imaginii originale într-un pixel de culoare gri. În reprezentarea RGB, orice pixel care are cantități egale de roșu, verde și albastru (R=B=G) reprezintă o nuanța de gri. Pentru realizarea efectului grayscale, fiecare pixel va fi prelucrat astfel: se va forma o valoare, reprezentând valoarea nuanței de gri, prin adunarea a diferitor procente din valorile componentelor roșu, verde și albastru (procentele pot fi văzute în figura de mai jos); noul pixel va avea toate componentele egale cu respectiva valoare obținută.
Fig. 17 Prelucrarea unui pixel pentru obținerea efectului Grayscale
Fig. 18 Rezultatul efectului Grayscale
Invert
Filtrul Invert realizează inversarea culorilor pixelilor unei imagini și valorile luminozității în stratul curent, astfel încât lasă impresia că imaginea a fost transformată într-un negativ. Zonele întunecate devin luminoase și zonele luminoase devin întunecate. Filtrul Invert se obține prin scăderea fiecărei componente R,G,B a unui pixel din valorea 255, așa cum se poate observa în figura de mai jos:
Fig. 19 Prelucrarea unui pixel pentru obținerea efectului Invert
Fig. 20 Rezultatul efectului Invert
Filtrul de culoare
Filtrul de culoare are ca efect prelucrarea imaginii astfel încât, selectându-se o culoare, imaginea va părea că este formată în totalitate din culoarea respectivă. Pentru obținerea acestui efect, presupunând că se dorește a se filtra culoarea cu componentele Rc, Gc și Bc, inițial se stabilesc procentele de roșu, verde și albastru din culoarea filtrată, împărțind fiecare componenta Rc, Gc, Bc a culorii alese la 255. Un nou pixel se va obține prin înmulțirea fiecărei componente R, G, B a pixelului sursă cu procentul obținut pentru componenta respectivă.
Fig. 21 Prelucrarea unui pixel pentru obținerea efectului Color filter
Fig. 22 Rezultatul efectului Color filter
Luminozitatea
Filtrul de luminozitate (brightness) are ca scop ajustarea luminozității imaginii, aceasta putând fi mărită sau micșorată, în funcție de preferințele utilizatorului. Valoarea luminozității poate lua valori în intervalul [-255, 255]. Prelucrarea imaginii pentru ajustarea luminozității presupune că pentru fiecare pixel curent al imaginii, pixelul prelucrat se obține prin însumarea fiecărei componente R, G, B cu valorea dorită a luminozității. În cazul în care în urma însumării în cadrul unei componente se obține o valoare mai mică decât 0, respectiva componentă este fixată la 0. Asemănător, în cazul în care se obține o valoare mai mare decât 255, valoarea componentei este fixată la 255, realizându-se așa-numita operație de clamping. Cu cât valoarea luminozității este mai mare, cu atât luminozitatea imaginii este mai puternică, astfel încât, pentru valorea maximă a luminozității (255), imaginea va fi în totalitate albă. Similar, cu cât valoarea luminozității este mai mică, cu atât luminozitatea va fi mai scăzută, astfel încât pentru o valoare minimă a luminozității (-255), imaginea va avea în totalitate culoarea neagră.
Fig. 23 Prelucrarea unui pixel pentru obținerea efectului Brightness
Fig. 24 Rezultatul efectului Brightness
Sepia
Filtrul Sepia are ca efect obținerea unei imagini cu aspect învechit, fiind o consecință a faptului că o imagine sepia este construită în totalitate din tonuri de maro. Cantitățile de roșu, verde și albastru pentru determinarea noilor valori ale componentelor pixelului prelucrat se pot observa în figura de mai jos:
Fig. 25 Prelucrarea unui pixel pentru obținerea efectului Sepia
Fig. 26 Rezultatul efectului Sepia
Contrast
Contrastul este definit ca separarea dintre cele mai întunecate și mai luminoase zone ale imaginii. Cu cât este mai mare valoarea contrastului, cu atat va crește separarea între întunecat și luminos, umbrele devenind mai întunecate și zonele luminoase fiind puse in evidență. Scăderea valorii contrastului are efect invers, astfel încat zonele luminoase devin mai întunecate, iar cele întunecate devin mai luminoase, având ca scop apropierea umbrelor și a luminozităților. Un contrast ridicat face ca o imagine să pară mai vie, să aiba culori vibrante, pe când un contrast slab are ca efect o imagine fadă.
Fig. 27 Rezultatul efectului Contrast (pentru valoarea 20 a contrastului)
Distorsionare [8]
Distorsionarea unei imagini se realizează prin ajustarea ușoară a coordonatelor fiecărui pixel. Direcția și distanța pe care coordonatele pixelilor sunt ajustate diferă pentru fiecare pixel în parte. Distanța maximă de offset aplicată depinde de factorul de distorsionare specificat. După ce sunt actualizate coordonatele tuturor pixelilor, se aplică un filtru median, care are ca scop obținerea unui efect de netezire și estompare a imaginii.
Pașii care trebuie urmați pentru obținerea unui efect de distorsionare:
Se iterează pixelii imaginii.
Calcularea noilor coordonate: pentru fiecare pixel iterat se generează două valori aleatoare reprezentând coodonatele de offset XY care urmează să fie aplicate coordonatelor pixelului curent. Valorile de offset pot fi mai mici de zero, în scopul de a reprezenta coordonatele mai sus sau la stânga pixelului curent.
Aplicarea unui filtru median: în urma procesării de la pasul 2), pixelii rezultați vor crea un aspect “punctat” al imaginii. Filtrul median are rolul de a înlătura acest aspect, conferind un aspect de netezire a imaginii, dar păstrând în același timp și aspectul distorsionat.
Pentru obținerea unui filtru median se execută următorii pași:
Se iterează pixelii imaginii.
Se inspectează vecinii pixelului curent: vecinii pixelului iterat se păstrează într-o colecție temporară.
Determinarea valorii mediane a vecinătății: după ce toți pixelii vecini au fost adăugați într-o colecție temporară, colecția este sortată după valoare. Valoarea elementului situat la mijlocul colecției sortate reprezintă valoarea mediană a vecinătății pixelului.
Fig. 28 Rezultatul efectului Distorsionare
Efectul de pictură în ulei [9]
Acest efect are ca scop, asa cum îi sugerează și numele, obținerea unui aspect de pictură în ulei. Imaginea prelucrată, în comparație cu imaginea sursă, pare a avea un nivel scăzut al detaliilor, precum și o paletă cromatică mai mică. Efectul de pictură în ulei conține două componente: gradienți de coloare și intensități de culoare. Prelucrarea se realizează în funcție de două mărimi primite ca date de intrare:
Mărimea filtrului: numărul de pixeli vecini luați în considerare la calcularea unui nou pixel.
Nivele de intensitate: valorile mai mari conduc la o gamă mai largă de intensități de culoare în imaginea rezultată; valorile mai mici vor reduce gama de intensități de culoare care fac parte din imaginea rezultată.
Pentru obținerea efectului de pictură în ulei, se parcurg următorii pași:
Se iterează pixelii imaginii. Pentru fiecare pixel iterat, se determină valorile pixelilor vecini pe baza mărimii filtrului.
Calcularea intensității culorii: se determină intensitatea culorii fiecărui pixel iterat, precum și a pixelilor vecini ai săi.
Determinarea intensității maxime a vecinătății: atunci când se calculează intensitățile de culoare ale pixelilor vecini, se stabilește valoarea maximă a intensității. În plus, se înregistrează apariția fiecărei valori a intensității și se însumează fiecare componentă (roșu, verde, abastru) a pixelilor care au valorea intensității egală. Intensitatea se determină pe baza formulei:
I = ( (R + G + B) / 3L) / 255
unde:
I – intensitatea: valoarea intensității calculate.
R – roșu: Componenta roșie a unui pixel.
G – verde: Componenta verde a unui pixel.
B – albastru: Componenta albastră a unui pixel.
L- numărul de niveluri de intensitate: numărul maxim de niveluri de intensitate specificat.
Atribuirea pixelului rezultat – valoarea atribuită pixelului corespunzător în imaginea rezultată echivalează cu suma totală a culorii pixelilor care au avut același nivel de intensitate. Suma totală primește o valoare medie, împărțind suma totală de culoare la numărul de apariții ale nivelului de intensitate.
Fig. 29 Rezultatul efectului Pictură în ulei
Sharpen [10]
Aplicarea unui filtru Sharpen asupra unei imagini are ca scop punerea în evidență a unor detalii ale imaginii, care, inițial, nu par a fi prezente. Obținerea acestui efect se realizează prin accentuarea unor contururi, prin înnegurarea pixelilor de culoare închisă și prin luminarea pixelilor ce au o culoare mai deschisă, astfel încât să fie mai ușor de perceput de către ochiul uman.
Implementarea acestui filtru presupune aplicarea convoluției imaginii (prezentată mai jos în secțiunea Convoluția) prin folosirea următoarei matrici:
Fig. 30 Matricea de convoluție pentru Sharpen
Fig. 31 Rezultatul efectului Sharpen
Substituția unei culori
Substituția unei culori se realizează prin înlocuirea unei culori din imaginea sursă cu o altă culoare în imaginea rezultată. Întrucât în cadrul unei imagini există mai multe nuanțe ale aceleiași culori, de cele mai multe ori diferențele dintre ele fiind aproape imperceptibile de către ochiul uman, algoritmul de substituție a unei culori permite nu numai înlocuirea culorii specificate, ci și a culorilor care se află într-o gamă apropiată culorii alese, prin specificarea unei marje.
Astfel, algoritmul presupune iterarea tuturor pixelilor, iar pentru fiecare pixel, se stabilește dacă este necesară substituția culorii sale. În acest scop, se testează dacă toate componentele pixelului (R, G, B) au valori cuprinse între valorile componentelor corespunzătoare ale culorii care se dorește a fi înlocuită +/- marja specificată. Dacă această condiție este îndeplinită, se realizează substituția culorii.
De notat este faptul că, în contextul existenței unei marje, noua culoare a pixelului nu este setată ca fiind exact culoarea de substituție, ci o nuanță a acesteia, având ca scop păstrarea intensităților și a aspectului imaginii inițiale.
Fig. 32 Prelucrarea unui pixel pentru substituția unei culori
Fig. 33 Rezultatul substituției unei culori
Solarizarea imaginii [11]
Solarizarea unei imagini poate fi descrisă ca un mod de inversiune a culorilor. Diferența dintre solarizarea imaginii și inversiunea culorilor imaginii este reprezentată de apariția unor valori de prag de care trebuie să se țină cont atunci când se realizează solarizarea imaginii.
Dacă inversiunea unei imagini poate fi implementată prin scăderea fiecărei componente de culoare a fiecărui pixel din valoarea 255, în cazul solarizării imaginii, aceeași operație este executată, dar numai dacă sunt îndeplinite anumite condiții. Considerându-se o culoare după care se face solarizarea, condiția de inversiune a unei componente a pixelului sursă este ca această componentă să aibă valoarea mai mică decât valoarea componentei corespunzătoare din culoarea de solarizare.
Fig. 34 Prelucrarea unui pixel pentru solarizarea imaginii
Fig. 35 Rezultatul solarizării imaginii
Detectarea contururilor (Edge Detection) [12]
"Detectarea contururilor (eng. edge detection) este numele dat pentru un set de metode matematice care vizează identificarea punctelor dintr-o imagine digitală la care luminozitatea imaginii se schimbă brusc sau, mai formal, are discontinuități. Punctele la care schimbările de luminozitate a imaginii se realizează brusc sunt de obicei organizate într-un set de segmente curbe numite margini/contururi. Aceeași problemă de a găsi discontinuități în semnale 1D este cunoscută sub numele de detectare de pași (eng. step detection), iar problema de a găsi discontinuități de semnal în timp este cunoscută sub numele de detecție a modificărilor (eng. change detection). Detectarea contururilor este un instrument fundamental în procesul de prelucrare a imaginilor." [13]
Detectarea contururilor a fost realizată prin folosirea mai multor algoritmi, precum Laplacian, Laplacian de Gaussian, Sobel, Prewitt și Kirsch. Toate acestea sunt realizate prin intermediul convoluției.
Convoluția
Convoluția este o operație matematică simplă care este fundamentală mai multor operatori în procesarea imaginilor. Convoluția oferă o modalitate de a înmulți două șiruri de numere, în general de mărime diferita, dar având aceeași dimensionalitate. Aceasta poate fi folosită în procesarea imaginilor pentru a implementa operatori ale căror valori rezultate ale pixelilor sunt combinații lineare simple ale valorilor de pixeli inițiali. [14]
Implementarea convoluției se realizează prin intermediul unui nucleu de convoluție, care este reprezentat printr-o matrice. Matricea, în cazul convoluției, trebuie să fie pătratică, iar o altă condiție importantă pe care trebuie să o respecte este aceea ca dimensiunile sale să nu fie multiplu de doi. Matrici valide sunt, de exemplu, matrici cu dimensiunile 3×3, 7×7 etc. De obicei, valorile dintr-o matrice de convoluție au suma 1, însă dacă acest lucru nu se aplică, se folosește un factor de convoluție egal cu raportul dintre valorea 1 și suma tututor elementelor matricii.
Aplicarea convoluției presupune că valoarea unui pixel se determină pe baza valorii pixelilor vecini. Valorile matricii reprezintă valori care se înmulțesc cu valorile pixelilor. Centrul matricii reprezintă pixelul care se modifică, iar celelalte valori reprezintă factorul cu care se vor înmulți pixelii vecini ai pixelului modificat în vederea obținerii valorii sale. Astfel, pentru determinarea componentelor unui pixel filtrat, se adună componentele corespunzătoare ale vecinilor săi asupra cărora s-au aplicat factorii corespunzători din matricea de convoluție.
Detectarea contururilor folosind Laplacian
"Metoda Laplaciană de detectare a marginilor este una dintre cele mai cunoscute metode de implementare a algoritmului de detectare de contururi. Laplacianul este definit ca suma derivatelor de ordinul al II-lea și este calculat ca suma diferențelor de-a lungul celor mai apropiați vecini ai pixelului central." [15]
Filtrul este, de fapt, aplicarea unei convoluții cu o matrice Laplaciană. Implementarea este realizată pentru a folosi matrici Laplaciene de dimensiuni diferite, precum și posibilitatea de a alege dacă imaginea rezultată este alb-negru sau color. De precizat este faptul că matricile Laplaciene sunt sensibile la “zgomotul imaginii”.
În cazul folosirii unei matrici Laplaciene 3×3, rezultatele pentru imagine alb-negru sau imagine color sunt aproape identice, diferențele fiind foarte puțin sesizabile.
Dacă se utilizează o matrice Laplaciană de dimensiune 5×5, diferențele dintre imaginea rezultată alb-negru și imaginea rezultată color sunt notabile. Marginile sunt detectate cu o precizie relativ bună.
Detectarea de contururi folosind Laplacian de Gaussian
Operatorul Laplacian de Gaussian (Laplacian of Gaussian-LoG) este o variație a filtrului Laplacian, care are ca scop înlăturarea sensibilității la zgomotul imaginii din cadrul acestuia. Pentru a realiza acest lucru, LoG implementează netezirea imaginii prin utilizarea unui filtru Gaussian de estompare. Principiul pe care se bazează această metodă este:
1.Aplicarea filtrului de convoluție utilizând o matrice Gaussiană asupra imaginii sursă.
2.Aplicarea filtrului de convoluție utilizând o matrice Laplaciană asupra imaginii obținute la pasul 1.
Variații ale LoG pot fi realizate prin folosirea de matrici Laplaciene și Gaussiene de diferite dimensiuni, rezultând astfel diferite combinații cu efecte diferite în cadrul detectării contururilor (Laplacian 3×3-Gaussian 3×3, Laplacian 3×3-Gaussian 5×5, Laplacian 5×5-Gaussian 3×3 etc.)
Detectarea de contururi folosind Sobel
"Operatorul Sobel este folosit în procesarea de imagini, cu precădere în algoritmii de detectare de margini. Este un operator de diferențiere discretă, care calculează o aproximare a gradientului funcției de intensitate a imaginii. Pentru fiecare punct din imagine, rezultatul operatorului Sobel este fie vectorul gradienților, fie norma acestui vector. Operatorul Sobel se bazează pe convoluția imaginii cu o valoare întreagă mică în direcție verticală și orizontală, fiind relativ necostisitoare din punct de vedere al calculelor." [16]
Se realizează prin intermediul convoluției, utilizând o matrice Sobel pentru direcție verticală și o alta pentru cea orizontală.
În cazul folosirii filtrului Sobel, contururile detectate nu sunt reprezentate în mod detaliat, fin, precum în metoda Laplaciană, fiindcă acesta nu este la fel de sensibil la zgomotul imaginii. De asemenea, se remarcă diferențe mai mult decât evidente între imaginea color și cea alb-negru.
Detectarea de contururi folosind Prewitt
"Operatorul Prewitt calculează gradientul intensității imaginii în fiecare punct, dând direcția celei mai mari creșteri posibile de la zonă luminoasă la zonă întunecată, precum și rata de schimbare în direcția respectivă. Astfel, rezultatul arată cât de brusc sau cât de lent se produce schimbarea imaginii în acel punct, prin urmare în ce măsură este posibil ca acea parte a imaginii să reprezinte un contur, precum și cum este posibil ca acel contur să fie orientat. În practică, este mai sigur și mai simplu de interpretat calcularea magnitudinii, și nu cea a direcției." [17]
Se realizează prin intermediul convoluției, utilizând o matrice Prewit pentru direcție verticală și o alta pentru cea orizontală.
Utilizând operatorul Prewitt pentru detectarea de contururi, se observă, de asemenea, că există mari diferențe între imaginea color rezultată și cea alb-negru.
Detectarea contururilor folosind Kirsch
"Operatorul Kirsch este un detector de contur non-linear, care găsește conturul cu cea mai mare intensitate în câteva direcții predeterminate." [18]
Se realizează prin intermediul convoluției, utilizând o matrice Kirsch pentru direcție verticală și o alta pentru cea orizontală.
În implementarea algoritmului de detectare de contururi folosind operatorul Kirsch, imaginile rezultate au un nivel al intensității ridicat.
Echilibrul culorilor [19]
Echilibrul culorilor se referă la ajustarea globală a intensității culorilor unei imaginii (roșu, verde, albastru), schimbând astfel aspectul global al culorilor imaginii. Folosit corect, rezultatul unui astfel de filtru poate fi unul foarte bun, astfel încât poate fi utilizat ca un filtru de corectare, atunci când culorile dintr-o imagine diferă de ceea ce se așteaptă de la imaginea respectivă. Așa cum îi sugerează și numele, efectul are ca scop realizarea unui echilibru al culorilor imaginii, astfel încât culorile care nu sunt considerate a fi în concordanță cu restul imaginii vor fi filtrate, conferind imaginii procesate un aspect mai natural.
Dându-se trei valori pentru nivelul de roșu, verde și albastru, valori întregi cuprinse în intervalul [0, 255] și care se consideră a fi valorile ideale pentru obținerea efectului dorit, echilibrul culorilor se realizează procesând fiecare pixel al imaginii sursă după formula prezentată în figura de mai jos:
Fig. 60 Prelucrarea unui pixel pentru echilibrul culorilor
Fig. 61 Rezultatul aplicării echilibrului culorilor
Imagine bi-tonala [20]
Filtrul de imagine bi-tonala prespune că imaginea rezultată va fi compusă doar din două culori. Dându-se cele două culori din care se dorește a fi formată imaginea finală, precum și o marjă, care poate lua valori întregi în intervalul [0, 765], imaginea rezultată este construită în felul următor:
1. Se consideră că una dintre cele două culori este folosită pentru zonele întunecate ale imaginii, iar cealaltă pentru cele luminoase.
2. Pentru fiecare pixel al imaginii sursă, trebuie să se stabilească dacă acesta aparține zonei luminoase sau întunecate, în funcție de marja furnizată, pentru a se stabili dacă noua culoare a pixelului va fi cea “întunecată” sau cea “luminoasă”. Acest lucru se realizează prin compararea valorii sumei celor trei componente ale pixelului sursă (R, G, B) cu valoarea marjei.
a. Dacă suma obținută este mai mică sau egală decât valoarea marjei, noua culoare a pixelului va fi culoarea “întunecată”.
b. Dacă suma obținută este mai mică decât valoarea marjei, noua culoare a pixelului va fi culoarea “luminoasă”.
Modul în care se realizează efectul poate fi observat și în figura de mai jos:
Fig. 62 Prelucrarea unui pixel pentru obținerea unei imagini bi-tonale
Fig. 63 Rezultatul aplicării efectului de imagine bi-tonală
Estomparea/neclaritatea imaginii (blur) [21]
Estomparea imaginii, în termeni largi, înseamnă reducerea detaliilor imaginii, a clarității sale. În urma estompării, detaliile devin mai puțin vizibile. Atfel, în cazul imaginilor care se consideră că au detalii mult prea definite, se pot aplica diverse tehnici de estompare, având diverse grade de intensitate, cu scopul de a obține o nouă imagine cu o intensitate mai mică a detaliilor.
Pentru implementarea filtrului de estompare s-au folosit mai mulți algoritmi, majoritatea bazați pe folosirea convoluției imaginii.
Estompare folosind filtrul de mediere
Filtrul de mediere este un filtru de procesare a imaginilor în care fiecare pixel din imaginea rezultată are ca valoare media valorilor pixelilor vecini. O caracteristică importantă a acestui filtru, cea de la care provine și denumirea sa, este faptul că valorile ponderilor într-un nucleu de convoluție sunt egale. O matrice nucleu pentru filtrul de mediere conține doar valori de 1, iar atunci când se realizează convoluția imaginii folosind un nucleu de filtru de mediere, valoarea factorului este egală cu raportul dintre 1 și suma tuturor valorilor din nucleu.
De exemplu, o matrice 3×3 pentru filtrul de mediere conține doar valori de 1, iar factorul de convoluție este 1/9, o matrice 5×5 conține doar valori de 1 și are factorul de convoluție 1/25 etc.
Estompare folosind filtrul Gaussian
Se implementează aplicând filtrul de convoluție, folosind matrici Gaussiene de dimensiuni 3×3, respectiv 5×5.
Spre deosebire de filtrul de mediere, imaginea rezultată în cazul folosirii unui filtru Gaussian pare a avea un nivel de netezire mai uniform.
Estompare folosind filtrul median
Spre deosebire de metodele de mai sus, filtrul median nu presupune folosirea convoluției sau a unui nucleu matrice predefinit.
Implementarea filtrului median se bazează pe calculul valorii mediane a unui grup de pixeli; grupul de pixeli formează o “fereastră”. Pentru realizarea filtrului, se parcurg următorii pași:
1. Se iterează fiecare pixel al imaginii sursă.
2. Pentru pixelul curent, se determină pixelii vecini, aflați în regiunea determinată de dimensiunile ferestrei.
3. Pixelii vecini găsiți sunt adăugați într-un tablou. După ce toți vecinii au fost adăugați în tablou, acesta este sortat după valoare.
4. Valorea mediană este reprezentată de valoarea pixelului aflat la mijlocul tabloului de pixeli vecini.
5. Pixelul curent primește valoarea mediană găsită.
Estompare cu efect de mișcare
Acest tip de filtru este asociat cu capturarea unei fotografii a obiectelor în mișcare. Implementarea unui astfel de filtru se bazează tot pe folosirea convoluției imaginii. Nucleul matrice folosit poate afecta, prin dimensiunile sale, intensitatea obținută a estompării în imaginea rezultată. Astfel, folosind matrici de dimensiuni mai mari, efctul este cel al unei mișcări mai rapide a imaginii, pe când folosind matrici de dimensiuni mici, rezultatul perceput este cel al unei mișcări mai lente.De asemenea, se poate preciza direcția mișcării: 135 de grade desemnează o mișcare spre stânga sus, iar 45 desemnează o mișcare spre dreapta sus. Matricile care nu desemnează o direcție sunt formate din 1 pe diagonala principală și secundară, în rest zero. Matricile care desemnează o mișcare la 135 de grade au doar pe diagonala principală valori de 1, iar pentru 45 de grade doar pe diagonala secundară.
Redimensionarea imaginii
Se realizează utilizând facilitățile oferite de clasa System.Drawing.Bitmap și de clasa System.Drawing.Graphics, cu ajutoul cărora se poate redimensiona imaginea conținută într-un obiect de tip System.Drawing.Bitmap prin specificarea noilor dimensiuni dorite, precum și a modului de resampling, care determină calitatea noii imagini.
Fig. 83 Redimensionarea imaginii
Rotirea și reflexia imaginii
Se realizează prin utilizarea facilităților oferite de clasa System.Drawing.Bitmap, care îndeplinesc rotirea și reflexia imaginii, în funcție de tipul de rotire și reflexie specificat print-un parametru de tip System.Drawing. RotateFlipType.
Fig. 84 Rotirea și reflexia imaginii
Salvarea imaginii în alt format/conversia
Salvarea imaginii într-un alt format se realizează tot prin facilitățile oferite de clasa System.Drawing.Bitmap, care pune la dispoziție și o metodă pentru salvarea imaginii conținute de un obiect de tip Bitmap într-un format specificat printr-un parametru de tipul ImageFormat aflat în namespace-ul System.Drawing.Imaging.
Fig. 85 Salvarea imaginii în alt format
Facilități implementate
Fiind o aplicație a cărei interacțiune cu utilizatorul este foarte mare, în vederea unei experiențe plăcute a folosirii aplicației au fost implementate facilități precum:
Undo/Redo
Zoom In/Zoom Out
Afișarea imaginii la dimensiunea reală/Încadrarea imaginii în zona disponibilă de vizualizare
Folosirea de controale atractive, cu imagini sugestive pentru scopul îndeplinit
Implementarea drag-and-drop.
MANUALUL UTILIZATORULUI
Prezentare generală
1 – Meniul principal
2 – Toolbarul principal
3 – Zona de vizualizare a imaginii
4 – Panoul cu tipuri de efecte disponibile
5 – Zona de vizualizare a efectelor
Deschiderea unei imagini
Deschiderea unei imagini se poate realiza prin 3 modalități:
Prin intermediul meniului File->Open
Prin apăsarea butonului Open image din toolbarul principal.
Prin acțiunea de drag-and-drop.
Se acceptă imagini în următoarele formate:
JPEG
PNG
BMP
TIFF
GIF
Salvarea și conversia imaginii
O imagine poate fi salvată accesând File->Save din meniul principal sau butonul Save din cadrul toolbarului.
De asemenea, imaginea poate fi convertită în oricare dintre următoarele formate: BMP, JPEG, PNG, GIF, TIFF, prin folosirea opțiunii de Save As… din meniul principal.
Localizarea pe disc a imaginii
Se realizează prin intermediul File->Locate on disk din meniul principal, care are ca efect deschiderea unei ferestre Windows la locația imaginii.
Ștergerea de pe disc a imaginii
Se realizează prin accesarea File->Delete from disk din meniul principal. O nouă fereastră va apărea pentru confirmarea ștergerii.
Redimensionarea imaginii
Din meniul principal: Edit->Resize
Prin apăsarea butonului Resize din toolbarul principal
Oricare dintre aceste acțiuni va duce la deschiderea ferestrei de redimensionare, în care se pot alege noile dimensiuni ale imaginii, precum și metoda de resampling, care determină calitatea imaginii redimensionate.
Rotirea imaginii
Imaginea se poate roti la stânga sau la dreapta, fie prin intermediul meniului principal (Edit->Rotate left, Edit->Rotate right), fie prin cel al butoanelor Rotate left/Rotate right din toolbarul principal.
Reflexia imaginii (Flip)
Reflexia imaginii se realizează atât pe verticală, cât și pe orizontală. Este posibilă prin intermediul meniului Edit->Vertical flip sau Edit->Horizontal flip sau prin intermediul butoanelor cu același nume din toolbarul principal.
Decuparea imaginii
Pentru decuparea imaginii, se stabilește zona de decupare a imaginii prin trasarea cu ajutorul mouse-ului a unui dreptunghi, apoi selectarea opțiunii Crop din toolbarul principal, sau prin Right Click->Save. Pentru a anula operația de decupare, se selectează Right Click->Cancel.
Zoom in / Zoom out
Pentru a apropia/îndepărta imaginea, se folosesc butoanele de zoom in/zoom out din toolbarul principal.
Încadrarea imaginii în zona de vizualizare
În cazul în care imaginea are o dimensiune foarte mare, aceasta nu poate fi vazută în întregime, depășind zona de vizualizare a editorului. În funcție de preferințe, imaginea poate fi afișată la dimensiunile sale actuale (Display photo at actual size) sau poate fi încadrată în zona de vizualizare disponibilă (Fit photo inside viewing area).
Undo/Redo
Aplicația oferă și facilitățile de Undo/Redo. Butoanele pentru aceste acțiuni se găsesc în toolbarul principal.
Aplicarea de filtre
Filtre generale
Filtrele generale se regăsesc în tab-ul General filters din partea dreaptă a editorului. Printre acestea se regăsesc: Grayscale, Brightness, Contrast, Sepia, Invert, Color filter, Distorsion, Oil Paint, Sharpen.
Grayscale
Transformă imaginea curentă într-o imagine în tonuri de gri.
Invert
Inversează culorile imaginii, astfel încât sunt folosite culorile complementare ale culorilor existente în imaginea inițială. În urma aplicării filtrului Invert, imaginea inițială pare a fi convertită într-un negativ.
Color filter
Atunci când efectul Color filter este selectat, o nouă fereastră va apărea, care conține un Color Picker ce permite selectarea culorii după care se dorește să se efectueze filtrarea. Rezultatul acestui filtru este că imaginea va părea că este formată în totalitate din culoarea selectată.
Brightness
Permite ajustarea luminozității imaginii, putând fi mărita sau micșorată, prin intermediul slider-ului din fereastra deschisă la apăsarea butonului Brightness.
Sepia
Are ca efect obținerea unei imagini cu aspect învechit, fiind o consecință a faptului că imaginea rezultată este construită în totalitate din tonuri de maro.
Contrast
Contrastul realizează separarea dintre cele mai întunecate și mai luminoase zone ale imaginii. Un contrast ridicat face ca o imagine să pară mai vie, să aiba culori vibrante, pe când un contrast slab are ca efect o imagine fadă. Valoarea contrastului poate fi setată prin intermediul slider-ului din fereastra ce apare la apăsarea butonului Contrast.
Distorsion
Realizează o distorsionare a imaginii, în funcție de nivelul de distorsionare ales prin intermediul sliderul-ului din fereastra ce apare la apăsarea butonului Distorsion.
Oil paint
Așa cum îi sugerează și numele, efectul urmărește obținerea aspectului unei picturi în ulei.
Sharpen
Are ca scop punerea în evidență a unor detalii ale imaginii, care, inițial, nu par a fi prezente, conferind un aspect de definire.
Edge detection
În tab-ul Edge detection se regăsesc mai multe butoane, care definesc mai multe metode de a obține detectarea contururilor imaginii. În funcție de metoda aleasă (Laplacian – 3×3, 5×5, LoG, Sobel, Kirsch, Prewitt etc.), rezultatul va fi o imagine alcătuită din contururi mai bine sau mai puțin definite, colorate sau gri.
Advanced filters
În tab-ul Advanced Filters se regăsesc butoane pentru aplicarea a încă patru filtre: Replace color, Solarise, Color balance, Bitonal.
Replace color
Oferă posibilitatea de a înlocui o culoare cu o altă culoare. La apăsarea butonului Replace Color, o nouă fereastră va apărea, în care se poate alege culoarea care se dorește a fi înlocuită, culoarea cu care se dorește a fi înlocuită și specificarea unei marje de înlocuire. Fiindcă în cadrul imaginii există mai multe nuanțe ale aceleiași culori pe care ochiul uman nu le poate distinge, marja precizează cât de mare este gama culorilor apropiate de cea selectată pentru a fi înlocuită.
Solarise
Solarizarea imaginii presupune inversarea totală sau parțiala a culorilor, în funcție de culoarea aleasă pentru solarizare.
Color balance
Color balance permite ajustarea globală a intensității culorilor unei imagini (roșu, verde, albastru), schimbând astfel aspectul global al culorilor imaginii.
Bitonal
Transformă imaginea inițială într-o imagine formată doar din două culori. Cele două culori pot fi alese în fereastra care se deschide la apăsarea butonului Bitonal. De asemenea, se poate regla și gradul în care una dintre culori este predominantă în imagine, folosind slider-ul Range.
Blur
În tab-ul Blur se regăsesc butoane pentru aplicarea de efecte de neclaritate (blur). Exista 17 opțiuni diferite de blurare, al căror efect diferă, unele realizând un efect de blurare mai accentuat, altele mai discret, unele creând senzație de mișcare etc.
Concluzii
Tehnologia WPF din cadrul platformei .NET este un adevărat sprijin în crearea de aplicații pentru sistemul de operare Windows, oferind posibilitatea construirii unei interfețe grafice prin intermediul XAML-ului și a implementării comportamentului acesteia în limbajul C#. Există numeroase articole foarte bine realizate despre WPF și C#, care mi-au fost de mare ajutor în realizarea aplicației.
Există statistici care demonstrează fapul că articolele ce includ imagini iși măresc considerabil numărul de vizualizări. De asemenea, este bine cunoscut faptul că imaginile sunt o componentă crucială în cazul comerțului electronic și al magazinelor online, ale căror vânzări se bazează pe prezentarea vizuală a produselor. Întrucât rețelele de socializare, magazinele online, afacerile pe Internet au înregistrat o dezvoltare incredibilă în ultimii ani și este clar ca își vor continua expansiunea, va exista mereu nevoia de software-uri de prelucrare a imaginilor pentru corecția micilor defecte care pot apărea în cadrul acestora.
Editorul și convertorul de imagini s-a dorit (și sper ca s-a și reușit) a fi simplu de utilizat, orientat pe interacțiunea cu utilizatorul, oferind o interfață grafică simplă, intuitivă și punând la dispoziție filtre și efecte pentru prelucrarea unei imagini. Pentru îmbunătățirea aplicației, ca pași următori am în plan implementarea următoarelor facilități:
Adăugarea mai mulor efecte;
Adăugarea de unelte tradiționale, precum cele pe care le oferă Paint (brush-uri, shape-uri etc.);
Opțiunea de a posta pe Facebook imaginea editată;
Posibilitatea de detectare a screenshot-urilor;
Implementarea unui mod dublu de vizualizare, în care este prezentată imaginea originală comparativ cu cea prelucrată.
Referințe web
[1] – Curs despre limbajul C#, Lucian Sasu , disponibil online la adresa http://www.cs.ubbcluj.ro/~vcioban/Bistrita/Manuale/CursDotNetSassu.pdf
[2] – C# (programming language), articol disponibil la adresa http://en.wikipedia.org/wiki/C_Sharp_(programming_language)
[3] – Introduction to the C# Language and the .NET Framework, disponibil la adresa http://msdn.microsoft.com/en-us/library/z1zx9t92.aspx
[4] – Windows Presentation Foundation, http://en.wikipedia.org/wiki/Windows_Presentation_Foundation
[5] – XAML Overview (WPF), http://msdn.microsoft.com/en-us/library/ms752059.aspx
[6] – RGB color model, http://en.wikipedia.org/wiki/RGB_color_model
[7] – Types of Bitmaps, http://msdn.microsoft.com/en-us/library/at62haz6(v=vs.110).aspx
[8] – Image Distortion Blur, http://softwarebydefault.com/2013/08/09/image-distortion-blur/
[9] – Oil Paint and Cartoon Filter, http://softwarebydefault.com/2013/06/29/oil-painting-cartoon-filter/
[10] – Sharpening Images, http://www.dl-c.com/sharpen.pdf
[11] – Image Solarise, http://softwarebydefault.com/2013/04/13/image-solarise/
[12] – Image Edge Detection. http://softwarebydefault.com/2013/05/11/image-edge-detection/
[13] – Edge detection, http://en.wikipedia.org/wiki/Edge_detection
[14] – Convolution, http://homepages.inf.ed.ac.uk/rbf/HIPR2/convolve.htm
[15] – Discrete Laplace operator, http://en.wikipedia.org/wiki/Discrete_Laplace_operator
[16] – Sobel operator, http://en.wikipedia.org/wiki/Sobel_operator
[17] – Prewitt operator, http://en.wikipedia.org/wiki/Prewitt_operator
[18] – Kirsch operator, http://en.wikipedia.org/wiki/Kirsch_operator
[19] – Color balance, http://softwarebydefault.com/2013/04/11/bitmap-color-balance/
[20] – Bitonal Bitmaps, http://softwarebydefault.com/2013/04/12/bitonal-bitmaps/
[21] – Image Blur FIlters, http://softwarebydefault.com/2013/06/09/image-blur-filters/
Pentru realizarea diagramelor s-a folosit Gliffy: Online Diagram Software and Flowchart Software, disponibil la adresa http://www.gliffy.com/
Codul sursă
BaseWindow.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ACE_UCV_EditorImagini
{
public class BaseWindow : Window, INotifyPropertyChanged
{
public BaseWindow()
{
DataContext = this;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
BitonalWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.BitonalWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="Bitonal" Height="412" Width="374">
<Window.Resources>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
BorderThickness="1"
Padding="4,2"
BorderBrush="DarkGray"
CornerRadius="3"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*" />
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="40*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20*" />
<RowDefinition Height="50*" />
<RowDefinition Height="90*" />
<RowDefinition Height="50*" />
<RowDefinition Height="90*" />
<RowDefinition Height="56*" />
</Grid.RowDefinitions>
<Label Grid.Row="1" Content="Dark Color" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Label Grid.Row="3" Content="Light Color" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Label Content="Range" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="29" FontWeight="Bold" />
<TextBlock Text="{Binding Path=Range}" Grid.Row="1" Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
<Slider Grid.Row="2" Grid.Column="2" Orientation="Vertical" Grid.RowSpan="3" HorizontalAlignment="Center"
Value="{Binding Path=Range, Mode=TwoWay}" Maximum="765" Margin="20,0"></Slider>
<Button Grid.Row="5" Content="OK" VerticalAlignment="Center" HorizontalAlignment="Right" Width="100" UseLayoutRounding="False" Click="okButton_Click" />
<Button Grid.Row="5" Grid.Column="2" Content="Cancel" VerticalAlignment="Center" HorizontalAlignment="Left" Width="100" Click="cancelButton_Click" />
<Border BorderThickness="2" BorderBrush="Black" Grid.Row="2" Width="85" Height="85" CornerRadius="12" MouseDown="Canvas_MouseDown" Background="{Binding Path=DarkColorSolidBrush}" ToolTip="Choose the color to replace" />
<Border BorderThickness="2" BorderBrush="Black" Grid.Row="4" Width="85" Height="85" CornerRadius="12" MouseDown="SelectReplaceColor_MouseDown" Background="{Binding Path=LightColorSolidBrush}" ToolTip="Choose the replacing color" />
<Button Style="{StaticResource MyButtonStyle}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="30" Height="50" BorderBrush="#00000000" ToolTip="Color picker" Click="Button_Click">
<Button.Background>
<ImageBrush ImageSource="file:///C:/Users/Iulia-PC/Desktop/ACE_UCV_EditorImagini_29.04.14/ACE_UCV_EditorImagini/ACE_UCV_EditorImagini/Resources/icon.jpg" />
</Button.Background>
</Button>
<Button Style="{StaticResource MyButtonStyle}" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="30" Height="50" BorderBrush="#00000000" ToolTip="Color picker">
<Button.Background>
<ImageBrush ImageSource="file:///C:/Users/Iulia-PC/Desktop/ACE_UCV_EditorImagini_29.04.14/ACE_UCV_EditorImagini/ACE_UCV_EditorImagini/Resources/icon.jpg" />
</Button.Background>
</Button>
</Grid>
</local:BaseWindow>
BitonalWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for ColorSubstitutionWindow.xaml
/// </summary>
public partial class BitonalWindow : BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Members
private Color _darkColor;
private Color _lightColor;
private Color _initialDarkColor;
private Color _initialLightColor;
private int _range;
private ColorPickerWindow _colorPickerWindow;
private bool _isSelectingDarkColor = false;
private bool _isSelectingLightColor = false;
private Result _result;
#endregion
#region Properties
public Color DarkColor
{
get { return _darkColor; }
set
{
_darkColor = value;
NotifyPropertyChanged("DarkColor");
NotifyPropertyChanged("DarkColorSolidBrush");
}
}
public SolidColorBrush DarkColorSolidBrush
{
get { return new SolidColorBrush(DarkColor); }
}
public Color LightColor
{
get { return _lightColor; }
set
{
_lightColor = value;
NotifyPropertyChanged("LightColor");
NotifyPropertyChanged("LightColorSolidBrush");
}
}
public SolidColorBrush LightColorSolidBrush
{
get { return new SolidColorBrush(LightColor); }
}
public int Range
{
get { return _range; }
set
{
_range = value;
NotifyPropertyChanged("Range");
}
}
public Result DialogResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("Result");
}
}
#endregion
#region Constructor
public BitonalWindow()
{
this.InitializeComponent();
LightColor = _initialLightColor = Colors.White;
DarkColor = _initialDarkColor = Colors.Black;
Range = 100;
}
#endregion
#region Events
private void okButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.Cancel;
this.Close();
}
#endregion
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
_isSelectingDarkColor = true;
_colorPickerWindow = new ColorPickerWindow();
_colorPickerWindow.PropertyChanged += new PropertyChangedEventHandler(_colorPickerWindow_PropertyChanged);
_colorPickerWindow.Closing += new CancelEventHandler(_colorPickerWindow_Closing);
_colorPickerWindow.ShowDialog();
}
#region ColorPickerWindow Events
private void _colorPickerWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var colorPicker = sender as ColorPickerWindow;
if (e.PropertyName == "SelectedColor")
{
if (_isSelectingDarkColor)
{
DarkColor = colorPicker.SelectedColor;
}
else if (_isSelectingLightColor)
{
LightColor = colorPicker.SelectedColor;
}
}
}
private void _colorPickerWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var colorPicker = sender as ColorPickerWindow;
if (colorPicker.ColorPickerResult == ColorPickerWindow.Result.OK)
{
_initialLightColor = LightColor;
_initialDarkColor = DarkColor;
}
else
{
LightColor = _initialLightColor;
DarkColor = _initialDarkColor;
}
if (_isSelectingLightColor) _isSelectingLightColor = false;
if (_isSelectingDarkColor) _isSelectingDarkColor = false;
}
#endregion
private void Button_Click(object sender, RoutedEventArgs e)
{
}
private void SelectReplaceColor_MouseDown(object sender, MouseButtonEventArgs e)
{
_isSelectingLightColor = true;
_colorPickerWindow = new ColorPickerWindow();
_colorPickerWindow.PropertyChanged += new PropertyChangedEventHandler(_colorPickerWindow_PropertyChanged);
_colorPickerWindow.Closing += new CancelEventHandler(_colorPickerWindow_Closing);
_colorPickerWindow.ShowDialog();
}
}
}
ColorBalanceWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.ColorBalanceWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="Color Balance" Height="260" Width="308">
<Grid Height="275">
<Grid.RowDefinitions>
<RowDefinition Height="35*" />
<RowDefinition Height="37*" />
<RowDefinition Height="40*" />
<RowDefinition Height="40*" />
<RowDefinition Height="34*" />
<RowDefinition Height="89*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="72*" />
<ColumnDefinition Width="57*" />
<ColumnDefinition Width="29*" />
<ColumnDefinition Width="78*" />
<ColumnDefinition Width="49*" />
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Red" Grid.Row="1" FontWeight="Bold" />
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Green" Grid.Row="2" FontWeight="Bold" />
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Blue" Grid.Row="3" FontWeight="Bold" />
<Slider Value="{Binding Path=RedValue}" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Minimum="0" Maximum="255" VerticalAlignment="Center"/>
<Slider Value="{Binding Path=GreenValue}" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Minimum="0" Maximum="255" VerticalAlignment="Center"/>
<Slider Value="{Binding Path=BlueValue}" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" Minimum="0" Maximum="255" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Path=RedValue}" Grid.Row="1" Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Path=GreenValue}" Grid.Row="2" Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding Path=BlueValue}" Grid.Row="3" Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Button Content="Cancel" Grid.Row="5" Grid.Column="3" Height="23" HorizontalAlignment="Left" Name="cancelButton" VerticalAlignment="Top" Width="75" Click="cancelButton_Click" />
<Button Content="OK" Grid.Row="5" Height="23" HorizontalAlignment="Right" Name="okButton" VerticalAlignment="Top" Width="75" Click="okButton_Click" Grid.ColumnSpan="2" />
</Grid>
</local:BaseWindow>
ColorBalanceWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for BrightnessWindow.xaml
/// </summary>
public partial class ColorBalanceWindow: BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Fields
private int _redValue;
private int _greenValue;
private int _blueValue;
private Result _result;
#endregion
#region Properties
public int RedValue
{
get { return _redValue; }
set
{
_redValue = value;
NotifyPropertyChanged("RedValue");
}
}
public int GreenValue
{
get { return _greenValue; }
set
{
_greenValue = value;
NotifyPropertyChanged("GreenValue");
}
}
public int BlueValue
{
get { return _blueValue; }
set
{
_blueValue = value;
NotifyPropertyChanged("BlueValue");
}
}
public Result SliderResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("SliderResult");
}
}
public String WindowTitle
{
get { return this.Title; }
set
{
this.Title = value;
NotifyPropertyChanged("SliderResult");
}
}
#endregion
#region Constructor
public ColorBalanceWindow()
{
this.InitializeComponent();
this.SliderResult = Result.Cancel;
this.RedValue = this.GreenValue = this.BlueValue = 255;
}
#endregion
private void okButton_Click(object sender, RoutedEventArgs e)
{
SliderResult = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
SliderResult = Result.Cancel;
this.Close();
}
}
}
ColorPickerWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.ColorPickerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="ColorPicker" Height="173" Width="300" Icon="/ACE_UCV_EditorImagini;component/Resources/colorpicker.png">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="134*" />
<ColumnDefinition Width="13*" />
<ColumnDefinition Width="131*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="98*" />
<RowDefinition Height="36*" />
</Grid.RowDefinitions>
<xctk:ColorPicker Name="ColorPicker1" SelectedColor="{Binding Path=SelectedColor}" DisplayColorAndName="True" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Button Content="OK" HorizontalAlignment="Right" Height="23" Name="okButton" VerticalAlignment="Top" Width="75" Grid.Row="1" Click="okButton_Click" />
<Button Content="Cancel" HorizontalAlignment="Left" Height="23" Name="cancelButton" VerticalAlignment="Top" Width="75" Grid.Row="1" Grid.Column="2" Click="cancelButton_Click" />
</Grid>
</local:BaseWindow>
ColorPickerWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class ColorPickerWindow : BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Members
private Color _color;
private Result _result;
#endregion
#region Properties
public Color SelectedColor
{
get { return _color; }
set
{
_color = value;
NotifyPropertyChanged("SelectedColor");
}
}
public Result ColorPickerResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("Result");
}
}
#endregion
#region Constructor
public ColorPickerWindow()
{
this.InitializeComponent();
SelectedColor = Colors.Black;
}
#endregion
#region Events
private void okButton_Click(object sender, RoutedEventArgs e)
{
SelectedColor = ColorPicker1.SelectedColor;
_result = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.Cancel;
this.Close();
}
#endregion
}
}
ColorSubstitutionWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.ColorSubstitutionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="ColorSubstitution" Height="412" Width="374">
<Window.Resources>
<!– This style is used for buttons, to remove the WPF default 'animated' mouse over effect –>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
BorderThickness="1"
Padding="4,2"
BorderBrush="DarkGray"
CornerRadius="3"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40*" />
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="40*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20*" />
<RowDefinition Height="50*" />
<RowDefinition Height="90*" />
<RowDefinition Height="50*" />
<RowDefinition Height="90*" />
<RowDefinition Height="56*" />
</Grid.RowDefinitions>
<Label Grid.Row="1" Content="Color to replace" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Label Grid.Row="3" Content="Replacing color" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Label Content="Range(%)" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Height="29" FontWeight="Bold" />
<TextBlock Text="{Binding Path=Range}" Grid.Row="1" Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
<Slider Grid.Row="2" Grid.Column="2" Orientation="Vertical" Grid.RowSpan="3" HorizontalAlignment="Center"
Value="{Binding Path=Range, Mode=TwoWay}" Maximum="100" Margin="20,0"></Slider>
<Button Grid.Row="5" Content="OK" VerticalAlignment="Center" HorizontalAlignment="Right" Width="100" UseLayoutRounding="False" Click="okButton_Click" />
<Button Grid.Row="5" Grid.Column="2" Content="Cancel" VerticalAlignment="Center" HorizontalAlignment="Left" Width="100" Click="cancelButton_Click" />
<Border BorderThickness="2" BorderBrush="Black" Grid.Row="2" Width="85" Height="85" CornerRadius="12" MouseDown="Canvas_MouseDown" Background="{Binding Path=ColorToSubtituteSolidBrush}" ToolTip="Choose the color to replace" />
<Border BorderThickness="2" BorderBrush="Black" Grid.Row="4" Width="85" Height="85" CornerRadius="12" MouseDown="SelectReplaceColor_MouseDown" Background="{Binding Path=ReplacingColorSolidBrush}" ToolTip="Choose the replacing color" />
<Button Style="{StaticResource MyButtonStyle}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="30" Height="50" BorderBrush="#00000000" ToolTip="Color picker" Click="Button_Click">
<Button.Background>
<ImageBrush ImageSource="file:///C:/Users/Iulia-PC/Desktop/ACE_UCV_EditorImagini_29.04.14/ACE_UCV_EditorImagini/ACE_UCV_EditorImagini/Resources/icon.jpg" />
</Button.Background>
</Button>
<Button Style="{StaticResource MyButtonStyle}" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="30" Height="50" BorderBrush="#00000000" ToolTip="Color picker">
<Button.Background>
<ImageBrush ImageSource="file:///C:/Users/Iulia-PC/Desktop/ACE_UCV_EditorImagini_29.04.14/ACE_UCV_EditorImagini/ACE_UCV_EditorImagini/Resources/icon.jpg" />
</Button.Background>
</Button>
</Grid>
</local:BaseWindow>
ColorSubstitutionWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for ColorSubstitutionWindow.xaml
/// </summary>
public partial class ColorSubstitutionWindow : BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Members
private Color _colorToSubstitute;
private Color _replacingColor;
private int _range;
private ColorPickerWindow _colorPickerWindow;
private bool _isSelectingColorToSubtitute = false;
private bool _isSelectingReplacingColor = false;
private Result _result;
#endregion
#region Properties
public Color ColorToSubtitute
{
get { return _colorToSubstitute; }
set
{
_colorToSubstitute = value;
NotifyPropertyChanged("ColorToSubtitute");
NotifyPropertyChanged("ColorToSubtituteSolidBrush");
}
}
public SolidColorBrush ColorToSubtituteSolidBrush
{
get { return new SolidColorBrush(ColorToSubtitute); }
}
public Color ReplacingColor
{
get { return _replacingColor; }
set
{
_replacingColor = value;
NotifyPropertyChanged("ReplacingColor");
NotifyPropertyChanged("ReplacingColorSolidBrush");
}
}
public SolidColorBrush ReplacingColorSolidBrush
{
get { return new SolidColorBrush(ReplacingColor); }
}
public int Range
{
get { return _range; }
set
{
_range = value;
NotifyPropertyChanged("Range");
}
}
public Result DialogResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("Result");
}
}
#endregion
#region Constructor
public ColorSubstitutionWindow()
{
this.InitializeComponent();
ColorToSubtitute = Colors.White;
ReplacingColor = Colors.White;
}
#endregion
#region Events
private void okButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.Cancel;
this.Close();
}
#endregion
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
_isSelectingColorToSubtitute = true;
_colorPickerWindow = new ColorPickerWindow();
_colorPickerWindow.PropertyChanged += new PropertyChangedEventHandler(_colorPickerWindow_PropertyChanged);
_colorPickerWindow.Closing += new CancelEventHandler(_colorPickerWindow_Closing);
_colorPickerWindow.ShowDialog();
}
#region ColorPickerWindow Events
private void _colorPickerWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var colorPicker = sender as ColorPickerWindow;
if (e.PropertyName == "SelectedColor")
{
if (_isSelectingColorToSubtitute)
{
ColorToSubtitute = colorPicker.SelectedColor;
}
else if (_isSelectingReplacingColor)
{
ReplacingColor = colorPicker.SelectedColor;
}
}
}
private void _colorPickerWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var colorPicker = sender as ColorPickerWindow;
if (colorPicker.ColorPickerResult == ColorPickerWindow.Result.OK)
{
}
if (_isSelectingColorToSubtitute) _isSelectingColorToSubtitute = false;
if (_isSelectingReplacingColor) _isSelectingReplacingColor = false;
}
#endregion
private void Button_Click(object sender, RoutedEventArgs e)
{
}
private void SelectReplaceColor_MouseDown(object sender, MouseButtonEventArgs e)
{
_isSelectingReplacingColor = true;
_colorPickerWindow = new ColorPickerWindow();
_colorPickerWindow.PropertyChanged += new PropertyChangedEventHandler(_colorPickerWindow_PropertyChanged);
_colorPickerWindow.Closing += new CancelEventHandler(_colorPickerWindow_Closing);
_colorPickerWindow.ShowDialog();
}
}
}
LaplacianOfGaussianWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.CustomControls.LaplacianOfGaussianWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="LaplacianOfGaussian" Height="254" Width="367">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="148*" />
<ColumnDefinition Width="32*" />
<ColumnDefinition Width="62*" />
<ColumnDefinition Width="103*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30*" />
<RowDefinition Height="25*" />
<RowDefinition Height="25*" />
<RowDefinition Height="25*" />
<RowDefinition Height="25*" />
<RowDefinition Height="25*" />
<RowDefinition Height="25*" />
<RowDefinition Height="55*" />
</Grid.RowDefinitions>
<Button Grid.Row="7" Content="OK" Name="okButton" Click="okButton_Click" Height="20" Width="80" HorizontalAlignment="Right" />
<Button Grid.Row="7" Grid.Column="2" Content="Cancel" Name="CancelButton" Click="cancelButton_Click" Height="20" Width="80" HorizontalAlignment="Left" Grid.ColumnSpan="2" />
<Label Content="LoG type:" VerticalAlignment="Bottom" HorizontalAlignment="Right" FontWeight="Bold" Margin="0,0,16,0" />
<RadioButton Content="Simple" Grid.Column="1" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsSimple}" Grid.ColumnSpan="3" Margin="0,0,83,0" />
<RadioButton Content="L(3×3)G(3×3)" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsL3x3G3x3}" Grid.Row="1" />
<RadioButton Content="L(3×3)G(5×5) a" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsL3x3G5x5a}" Grid.Row="2" />
<RadioButton Content="L(3×3)G(5×5) b" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsL3x3G5x5b}" Grid.Row="3" />
<RadioButton Content="L(5×5)G(3×3)" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsL5x5G3x3}" Grid.Row="4" />
<RadioButton Content="L(5×5)G(5×5) a" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsL5x5G5x5a}" Grid.Row="5"/>
<RadioButton Content="L(5×5)G(5×5) b" Grid.Column="1" Grid.ColumnSpan="3" VerticalAlignment="Bottom" IsChecked="{Binding Path=IsL5x5G5x5b}" Grid.Row="6"/>
</Grid>
</local:BaseWindow>
LaplacianOfGaussianWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ACE_UCV_EditorImagini.CustomControls
{
/// <summary>
/// Interaction logic for LaplacianWindow.xaml
/// </summary>
public partial class LaplacianOfGaussianWindow : BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Fields
private Result _result;
private bool _isSimple;
private bool _isL3x3G3x3;
private bool _isL3x3G5x5a;
private bool _isL3x3G5x5b;
private bool _isL5x5G3x3;
private bool _isL5x5G5x5a;
private bool _isL5x5G5x5b;
#endregion
#region Properties
public Result WindowResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("WindowResult");
}
}
public bool IsSimple
{
get { return _isSimple; }
set
{
_isSimple = value;
NotifyPropertyChanged("IsSimple");
}
}
public bool IsL3x3G3x3
{
get { return _isL3x3G3x3; }
set
{
_isL3x3G3x3 = value;
NotifyPropertyChanged("IsL3x3G3x3");
}
}
public bool IsL3x3G5x5a
{
get { return _isL3x3G5x5a; }
set
{
_isL3x3G5x5a = value;
NotifyPropertyChanged("IsL3x3G5x5a");
}
}
public bool IsL3x3G5x5b
{
get { return _isL3x3G5x5b; }
set
{
_isL3x3G5x5b = value;
NotifyPropertyChanged("IsL3x3G5x5b");
}
}
public bool IsL5x5G3x3
{
get { return _isL5x5G3x3; }
set
{
_isL5x5G3x3 = value;
NotifyPropertyChanged("IsL5x5G3x3");
}
}
public bool IsL5x5G5x5a
{
get { return _isL5x5G5x5a; }
set
{
_isL5x5G5x5a = value;
NotifyPropertyChanged("IsL5x5G5x5a");
}
}
public bool IsL5x5G5x5b
{
get { return _isL5x5G5x5b; }
set
{
_isL5x5G5x5b = value;
NotifyPropertyChanged("IsL5x5G5x5b");
}
}
#endregion
public LaplacianOfGaussianWindow()
{
InitializeComponent();
IsSimple = true;
WindowResult = Result.Cancel;
}
private void okButton_Click(object sender, RoutedEventArgs e)
{
WindowResult = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
WindowResult = Result.Cancel;
this.Close();
}
}
}
LaplacianWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.CustomControls.LaplacianWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="Laplacian" Height="190" Width="367">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="148*" />
<ColumnDefinition Width="32*" />
<ColumnDefinition Width="165*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="66*" />
<RowDefinition Height="46*" />
<RowDefinition Height="39*" />
</Grid.RowDefinitions>
<Button Grid.Row="2" Grid.Column="0" Content="OK" Name="okButton" Click="okButton_Click" Height="20" Width="80" HorizontalAlignment="Right" />
<Button Grid.Row="2" Grid.Column="2" Content="Cancel" Name="CancelButton" Click="cancelButton_Click" Grid.ColumnSpan="2" Height="20" Width="80" HorizontalAlignment="Left" />
<Label Content="Laplacian type:" VerticalAlignment="Center" HorizontalAlignment="Right" FontWeight="Bold" />
<RadioButton Content="3×3" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" IsChecked="{Binding Path=Is3x3}"/>
<RadioButton Content="5×5" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Bottom" IsChecked="{Binding Path=Is5x5}"/>
<CheckBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Content="Gray" VerticalAlignment="Center" IsChecked="{Binding Path=IsGray}"/>
</Grid>
</local:BaseWindow>
LaplacianWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ACE_UCV_EditorImagini.CustomControls
{
/// <summary>
/// Interaction logic for LaplacianWindow.xaml
/// </summary>
public partial class LaplacianWindow : BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Fields
private Result _result;
private bool _is3x3;
private bool _is5x5;
private bool _isGray;
#endregion
#region Properties
public Result WindowResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("WindowResult");
}
}
public bool Is3x3
{
get { return _is3x3; }
set
{
_is3x3 = value;
NotifyPropertyChanged("Is3x3");
}
}
public bool Is5x5
{
get { return _is5x5; }
set
{
_is5x5 = value;
NotifyPropertyChanged("Is5x5");
}
}
public bool IsGray
{
get { return _isGray; }
set
{
_isGray = value;
NotifyPropertyChanged("IsGray");
}
}
#endregion
public LaplacianWindow()
{
InitializeComponent();
Is3x3 = true;
Is5x5 = false;
WindowResult = Result.Cancel;
}
private void okButton_Click(object sender, RoutedEventArgs e)
{
WindowResult = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
WindowResult = Result.Cancel;
this.Close();
}
}
}
ResizeWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.ResizeWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="Resize" Height="269" Width="300" Icon="/ACE_UCV_EditorImagini;component/Resources/resize.png">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="40"/>
<RowDefinition Height="30"/>
<RowDefinition Height="35*" />
<RowDefinition Height="35*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="1" Content="Width:" HorizontalAlignment="Right" FontWeight="Bold" />
<Label Grid.Row="2" Content="Height:" HorizontalAlignment="Right" FontWeight="Bold" />
<Border Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" CornerRadius="4" Height="28" Width="85" BorderThickness="2" BorderBrush="#62000000">
<xctk:IntegerUpDown Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Name="widthIntegerUpDown" Value="{Binding Path=DataContext.ResizeWidth}" Height="25" Width="80" BorderBrush="#00000000" />
</Border>
<Border Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" CornerRadius="4" Height="28" Width="85" BorderThickness="2" BorderBrush="#62000000">
<xctk:IntegerUpDown Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Name="heightIntegerUpDown" Value="{Binding Path=DataContext.ResizeHeight}" Height="25" Width="80" BorderBrush="#00000000" />
</Border>
<Label Grid.Row="1" Grid.Column="4" Content="pixels" HorizontalAlignment="Left"/>
<Label Grid.Row="2" Grid.Column="4" Content="pixels" HorizontalAlignment="Left"/>
<Button Grid.Row="6" Grid.Column="0" Content="OK" Name="okButton" Click="okButton_Click" Grid.ColumnSpan="2" Height="20" Width="80" HorizontalAlignment="Right" />
<Button Grid.Row="6" Grid.Column="3" Content="Cancel" Name="CancelButton" Click="cancelButton_Click" Grid.ColumnSpan="2" Height="20" Width="80" HorizontalAlignment="Left" />
<Label Grid.Row="4" Content="Resampling:" HorizontalAlignment="Right" FontWeight="Bold" />
<ComboBox Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="4" Height="23" HorizontalAlignment="Left" Name="comboBox1" VerticalAlignment="Center" Width="130" SelectedIndex="3" SelectionChanged="comboBox1_SelectionChanged">
<ComboBoxItem Content="Best Quality" />
<ComboBoxItem Content="Bicubic" />
<ComboBoxItem Content="Bilinear" />
<ComboBoxItem Content="Nearest Neighbor" />
</ComboBox>
</Grid>
</local:BaseWindow>
ResizeWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Drawing.Drawing2D;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for ResizeWindow.xaml
/// </summary>
public partial class ResizeWindow : BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Fields
private Result _result;
private int _height;
private int _width;
private InterpolationMode _resampling;
#endregion
#region Properties
public Result ResizeResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("ResizeResult");
}
}
public int ResizeHeight
{
get { return _height; }
set { _height = value; }
}
public int ResizeWidth
{
get { return _width; }
set { _width = value; }
}
public InterpolationMode Resampling
{
get { return _resampling; }
set { _resampling = value; }
}
#endregion
public ResizeWindow()
{
InitializeComponent();
}
public ResizeWindow(int startWidth, int startHeight)
{
InitializeComponent();
widthIntegerUpDown.Value = startWidth;
heightIntegerUpDown.Value = startHeight;
}
private void okButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
_result = Result.Cancel;
this.Close();
}
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch ((e.AddedItems[0] as ComboBoxItem).Content as string)
{
case "Nearest Neighbor":
Resampling = InterpolationMode.NearestNeighbor;
break;
case "Bilinear":
Resampling = InterpolationMode.Bilinear;
break;
case "Bicubic":
Resampling = InterpolationMode.Bicubic;
break;
case "Best Quality":
Resampling = InterpolationMode.High;
break;
}
}
}
}
SliderWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.SliderWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="{Binding Path=WindowTitle}" Height="158" Width="300">
<Grid Height="126">
<Grid.RowDefinitions>
<RowDefinition Height="28*" />
<RowDefinition Height="57*" />
<RowDefinition Height="41*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="126*" />
<ColumnDefinition Width="28*" />
<ColumnDefinition Width="124*" />
</Grid.ColumnDefinitions>
<Slider Name ="slider1" Minimum="-255" Maximum="255" TickFrequency="1" Grid.ColumnSpan="3" Grid.Row="1" Value="{Binding Path=SliderValue}" />
<Button Content="Cancel" Grid.Row="2" Grid.Column="2" Height="23" HorizontalAlignment="Left" Name="cancelButton" VerticalAlignment="Top" Width="75" Click="cancelButton_Click" />
<Button Content="OK" Grid.Row="2" Height="23" HorizontalAlignment="Right" Name="okButton" VerticalAlignment="Top" Width="75" Click="okButton_Click" />
</Grid>
</local:BaseWindow>
SliderWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for BrightnessWindow.xaml
/// </summary>
public partial class SliderWindow: BaseWindow
{
public enum Result
{
OK,
Cancel,
}
#region Fields
private int _sliderValue;
private Result _result;
#endregion
#region Properties
public int SliderValue
{
get { return _sliderValue; }
set
{
_sliderValue = value;
NotifyPropertyChanged("SliderValue");
}
}
public Result SliderResult
{
get { return _result; }
set
{
_result = value;
NotifyPropertyChanged("SliderResult");
}
}
public String WindowTitle
{
get { return this.Title; }
set
{
this.Title = value;
NotifyPropertyChanged("SliderResult");
}
}
#endregion
#region Constructor
public SliderWindow()
{
this.InitializeComponent();
this.SliderResult = Result.Cancel;
this.SliderValue = 0;
}
public SliderWindow(int minVal, int maxVal)
{
this.InitializeComponent();
this.slider1.Minimum = minVal;
this.slider1.Maximum = maxVal;
this.SliderValue = (maxVal + minVal) / 2;
this.SliderResult = Result.Cancel;
}
public SliderWindow(string windowTitle, int minVal, int maxVal)
{
this.InitializeComponent();
WindowTitle = windowTitle;
this.slider1.Minimum = minVal;
this.slider1.Maximum = maxVal;
this.SliderValue = (maxVal + minVal) / 2;
this.SliderResult = Result.Cancel;
}
#endregion
#region Events
private void brightnessSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
SliderValue = (int)e.NewValue;
}
#endregion
private void okButton_Click(object sender, RoutedEventArgs e)
{
SliderResult = Result.OK;
this.Close();
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
SliderResult = Result.Cancel;
this.Close();
}
}
}
BitmapExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
namespace ACE_UCV_EditorImagini
{
public static class BitmapExtension
{
public static byte[] GetByteArray(this Bitmap bitmap)
{
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] bitmapBuffer = new byte[bitmapData.Stride * bitmapData.Height];
Marshal.Copy(bitmapData.Scan0, bitmapBuffer, 0, bitmapBuffer.Length);
bitmap.UnlockBits(bitmapData);
return bitmapBuffer;
}
public static Bitmap GetBitmap(this byte[] pixelBuffer, int width, int height)
{
Bitmap bitmap = new Bitmap(width, height);
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(pixelBuffer, 0, bitmapData.Scan0, pixelBuffer.Length);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
}
}
Matrix.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ACE_UCV_EditorImagini
{
public static class Matrix
{
public static double[,] Laplacian_3x3
{
get
{
return new double[,]
{ { -1, -1, -1, },
{ -1, 8, -1, },
{ -1, -1, -1, }, };
}
}
public static double[,] Laplacian_5x5
{
get
{
return new double[,]
{ { -1, -1, -1, -1, -1, },
{ -1, -1, -1, -1, -1, },
{ -1, -1, 24, -1, -1, },
{ -1, -1, -1, -1, -1, },
{ -1, -1, -1, -1, -1 }, };
}
}
public static double[,] Laplacian_Of_Gaussian
{
get
{
return new double[,]
{ { 0, 0, -1, 0, 0 },
{ 0, -1, -2, -1, 0 },
{ -1, -2, 16, -2, -1 },
{ 0, -1, -2, -1, 0 },
{ 0, 0, -1, 0, 0 }, };
}
}
public static double[,] Gaussian_3x3
{
get
{
return new double[,]
{ { 1, 2, 1, },
{ 2, 4, 2, },
{ 1, 2, 1, }, };
}
}
public static double[,] Gaussian_5x5_Type1
{
get
{
return new double[,]
{ { 2, 4, 5, 4, 2 },
{ 4, 9, 12, 9, 4 },
{ 5, 12, 15, 12, 5 },
{ 4, 9, 12, 9, 4 },
{ 2, 4, 5, 4, 2 }, };
}
}
public static double[,] Gaussian_5x5_Type2
{
get
{
return new double[,]
{ { 1, 4, 6, 4, 1 },
{ 4, 16, 24, 16, 4 },
{ 6, 24, 36, 24, 6 },
{ 4, 16, 24, 16, 4 },
{ 1, 4, 6, 4, 1 }, };
}
}
public static double[,] Sobel_3x3_Horizontal
{
get
{
return new double[,]
{ { -1, 0, 1, },
{ -2, 0, 2, },
{ -1, 0, 1, }, };
}
}
public static double[,] Sobel_3x3_Vertical
{
get
{
return new double[,]
{ { 1, 2, 1, },
{ 0, 0, 0, },
{ -1, -2, -1, }, };
}
}
public static double[,] Prewitt_3x3_Horizontal
{
get
{
return new double[,]
{ { -1, 0, 1, },
{ -1, 0, 1, },
{ -1, 0, 1, }, };
}
}
public static double[,] Prewitt_3x3_Vertical
{
get
{
return new double[,]
{ { 1, 1, 1, },
{ 0, 0, 0, },
{ -1, -1, -1, }, };
}
}
public static double[,] Kirsch_3x3_Horizontal
{
get
{
return new double[,]
{ { 5, 5, 5, },
{ -3, 0, -3, },
{ -3, -3, -3, }, };
}
}
public static double[,] Kirsch_3x3_Vertical
{
get
{
return new double[,]
{ { 5, -3, -3, },
{ 5, 0, -3, },
{ 5, -3, -3, }, };
}
}
public static double[,] Mean_3x3
{
get
{
return new double[,]
{ { 1, 1, 1, },
{ 1, 1, 1, },
{ 1, 1, 1, }, };
}
}
public static double[,] Mean_5x5
{
get
{
return new double[,]
{ { 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1}, };
}
}
public static double[,] Mean_7x7
{
get
{
return new double[,]
{ { 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1}, };
}
}
public static double[,] Mean_9x9
{
get
{
return new double[,]
{ { 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1}, };
}
}
public static double[,] GaussianBlur_3x3
{
get
{
return new double[,]
{ { 1, 2, 1, },
{ 2, 4, 2, },
{ 1, 2, 1, }, };
}
}
public static double[,] Gaussian_5x5
{
get
{
return new double[,]
{ { 2, 04, 05, 04, 2 },
{ 4, 09, 12, 09, 4 },
{ 5, 12, 15, 12, 5 },
{ 4, 09, 12, 09, 4 },
{ 2, 04, 05, 04, 2 }, };
}
}
public static double[,] Motion_5x5
{
get
{
return new double[,]
{ { 1, 0, 0, 0, 1},
{ 0, 1, 0, 1, 0},
{ 0, 0, 1, 0, 0},
{ 0, 1, 0, 1, 0},
{ 1, 0, 0, 0, 1}, };
}
}
public static double[,] Motion_5x5_45Degrees
{
get
{
return new double[,]
{ { 0, 0, 0, 0, 1},
{ 0, 0, 0, 1, 0},
{ 0, 0, 1, 0, 0},
{ 0, 1, 0, 0, 0},
{ 1, 0, 0, 0, 0}, };
}
}
public static double[,] Motion_5x5_135Degrees
{
get
{
return new double[,]
{ { 1, 0, 0, 0, 0},
{ 0, 1, 0, 0, 0},
{ 0, 0, 1, 0, 0},
{ 0, 0, 0, 1, 0},
{ 0, 0, 0, 0, 1}, };
}
}
public static double[,] Motion_7x7
{
get
{
return new double[,]
{ { 1, 0, 0, 0, 0, 0, 1},
{ 0, 1, 0, 0, 0, 1, 0},
{ 0, 0, 1, 0, 1, 0, 0},
{ 0, 0, 0, 1, 0, 0, 0},
{ 0, 0, 1, 0, 1, 0, 0},
{ 0, 1, 0, 0, 0, 1, 0},
{ 1, 0, 0, 0, 0, 0, 1}, };
}
}
public static double[,] Motion_7x7_45Degrees
{
get
{
return new double[,]
{ { 0, 0, 0, 0, 0, 0, 1},
{ 0, 0, 0, 0, 0, 1, 0},
{ 0, 0, 0, 0, 1, 0, 0},
{ 0, 0, 0, 1, 0, 0, 0},
{ 0, 0, 1, 0, 0, 0, 0},
{ 0, 1, 0, 0, 0, 0, 0},
{ 1, 0, 0, 0, 0, 0, 0}, };
}
}
public static double[,] Motion_7x7_135Degrees
{
get
{
return new double[,]
{ { 1, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 0, 0, 0, 0},
{ 0, 0, 1, 0, 0, 0, 0},
{ 0, 0, 0, 1, 0, 0, 0},
{ 0, 0, 0, 0, 1, 0, 0},
{ 0, 0, 0, 0, 0, 1, 0},
{ 0, 0, 0, 0, 0, 0, 1}, };
}
}
public static double[,] Motion_9x9
{
get
{
return new double[,]
{ {1, 0, 0, 0, 0, 0, 0, 0, 1,},
{0, 1, 0, 0, 0, 0, 0, 1, 0,},
{0, 0, 1, 0, 0, 0, 1, 0, 0,},
{0, 0, 0, 1, 0, 1, 0, 0, 0,},
{0, 0, 0, 0, 1, 0, 0, 0, 0,},
{0, 0, 0, 1, 0, 1, 0, 0, 0,},
{0, 0, 1, 0, 0, 0, 1, 0, 0,},
{0, 1, 0, 0, 0, 0, 0, 1, 0,},
{1, 0, 0, 0, 0, 0, 0, 0, 1,}, };
}
}
public static double[,] Motion_9x9_45Degrees
{
get
{
return new double[,]
{ {0, 0, 0, 0, 0, 0, 0, 0, 1,},
{0, 0, 0, 0, 0, 0, 0, 1, 0,},
{0, 0, 0, 0, 0, 0, 1, 0, 0,},
{0, 0, 0, 0, 0, 1, 0, 0, 0,},
{0, 0, 0, 0, 1, 0, 0, 0, 0,},
{0, 0, 0, 1, 0, 0, 0, 0, 0,},
{0, 0, 1, 0, 0, 0, 0, 0, 0,},
{0, 1, 0, 0, 0, 0, 0, 0, 0,},
{1, 0, 0, 0, 0, 0, 0, 0, 0,}, };
}
}
public static double[,] Motion_9x9_135Degrees
{
get
{
return new double[,]
{ {1, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 1, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 1, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 1, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 1, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 1, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 1, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 1, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 1,}, };
}
}
}
}
ImageFilters.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
namespace ACE_UCV_EditorImagini
{
public static class ImageFilters
{
private static byte ClipByte(double colour)
{
return (byte)(colour > 255 ? 255 : (colour < 0 ? 0 : colour));
}
public static Bitmap Prepare(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
return sourceBuffer.GetBitmap(width, height);
}
public static Bitmap Grayscale(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
for (int i = 0; i < sourceBuffer.Length; i += 4)
{
byte gray = (byte)(.299 * sourceBuffer[i + 2] + .587 * sourceBuffer[i + 1] + .114 * sourceBuffer[i]);
sourceBuffer[i] = gray;
sourceBuffer[i + 1] = gray;
sourceBuffer[i + 2] = gray;
}
return sourceBuffer.GetBitmap(width, height);
}
public static Bitmap Invert(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
for (int i = 0; i < sourceBuffer.Length; i += 4)
{
sourceBuffer[i] = (byte)(255 – sourceBuffer[i]);
sourceBuffer[i + 1] = (byte)(255 – sourceBuffer[i + 1]);
sourceBuffer[i + 2] = (byte)(255 – sourceBuffer[i + 2]);
}
return sourceBuffer.GetBitmap(width, height);
}
public static Bitmap ColorFilter(Bitmap bitmap, System.Windows.Media.Color color)
{
double rp = (double)color.R / 255;
double gp = (double)color.G / 255;
double bp = (double)color.B / 255;
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
for (int i = 0; i < sourceBuffer.Length; i += 4)
{
byte nPixelR = (byte)(sourceBuffer[i + 2] * rp);
byte nPixelG = (byte)(sourceBuffer[i + 1] * gp);
byte nPixelB = (byte)(sourceBuffer[i] * bp);
sourceBuffer[i] = nPixelB;
sourceBuffer[i + 1] = nPixelG;
sourceBuffer[i + 2] = nPixelR;
}
return sourceBuffer.GetBitmap(width, height);
}
public static Bitmap Brightness(Bitmap bitmap, int brightness)
{
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
for (int i = 0; i < sourceBuffer.Length; i += 4)
{
int cR = sourceBuffer[i + 2] + brightness;
int cG = sourceBuffer[i + 1] + brightness;
int cB = sourceBuffer[i] + brightness;
cR = ClipByte(cR);
cG = ClipByte(cG);
cB = ClipByte(cG);
sourceBuffer[i] = (byte)cB;
sourceBuffer[i + 1] = (byte)cG;
sourceBuffer[i + 2] = (byte)cR;
}
return sourceBuffer.GetBitmap(width, height);
}
public static Bitmap Sepia(Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
byte[] resultBuffer = bitmap.GetByteArray();
for (int i = 0; i < sourceBuffer.Length; i += 4)
{
double cR = (sourceBuffer[i + 2] * .393) + (sourceBuffer[i + 1] * .769) + (sourceBuffer[i] * .189);
double cG = (sourceBuffer[i + 2] * .349) + (sourceBuffer[i + 1] * .686) + (sourceBuffer[i] * .168);
double cB = (sourceBuffer[i + 2] * .272) + (sourceBuffer[i + 1] * .534) + (sourceBuffer[i] * .131);
cR = ClipByte(cR);
cG = ClipByte(cG);
cB = ClipByte(cB);
resultBuffer[i] = (byte)cB;
resultBuffer[i + 1] = (byte)cG;
resultBuffer[i + 2] = (byte)cR;
}
return resultBuffer.GetBitmap(width, height);
}
public static Bitmap Contrast(Bitmap bitmap, double contrastValue)
{
if (contrastValue < -100) contrastValue = -100;
if (contrastValue > 100) contrastValue = 100;
contrastValue = (100.0 + contrastValue) / 100.0;
contrastValue *= contrastValue;
int width = bitmap.Width;
int height = bitmap.Height;
byte[] sourceBuffer = bitmap.GetByteArray();
for (int i = 0; i < sourceBuffer.Length; i += 4)
{
double newRed = sourceBuffer[i + 2] / 255.0;
newRed -= 0.5;
newRed *= contrastValue;
newRed += 0.5;
newRed *= 255;
newRed = ClipByte(newRed);
double newGreen = sourceBuffer[i + 1] / 255.0;
newGreen -= 0.5;
newGreen *= contrastValue;
newGreen += 0.5;
newGreen *= 255;
newGreen = ClipByte(newGreen);
double newBlue = sourceBuffer[i] / 255.0;
newBlue -= 0.5;
newBlue *= contrastValue;
newBlue += 0.5;
newBlue *= 255;
newBlue = ClipByte(newBlue);
sourceBuffer[i] = (byte)newBlue;
sourceBuffer[i + 1] = (byte)newGreen;
sourceBuffer[i + 2] = (byte)newRed;
}
return sourceBuffer.GetBitmap(width, height);
}
public static Bitmap RotateFlip(Bitmap bitmap, RotateFlipType rotateFlipType)
{
Bitmap processedBitmap = (Bitmap)bitmap.Clone();
processedBitmap.RotateFlip(rotateFlipType);
return processedBitmap;
}
public static Bitmap Distortion(Bitmap sourceBitmap, int distortValue)
{
byte[] sourceBuffer = sourceBitmap.GetByteArray();
byte[] resultBuffer = sourceBitmap.GetByteArray();
int stride = sourceBitmap.Width * 4;
int offset = 0, _Y = 0, _X = 0;
int factorMax = (distortValue + 1) * 2;
Random rand = new Random();
for (int k = 0; k + 4 < sourceBuffer.Length; k += 4)
{
_Y = distortValue – rand.Next(0, factorMax);
_X = distortValue – rand.Next(0, factorMax);
if (_X * 4 + (k % stride) < stride && _X * 4 + (k % stride) > 0)
{
offset = k + _Y * stride + 4 * _X;
if (offset >= 0 && offset + 4 < resultBuffer.Length)
{
resultBuffer[offset] = sourceBuffer[k];
resultBuffer[offset + 1] = sourceBuffer[k + 1];
resultBuffer[offset + 2] = sourceBuffer[k + 2];
}
}
}
return resultBuffer.GetBitmap(sourceBitmap.Width, sourceBitmap.Height).Median(3);
}
public static Bitmap OilPaint(Bitmap bitmap, int nr_levels, int filterSize)
{
int stride = bitmap.Width * 4;
int height = bitmap.Height;
int width = bitmap.Width;
byte[] sourceBuffer = bitmap.GetByteArray();
byte[] resultBuffer = bitmap.GetByteArray();
int[] intensityArray = new int[nr_levels];
int[] blueArray = new int[nr_levels];
int[] greenArray = new int[nr_levels];
int[] redArray = new int[nr_levels];
nr_levels = nr_levels – 1;
int filterOffset = (filterSize – 1) / 2;
int byteOffset = 0;
int calcOffset = 0;
int currentIntensity = 0;
int maxIntensity = 0;
int maxIndex = 0;
double blue = 0;
double green = 0;
double red = 0;
for (int offsetY = filterOffset; offsetY < height – filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX < width – filterOffset; offsetX++)
{
blue = green = red = 0;
currentIntensity = maxIntensity = maxIndex = 0;
intensityArray = new int[nr_levels + 1];
blueArray = new int[nr_levels + 1];
greenArray = new int[nr_levels + 1];
redArray = new int[nr_levels + 1];
byteOffset = offsetY * stride + offsetX * 4;
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset + (filterX * 4) + (filterY * stride);
currentIntensity = (int)Math.Round(((double)
(sourceBuffer[calcOffset] +
sourceBuffer[calcOffset + 1] +
sourceBuffer[calcOffset + 2]) / 3.0 *
(nr_levels)) / 255.0);
intensityArray[currentIntensity] += 1;
blueArray[currentIntensity] += sourceBuffer[calcOffset];
greenArray[currentIntensity] += sourceBuffer[calcOffset + 1];
redArray[currentIntensity] += sourceBuffer[calcOffset + 2];
if (intensityArray[currentIntensity] > maxIntensity)
{
maxIntensity = intensityArray[currentIntensity];
maxIndex = currentIntensity;
}
}
}
blue = blueArray[maxIndex] / maxIntensity;
green = greenArray[maxIndex] / maxIntensity;
red = redArray[maxIndex] / maxIntensity;
resultBuffer[byteOffset] = ClipByte(blue);
resultBuffer[byteOffset + 1] = ClipByte(green);
resultBuffer[byteOffset + 2] = ClipByte(red);
resultBuffer[byteOffset + 3] = sourceBuffer[byteOffset + 3];
}
}
return resultBuffer.GetBitmap(width, height);
}
public static Bitmap Solarise(this Bitmap bitmap, byte blue, byte green, byte red)
{
byte[] sourceBuffer = bitmap.GetByteArray();
byte byte_255 = 255;
for (int k = 0; k + 4 < sourceBuffer.Length; k += 4)
{
if (sourceBuffer[k] < blue)
{
sourceBuffer[k] = (byte)(byte_255 – sourceBuffer[k]);
}
if (sourceBuffer[k + 1] < green)
{
sourceBuffer[k + 1] = (byte)(byte_255 – sourceBuffer[k + 1]);
}
if (sourceBuffer[k + 2] < red)
{
sourceBuffer[k + 2] = (byte)(byte_255 – sourceBuffer[k + 2]);
}
}
return sourceBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
public static Bitmap ColorSubstitution(this Bitmap bitmap, System.Windows.Media.Color oldColor, System.Windows.Media.Color newColor, int range)
{
byte[] resultBuffer = bitmap.GetByteArray();
byte R = 0, G = 0, B = 0, A = 0;
int resultRed = 0, resultGreen = 0, resultBlue = 0;
byte newRed = newColor.R;
byte newGreen = newColor.G;
byte newBlue = newColor.B;
byte oldRed = oldColor.R;
byte oldGreen = oldColor.G;
byte oldBlue = oldColor.B;
byte minValue = 0;
byte maxValue = 255;
for (int k = 0; k < resultBuffer.Length; k += 4)
{
A = resultBuffer[k + 3];
if (A != 0)
{
B = resultBuffer[k];
G = resultBuffer[k + 1];
R = resultBuffer[k + 2];
if ((B < oldBlue + range && B > oldBlue – range) &&
(G < oldGreen + range && G > oldGreen – range) &&
(R < oldRed + range && R > oldRed – range))
{
resultBlue = oldBlue – B + newBlue;
if (resultBlue > maxValue) resultBlue = maxValue;
else if (resultBlue < minValue) resultBlue = minValue;
resultGreen = oldGreen – G + newGreen;
if (resultGreen > maxValue) resultGreen = maxValue;
else if (resultGreen < minValue) resultGreen = minValue;
resultRed = oldRed – R + newRed;
if (resultRed > maxValue) resultRed = maxValue;
else if (resultRed < minValue) resultRed = minValue;
resultBuffer[k] = (byte)resultBlue;
resultBuffer[k + 1] = (byte)resultGreen;
resultBuffer[k + 2] = (byte)resultRed;
resultBuffer[k + 3] = A;
}
}
}
return resultBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
public static Bitmap Sharpen(Bitmap image, double strength)
{
var newBimap = image.Clone() as Bitmap;
int width = newBimap.Width;
int height = newBimap.Height;
const int matrixSize = 5;
var matrix = new double[,]
{
{-1, -1, -1, -1, -1},
{-1, 2, 2, 2, -1},
{-1, 2, 16, 2, -1},
{-1, 2, 2, 2, -1},
{-1, -1, -1, -1, -1}
};
double bias = 1.0 – strength;
double factor = strength / 16.0;
const int s = matrixSize / 2;
var result = new Color[newBimap.Width, newBimap.Height];
if (newBimap != null)
{
var sourceBuffer = newBimap.GetByteArray();
int stride = sourceBuffer.Length / height;
int rgb;
for (int x = s; x < width – s; x++)
{
for (int y = s; y < height – s; y++)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < matrixSize; filterX++)
{
for (int filterY = 0; filterY < matrixSize; filterY++)
{
int imageX = (x – s + filterX + width) % width;
int imageY = (y – s + filterY + height) % height;
rgb = imageY * stride + 4 * imageX;
red += sourceBuffer[rgb + 2] * matrix[filterX, filterY];
green += sourceBuffer[rgb + 1] * matrix[filterX, filterY];
blue += sourceBuffer[rgb + 0] * matrix[filterX, filterY];
}
rgb = y * stride + 4 * x;
int r = Math.Min(Math.Max((int)(factor * red + (bias * sourceBuffer[rgb + 2])), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + (bias * sourceBuffer[rgb + 1])), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + (bias * sourceBuffer[rgb + 0])), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
for (int x = s; x < width – s; x++)
{
for (int y = s; y < height – s; y++)
{
rgb = y * stride + 4 * x;
sourceBuffer[rgb + 2] = result[x, y].R;
sourceBuffer[rgb + 1] = result[x, y].G;
sourceBuffer[rgb + 0] = result[x, y].B;
}
}
newBimap = sourceBuffer.GetBitmap(width, height);
}
return newBimap;
}
public static Bitmap ResizeBitmap(Bitmap bitmap, int width, int height,
InterpolationMode interpMode = InterpolationMode.NearestNeighbor)
{
Bitmap result = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = interpMode;
g.DrawImage(bitmap, 0, 0, width, height);
}
return result;
}
public static Bitmap Crop(Bitmap bitmap, Rectangle cropArea)
{
return bitmap.Clone(cropArea, bitmap.PixelFormat);
}
public enum BlurType
{
Mean_3x3,
Mean_5x5,
Mean_7x7,
Mean_9x9,
Gaussian_3x3,
Gaussian_5x5,
Motion_5x5,
Motion_5x5_45Degrees,
Motion_5x5_135Degrees,
Motion_7x7,
Motion_7x7_45Degrees,
Motion_7x7_135Degrees,
Motion_9x9,
Motion_9x9_45Degrees,
Motion_9x9_135Degrees,
Median_3x3,
Median_5x5,
Median_7x7,
Median_9x9,
Median_11x11
}
public static Bitmap BlurFilter(this Bitmap bitmap, BlurType type)
{
Bitmap resultBitmap = null;
switch (type)
{
case BlurType.Mean_3x3:
resultBitmap = ConvolutionFilter(bitmap, Matrix.Mean_3x3, 1.0 / 9.0, 0);
break;
case BlurType.Mean_5x5:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Mean_5x5, 1.0 / 25.0, 0);
break;
case BlurType.Mean_7x7:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Mean_7x7, 1.0 / 49.0, 0);
break;
case BlurType.Mean_9x9:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Mean_9x9, 1.0 / 81.0, 0);
break;
case BlurType.Gaussian_3x3:
resultBitmap = bitmap.ConvolutionFilter(Matrix.GaussianBlur_3x3, 1.0 / 16.0, 0);
break;
case BlurType.Gaussian_5x5:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Gaussian_5x5, 1.0 / 159.0, 0);
break;
case BlurType.Motion_5x5:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_5x5, 1.0 / 10.0, 0);
break;
case BlurType.Motion_5x5_45Degrees:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_5x5_45Degrees, 1.0 / 5.0, 0);
break;
case BlurType.Motion_5x5_135Degrees:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_5x5_135Degrees, 1.0 / 5.0, 0);
break;
case BlurType.Motion_7x7:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_7x7, 1.0 / 14.0, 0);
break;
case BlurType.Motion_7x7_45Degrees:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_7x7_45Degrees, 1.0 / 7.0, 0);
break;
case BlurType.Motion_7x7_135Degrees:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_7x7_135Degrees, 1.0 / 7.0, 0);
break;
case BlurType.Motion_9x9:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_9x9, 1.0 / 18.0, 0);
break;
case BlurType.Motion_9x9_45Degrees:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_9x9_45Degrees, 1.0 / 9.0, 0);
break;
case BlurType.Motion_9x9_135Degrees:
resultBitmap = bitmap.ConvolutionFilter(Matrix.Motion_9x9_135Degrees, 1.0 / 9.0, 0);
break;
case BlurType.Median_3x3:
resultBitmap = bitmap.Median(3);
break;
case BlurType.Median_5x5:
resultBitmap = bitmap.Median(5);
break;
case BlurType.Median_7x7:
resultBitmap = bitmap.Median(7);
break;
case BlurType.Median_9x9:
resultBitmap = bitmap.Median(9);
break;
case BlurType.Median_11x11:
resultBitmap = bitmap.Median(11);
break;
}
return resultBitmap;
}
public static Bitmap ColorBalance(this Bitmap bitmap, byte blueValue,
byte greenValue, byte redValue)
{
byte[] sourceBuffer = bitmap.GetByteArray();
float blue = 0;
float green = 0;
float red = 0;
float floatBlue = blueValue;
float floatGreen = greenValue;
float floatRed = redValue;
for (int i = 0; i + 4 < sourceBuffer.Length; i += 4)
{
blue = 255.0f / floatBlue * (float)sourceBuffer[i];
green = 255.0f / floatGreen * (float)sourceBuffer[i + 1];
red = 255.0f / floatRed * (float)sourceBuffer[i + 2];
blue = ClipByte(blue);
green = ClipByte(green);
red = ClipByte(red);
sourceBuffer[i] = (byte)blue;
sourceBuffer[i + 1] = (byte)green;
sourceBuffer[i + 2] = (byte)red;
}
return sourceBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
public static Bitmap Bitonal(this Bitmap bitmap, System.Windows.Media.Color darkColor, System.Windows.Media.Color lightColor, int range)
{
byte[] sourceBuffer = bitmap.GetByteArray();
for (int i = 0; i + 4 < sourceBuffer.Length; i += 4)
{
if (sourceBuffer[i] + sourceBuffer[i + 1] + sourceBuffer[i + 2] <= range)
{
sourceBuffer[i] = darkColor.B;
sourceBuffer[i + 1] = darkColor.G;
sourceBuffer[i + 2] = darkColor.R;
}
else
{
sourceBuffer[i] = lightColor.B;
sourceBuffer[i + 1] = lightColor.G;
sourceBuffer[i + 2] = lightColor.R;
}
}
return sourceBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
public static Bitmap Median(this Bitmap bitmap, int matrixSize)
{
byte[] sourceBuffer = bitmap.GetByteArray();
byte[] resultBuffer = new byte[sourceBuffer.Length];
byte[] middlePixel;
int stride = bitmap.Width * 4;
int filterOffset = (matrixSize – 1) / 2;
int calcOffset = 0, _Y = 0, _X = 0;
List<int> neighbour = new List<int>();
for (int i = 0; i + 4 < sourceBuffer.Length; i += 4)
{
_Y = -filterOffset; _X = -filterOffset;
neighbour.Clear();
while (_Y <= filterOffset)
{
calcOffset = i + (_X * 4) + (_Y * stride);
if (calcOffset > 0 && calcOffset + 4 < sourceBuffer.Length)
{
neighbour.Add(BitConverter.ToInt32(sourceBuffer, calcOffset));
}
_X++;
if (_X > filterOffset) { _X = -filterOffset; _Y++; }
}
neighbour.Sort();
middlePixel = BitConverter.GetBytes(neighbour[filterOffset]);
resultBuffer[i] = middlePixel[0];
resultBuffer[i + 1] = middlePixel[1];
resultBuffer[i + 2] = middlePixel[2];
resultBuffer[i + 3] = middlePixel[3];
}
return resultBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
private static Bitmap Convolution(Bitmap bitmap, double[,] filterMatrix, double factor = 1, int bias = 0, bool grayscale = false)
{
BitmapData sourceData = bitmap.LockBits(new Rectangle(0, 0,
bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] source = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, source, 0, source.Length);
bitmap.UnlockBits(sourceData);
if (grayscale == true)
{
float rgb = 0;
for (int i = 0; i < source.Length; i += 4)
{
rgb = source[i] * 0.11f;
rgb += source[i + 1] * 0.59f;
rgb += source[i + 2] * 0.3f;
source[i] = (byte)rgb;
source[i + 1] = source[i];
source[i + 2] = source[i];
}
}
double blue = 0.0;
double green = 0.0;
double red = 0.0;
int filterWidth = filterMatrix.GetLength(1);
int filterHeight = filterMatrix.GetLength(0);
int filterOffset = (filterWidth – 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
for (int offsetY = filterOffset; offsetY < bitmap.Height – filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX < bitmap.Width – filterOffset; offsetX++)
{
blue = 0;
green = 0;
red = 0;
byteOffset = offsetY * sourceData.Stride + offsetX * 4;
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
blue += (double)(source[calcOffset]) * filterMatrix[filterY + filterOffset, filterX + filterOffset];
green += (double)(source[calcOffset + 1]) * filterMatrix[filterY + filterOffset, filterX + filterOffset];
red += (double)(source[calcOffset + 2]) * filterMatrix[filterY + filterOffset, filterX + filterOffset];
}
}
blue = factor * blue + bias;
green = factor * green + bias;
red = factor * red + bias;
blue = ClipByte(blue);
green = ClipByte(green);
red = ClipByte(red);
resultBuffer[byteOffset] = (byte)(blue);
resultBuffer[byteOffset + 1] = (byte)(green);
resultBuffer[byteOffset + 2] = (byte)(red);
resultBuffer[byteOffset + 3] = source[byteOffset + 3];
}
}
return resultBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
public static Bitmap ConvolutionFilter(this Bitmap bitmap, double[,] xMatrix, double[,] yMatrix, double factor = 1, int bias = 0, bool grayscale = false)
{
byte[] sourceBuffer = bitmap.GetByteArray();
byte[] resultBuffer = bitmap.GetByteArray();
int stride = bitmap.Width * 4;
if (grayscale == true)
{
float rgb = 0;
for (int k = 0; k < sourceBuffer.Length; k += 4)
{
rgb = sourceBuffer[k] * 0.11f;
rgb += sourceBuffer[k + 1] * 0.59f;
rgb += sourceBuffer[k + 2] * 0.3f;
sourceBuffer[k] = (byte)rgb;
sourceBuffer[k + 1] = sourceBuffer[k];
sourceBuffer[k + 2] = sourceBuffer[k];
}
}
double blueX = 0.0;
double greenX = 0.0;
double redX = 0.0;
double blueY = 0.0;
double greenY = 0.0;
double redY = 0.0;
double blueTotal = 0.0;
double greenTotal = 0.0;
double redTotal = 0.0;
int filterOffset = 1;
int calcOffset = 0;
int byteOffset = 0;
for (int offsetY = filterOffset; offsetY < bitmap.Height – filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX < bitmap.Width – filterOffset; offsetX++)
{
blueX = greenX = redX = 0;
blueY = greenY = redY = 0;
blueTotal = greenTotal = redTotal = 0.0;
byteOffset = offsetY * stride + offsetX * 4;
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset + (filterX * 4) + (filterY * stride);
blueX += (double)(sourceBuffer[calcOffset]) * xMatrix[filterY + filterOffset, filterX + filterOffset];
greenX += (double)(sourceBuffer[calcOffset + 1]) * xMatrix[filterY + filterOffset, filterX + filterOffset];
redX += (double)(sourceBuffer[calcOffset + 2]) * xMatrix[filterY + filterOffset, filterX + filterOffset];
blueY += (double)(sourceBuffer[calcOffset]) * yMatrix[filterY + filterOffset, filterX + filterOffset];
greenY += (double)(sourceBuffer[calcOffset + 1]) * yMatrix[filterY + filterOffset, filterX + filterOffset];
redY += (double)(sourceBuffer[calcOffset + 2]) * yMatrix[filterY + filterOffset, filterX + filterOffset];
}
}
blueTotal = Math.Sqrt((blueX * blueX) + (blueY * blueY));
greenTotal = Math.Sqrt((greenX * greenX) + (greenY * greenY));
redTotal = Math.Sqrt((redX * redX) + (redY * redY));
blueTotal = ClipByte(blueTotal);
greenTotal = ClipByte(greenTotal);
redTotal = ClipByte(redTotal);
resultBuffer[byteOffset] = (byte)(blueTotal);
resultBuffer[byteOffset + 1] = (byte)(greenTotal);
resultBuffer[byteOffset + 2] = (byte)(redTotal);
resultBuffer[byteOffset + 3] = sourceBuffer[byteOffset + 3];
}
}
return resultBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
public static Bitmap Laplacian3x3Filter(this Bitmap bitmap, bool grayscale = true)
{
return Convolution(bitmap, Matrix.Laplacian_3x3, 1.0, 0, grayscale);
}
public static Bitmap Laplacian5x5Filter(this Bitmap bitmap, bool grayscale = true)
{
return Convolution(bitmap, Matrix.Laplacian_5x5, 1.0, 0, grayscale);
}
public static Bitmap LaplacianOfGaussianFilter(this Bitmap bitmap)
{
return Convolution(bitmap, Matrix.Laplacian_Of_Gaussian, 1.0, 0, true);
}
public static Bitmap Laplacian3x3OfGaussian3x3Filter(this Bitmap bitmap)
{
Bitmap resultBitmap = Convolution(bitmap, Matrix.Gaussian_3x3, 1.0 / 16.0, 0, true);
resultBitmap = Convolution(resultBitmap, Matrix.Laplacian_3x3, 1.0, 0, false);
return resultBitmap;
}
public static Bitmap Laplacian3x3OfGaussian5x5Filter1(this Bitmap bitmap)
{
Bitmap resultBitmap = Convolution(bitmap, Matrix.Gaussian_5x5_Type1, 1.0 / 159.0, 0, true);
resultBitmap = Convolution(resultBitmap, Matrix.Laplacian_3x3, 1.0, 0, false);
return resultBitmap;
}
public static Bitmap Laplacian3x3OfGaussian5x5Filter2(this Bitmap bitmap)
{
Bitmap resultBitmap = Convolution(bitmap, Matrix.Gaussian_5x5_Type2, 1.0 / 256.0, 0, true);
resultBitmap = Convolution(resultBitmap, Matrix.Laplacian_3x3, 1.0, 0, false);
return resultBitmap;
}
public static Bitmap Laplacian5x5OfGaussian3x3Filter(this Bitmap bitmap)
{
Bitmap resultBitmap = Convolution(bitmap, Matrix.Gaussian_3x3, 1.0 / 16.0, 0, true);
resultBitmap = Convolution(resultBitmap, Matrix.Laplacian_5x5, 1.0, 0, false);
return resultBitmap;
}
public static Bitmap Laplacian5x5OfGaussian5x5Filter1(this Bitmap bitmap)
{
Bitmap resultBitmap = Convolution(bitmap, Matrix.Gaussian_5x5_Type1, 1.0 / 159.0, 0, true);
resultBitmap = Convolution(resultBitmap, Matrix.Laplacian_5x5, 1.0, 0, false);
return resultBitmap;
}
public static Bitmap Laplacian5x5OfGaussian5x5Filter2(this Bitmap bitmap)
{
Bitmap resultBitmap = Convolution(bitmap, Matrix.Gaussian_5x5_Type2, 1.0 / 256.0, 0, true);
resultBitmap = Convolution(resultBitmap, Matrix.Laplacian_5x5, 1.0, 0, false);
return resultBitmap;
}
public static Bitmap Sobel3x3Filter(this Bitmap bitmap, bool grayscale = true)
{
return ConvolutionFilter(bitmap, Matrix.Sobel_3x3_Horizontal, Matrix.Sobel_3x3_Vertical, 1.0, 0, grayscale);
}
public static Bitmap PrewittFilter(this Bitmap bitmap, bool grayscale = true)
{
return ConvolutionFilter(bitmap, Matrix.Prewitt_3x3_Horizontal, Matrix.Prewitt_3x3_Vertical, 1.0, 0, grayscale);
}
public static Bitmap KirschFilter(this Bitmap bitmap, bool grayscale = true)
{
return ConvolutionFilter(bitmap, Matrix.Kirsch_3x3_Horizontal, Matrix.Kirsch_3x3_Vertical, 1.0, 0, grayscale);
}
public static Bitmap ConvolutionFilter(this Bitmap bitmap, double[,] matrix, double factor = 1, int bias = 0)
{
BitmapData sourceData = bitmap.LockBits(new Rectangle(0, 0,
bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] sourceBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, sourceBuffer.Length);
bitmap.UnlockBits(sourceData);
double B = 0.0;
double G = 0.0;
double R = 0.0;
int filterWidth = matrix.GetLength(1);
int filterHeight = matrix.GetLength(0);
int filterOffset = (filterWidth – 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
for (int offsetY = filterOffset; offsetY < bitmap.Height – filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX < bitmap.Width – filterOffset; offsetX++)
{
B = 0;
G = 0;
R = 0;
byteOffset = offsetY * sourceData.Stride + offsetX * 4;
for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
B += (double)(sourceBuffer[calcOffset]) * matrix[filterY + filterOffset, filterX + filterOffset];
G += (double)(sourceBuffer[calcOffset + 1]) * matrix[filterY + filterOffset, filterX + filterOffset];
R += (double)(sourceBuffer[calcOffset + 2]) * matrix[filterY + filterOffset, filterX + filterOffset];
}
}
B = factor * B + bias;
G = factor * G + bias;
R = factor * R + bias;
B = ClipByte(R);
G = ClipByte(G);
R = ClipByte(R);
resultBuffer[byteOffset] = (byte)(B);
resultBuffer[byteOffset + 1] = (byte)(G);
resultBuffer[byteOffset + 2] = (byte)(R);
resultBuffer[byteOffset + 3] = sourceBuffer[byteOffset + 3];
}
}
return resultBuffer.GetBitmap(bitmap.Width, bitmap.Height);
}
}
}
CustomBitmap.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ACE_UCV_EditorImagini
{
public class CustomBitmap
{
public Bitmap Bitmap { get; set; }
public String Path { get; set; }
public CustomBitmap()
{
}
public CustomBitmap(String path)
{
Path = path;
Bitmap = new Bitmap(path);
}
}
}
UndoRedoObject.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
namespace ACE_UCV_EditorImagini
{
public class UndoRedoObject : INotifyPropertyChanged
{
#region Members
Stack<Bitmap> _undoStack;
Stack<Bitmap> _redoStack;
private bool _canUndo;
private bool _canRedo;
public Bitmap CurrentObject;
#endregion
#region Properties
public bool CanUndo
{
get { return _canUndo; }
set
{
if (_canUndo != value)
{
_canUndo = value;
NotifyPropertyChanged("CanUndo");
}
}
}
public bool CanRedo
{
get { return _canRedo; }
set
{
if (_canRedo != value)
{
_canRedo = value;
NotifyPropertyChanged("CanRedo");
}
}
}
#endregion
public UndoRedoObject()
{
_undoStack = new Stack<Bitmap>();
_redoStack = new Stack<Bitmap>();
CanUndo = false;
CanRedo = false;
}
public void PushObjectToUndoStack(Bitmap obj)
{
if (obj != null)
_undoStack.Push((Bitmap)obj.Clone());
if (_undoStack.Count > 0) CanUndo = true;
CanRedo = false;
_redoStack.Clear();
}
public void PushObjectToRedoStack(Bitmap obj)
{
if (obj != null)
_redoStack.Push((Bitmap)obj.Clone());
if (_redoStack.Count > 0) CanRedo = true;
else CanRedo = false;
}
public Bitmap Undo()
{
try
{
Bitmap obj = (Bitmap)_undoStack.Pop().Clone();
if (_undoStack.Count == 0) CanUndo = false;
else CanUndo = true;
return obj;
}
catch
{
return null;
}
}
public Bitmap Redo(Bitmap currentBitmap)
{
try
{
Bitmap obj = (Bitmap)_redoStack.Pop().Clone();
_undoStack.Push(currentBitmap);
if (_undoStack.Count == 0)
CanUndo = false;
else
CanUndo = true;
if (_redoStack.Count == 0) CanRedo = false;
else CanRedo = true;
return obj;
}
catch
{
return null;
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
MainWindow.xaml
<local:BaseWindow x:Class="ACE_UCV_EditorImagini.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ACE_UCV_EditorImagini"
Title="" Height="716" Width="1006" Icon="/ACE_UCV_EditorImagini;component/Resources/logo.png" Drop="BaseWindow_Drop" AllowDrop="True">
<Window.Resources>
<Style TargetType="Image" x:Key="DisabledImageStyle">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Drop="BaseWindow_Drop">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="40" />
<RowDefinition Name="PictureAreaRow" Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="PictureAreaColumn" Width="*" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>
<Menu VerticalAlignment="Top" Grid.ColumnSpan="2" Grid.RowSpan="2" BorderBrush="Blue" BorderThickness="1">
<MenuItem Header="_File" >
<MenuItem Header="_Open" Click="OpenMenu_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/open.png" Height="20" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="_Save" Click="SaveMenu_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/save.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Save as…" Click="SaveAsMenu_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/save.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Locate on disk" Click="LocateOnDiskMenu_Click"/>
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Delete from disk" Click="DeleteFromDiskMenu_Click"/>
<Separator />
<MenuItem Header="E_xit"/>
</MenuItem>
<MenuItem Header="_Edit" >
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="_Resize" Click="MenuItem_Resize_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/resize.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Rotate left" Click="rotateLeftButton_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/rotate_left.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Rotate right" Click="rotateRightButton_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/rotate_right.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Vertical flip" Click="flipVerticallyButton_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/flip_vertically.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsEnabled="{Binding Path=EffectsEnabled}" Header="Horizontal flip" Click="flipHorizontallyButton_Click">
<MenuItem.Icon>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/flip_horizontally.png" Height="20"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="_Help" >
<MenuItem Header="_Help" />
</MenuItem>
</Menu>
<Grid Grid.Row="1" Background="#0F00005B">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Grid.Column="1" Click="OpenMenu_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/open.png" ToolTip="Open image" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="2" Click="SaveMenu_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/save.png" ToolTip="Save" />
</StackPanel>
</Button>
<Button Grid.Row="1" Grid.Column="3" Name="rotateLeftButton" Click="rotateLeftButton_Click" IsEnabled="{Binding Path=EffectsEnabled}">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/rotate_left.png" ToolTip="Rotate left" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Name="rotateRightButton" Grid.Column="4" Click="rotateRightButton_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/rotate_right.png" ToolTip="Rotate right" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Name="flipVerticallyButton" Grid.Column="5" Click="flipVerticallyButton_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/flip_vertically.png" ToolTip="Vertical flip" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Name="flipHorizontallyButton" Grid.Column="6" Click="flipHorizontallyButton_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/flip_horizontally.png" ToolTip="Horizontal flip" />
</StackPanel>
</Button>
<Button Name="undoButton" Grid.Row="1" Grid.Column="7" Click="undo_Click" HorizontalAlignment="Left" Width="30" IsEnabled="False">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/undo.png" ToolTip="Undo" />
</StackPanel>
</Button>
<Button Name="redoButton" Grid.Row="1" Grid.Column="8" Click="redo_Click" IsEnabled="False">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/redo.png" ToolTip="Redo" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="9" Click="MenuItem_Resize_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/resize.png" ToolTip="Resize" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="10" Click="zoomIn_Click" HorizontalAlignment="Left" Width="30">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/Zoom_in.png" ToolTip="Zoom in" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="11" Click="zoomOut_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/Zoom_out.png" ToolTip="Zoom out" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="12" Click="select_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/select.png" ToolTip="Selection" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="13" Click="crop_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/crop.jpg" ToolTip="Crop" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="15" Click="fitPhoto_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/fit.png" ToolTip="Fit photo inside viewing area" />
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Grid.Row="1" Grid.Column="16" Click="actualSize_Click">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/actualsize.png" ToolTip="Display photo at actual size" />
</StackPanel>
</Button>
<TextBlock Text="{Binding Path=ZoomFactorPercentString}" Grid.Row="1" Grid.Column="20" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,5,0"/>
</Grid>
<ScrollViewer SnapsToDevicePixels="True"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Disabled" Grid.Row="1" Grid.RowSpan="2" Grid.Column="1">
<TabControl Grid.Row="1" Grid.RowSpan="2" HorizontalAlignment="Right" VerticalAlignment="Stretch" Name="tabControl1" Grid.Column="1" Width="250" FlowDirection="LeftToRight" HorizontalContentAlignment="Left">
<TabItem Header="General filters" Name="tabItem1" VerticalAlignment="Top" ClipToBounds="True">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="75" />
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="85" />
<RowDefinition Height="85" />
<RowDefinition Height="85" />
<RowDefinition Height="85*"/>
<RowDefinition Height="85*" />
</Grid.RowDefinitions>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="grayscaleButton" Click="grayscaleButton_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Width="70" Height="80">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/grayscale.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Grayscale</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="invertButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="1" Click="invertButton_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/invert.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Invert</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="colorFilterButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Click="colorFilterButton_Click" Height="80" Width="70">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/colorfilter.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Color filter</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="brightnessButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="0" Click="brightnessButton_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/brightness.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Brightness</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="sepiaButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1" Click="sepiaButton_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/sepia.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Sepia</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="contrastButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="2" Click="contrastButton_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/contrast.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Contrast</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="distortionButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="distortionButton_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/distortion.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Distortion</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Name="oilPaintButton" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="1" Click="oilPaintButton_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/oilpaint.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Oil Paint</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="2" Click="sharpen_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/sharpen.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Sharpen</TextBlock>
</StackPanel>
</Button>
</Grid>
</TabItem>
<TabItem Header="Edge detection" Name="tabItem2" VerticalAlignment="Top">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="75" />
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="85" />
<RowDefinition Height="85" />
<RowDefinition Height="85" />
<RowDefinition Height="85*" />
<RowDefinition Height="85*" />
<RowDefinition Height="85*" />
<RowDefinition Height="85*" />
</Grid.RowDefinitions>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1" Click="Sobel3x3_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/sobel.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Sobel</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="2" Click="Sobel3x3Gray_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/sobelgray.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Sobel Gray</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="prewitt_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/prewitt.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Prewitt</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="1" Click="prewittGray_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/prewittgray.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Prewitt Gray</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="2" Click="kirsch_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/kirsch.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Kirsh</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="0" Click="kirschGray_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/kirschgray.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Kirsch Gray</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" Click="laplacianEdgeDetection_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/laplacian.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Laplacian</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="1" Click="laplacianOfGaussianEdgeDetection_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/log.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">LoG</TextBlock>
</StackPanel>
</Button>
</Grid>
</TabItem>
<TabItem Header="Blur" VerticalAlignment="Top">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="82" />
<ColumnDefinition Width="82" />
<ColumnDefinition Width="82" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="87" />
<RowDefinition Height="87" />
<RowDefinition Height="87" />
<RowDefinition Height="87*" />
<RowDefinition Height="87*" />
<RowDefinition Height="87*" />
<RowDefinition Height="87*" />
</Grid.RowDefinitions>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Click="BlurGaussian3x3_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/gauss3x3.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Gaussian3x3</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="1" Click="GaussianBlur5x5_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/gauss5x5.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Gaussian5x5</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Click="Mean3x3_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/mean3x3.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Mean3x3</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="0" Click="Mean5x5_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/mean5x5.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Mean5x5</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1" Click="Mean7x7_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/mean7x7.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Mean7x7</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="2" Click="Mean9x9_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/mean9x9.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Mean9x9</TextBlock >
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="0" Click="Median3x3_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/median3x3.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Median3x3</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="1" Click="Median5x5_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/median5x5.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Median5x5</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="2" Click="Median7x7_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/median7x7.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Median7x7</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="3" Click="Median9x9_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/median9x9.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Median9x9</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="1" Click="Median11x11_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/median11x11.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Median11x11</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="3" Grid.Column="2" Click="Motion5x5_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/motion5x5.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion5x5</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="4" Grid.Column="0" Click="Motion5x5_135_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/motion5x5_135.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion5x5L</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="4" Grid.Column="1" Click="Motion5x5_45_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/motion5x5_45.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion5x5R</TextBlock>
</StackPanel>
</Button>
<Button VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="4" Grid.Column="2" Click="Motion7x7_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/brightness_button.jpg" Stretch="None" Height="50" Width="50" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion7x7</TextBlock>
</StackPanel>
</Button>
<Button VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="5" Grid.Column="0" Click="Motion7x7_135_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/brightness_button.jpg" Stretch="None" Height="50" Width="50" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion7x7_135</TextBlock>
</StackPanel>
</Button>
<Button VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="5" Grid.Column="1" Click="Motion7x7_45_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/brightness_button.jpg" Stretch="None" Height="50" Width="50" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion7x7_45</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="4" Grid.Column="2" Click="Motion9x9_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/motion9x9.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion9x9</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="5" Grid.Column="0" Click="Motion9x9_135_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/motion9x9_135.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion9x9L</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="5" Grid.Column="1" Click="Motion9x9_45_Click" Width="70" Height="80">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/motion9x9_45.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold" Height="20">Motion9x9R</TextBlock>
</StackPanel>
</Button>
</Grid>
</TabItem>
<TabItem Header="Advanced filters" Name="tabItem3" VerticalAlignment="Top">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="82" />
<ColumnDefinition Width="82" />
<ColumnDefinition Width="82" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="87" />
<RowDefinition Height="87" />
<RowDefinition Height="85" />
<RowDefinition Height="85*" />
</Grid.RowDefinitions>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Click="colorSubstitution_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Width="80" Height="85">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/colorsubst.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Replace color</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="1" Click="solarise_Click" Width="80" Height="85">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/solarize.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Solarise</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="2" Click="colorBalance_Click" Width="80" Height="85">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/colorbalance.png" Stretch="None"/>
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Color balance</TextBlock>
</StackPanel>
</Button>
<Button IsEnabled="{Binding Path=EffectsEnabled}" Click="bitonal_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Width="80" Height="85" Grid.Row="1">
<StackPanel>
<Image Style="{StaticResource DisabledImageStyle}" Source="Resources/bitonal.png" Stretch="None" />
<TextBlock VerticalAlignment ="Bottom" HorizontalAlignment="Center" FontWeight="DemiBold">Bitonal</TextBlock>
</StackPanel>
</Button>
</Grid>
</TabItem>
</TabControl>
</ScrollViewer>
<ScrollViewer Grid.Row ="2" Name="MainScrollViewer" PanningMode="Both" VerticalScrollBarVisibility="Auto" Background="#33A0A0A0" HorizontalScrollBarVisibility="Auto" VerticalAlignment="Center" HorizontalAlignment="Center" Drop="BaseWindow_Drop">
<StackPanel Name="StackPanelForImage" Background="Transparent" DockPanel.Dock="Bottom" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Drop="BaseWindow_Drop">
</StackPanel>
</ScrollViewer>
</Grid>
</local:BaseWindow>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;
using System.Drawing.Drawing2D;
using Microsoft.Win32;
using System.Drawing.Imaging;
using System.IO;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Windows.Threading;
using ACE_UCV_EditorImagini.CustomControls;
namespace ACE_UCV_EditorImagini
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : BaseWindow
{
#region Fields
private int[] ZoomValues = new int[]{10,25,33,50,66,75,100,150,200,250,300,350,400,500};
private bool _effectsEnabled;
private CustomBitmap _currentBitmap;
private CustomBitmap _tempBitmap;
private ImageFormat _currentBitmapFormat;
private string _currentBitmapFilePath;
private ColorPickerWindow _colorPickerWindow;
private SliderWindow _brightnessWindow;
private SliderWindow _contrastWindow;
private SliderWindow _distortionWindow;
private ResizeWindow _resizeWindow;
private ColorSubstitutionWindow _colorSubstitutionWindow;
private LaplacianWindow _laplacianWindow;
private LaplacianOfGaussianWindow _laplacianOfGaussianWindow;
private ColorBalanceWindow _colorBalanceWindow;
private BitonalWindow _bitonalWindow;
private DragCanvas _dragCanvas;
private SelectionCanvas _selectionCanvas;
private Shape _rubberBand;
private double _zoomFactor = 1.0;
private const int DEFAULT_ZOOM_FACTOR_INDEX = 6;
private int _zoomFactorIndex = DEFAULT_ZOOM_FACTOR_INDEX;
private System.Windows.Controls.ContextMenu _contextMenuDragCanvas;
private RoutedEventHandler _contextMenuDragCanvasEventHandler;
private System.Windows.Controls.Image MainImage;
private bool _isColorFilter = false;
private bool _isSolarizeFilter = false;
private bool _isUndo = false;
private bool _isSelecting = false;
private UndoRedoObject _undoRedoObject;
#endregion
#region Properties
public string CurrentBitmapFilePath
{
get { return _currentBitmapFilePath; }
set
{
_currentBitmapFilePath = value;
NotifyPropertyChanged("CurrentBitmapFilePath");
}
}
public Bitmap CurrentBitmap
{
get { return _currentBitmap.Bitmap; }
set
{
if (_currentBitmap.Bitmap != value)
{
if (!_isUndo && _currentBitmap.Bitmap != null)
{
_undoRedoObject.PushObjectToUndoStack(_currentBitmap.Bitmap);
}
_currentBitmap.Bitmap = value;
NotifyPropertyChanged("CurrentBitmap");
}
}
}
public Bitmap TempBitmap
{
get { return _tempBitmap.Bitmap; }
set
{
if (_tempBitmap.Bitmap != value)
{
_tempBitmap.Bitmap = value;
NotifyPropertyChanged("TempBitmap");
}
}
}
public bool EffectsEnabled
{
get { return _effectsEnabled; }
set
{
if (_effectsEnabled != value)
{
_effectsEnabled = value;
NotifyPropertyChanged("EffectsEnabled");
}
}
}
public double ZoomFactor
{
get { return _zoomFactor; }
set
{
if (_zoomFactor != value)
{
_zoomFactor = value;
NotifyPropertyChanged("ZoomFactor");
NotifyPropertyChanged("ZoomFactorPercentString");
}
}
}
public string ZoomFactorPercentString
{
get { return "Zoom: " + _zoomFactor*100 + "%"; }
}
public bool IsSelecting
{
get { return _isSelecting; }
set
{
if (_isSelecting != value)
{
_isSelecting = value;
NotifyPropertyChanged("IsSelecting");
}
}
}
#endregion
#region Constructor
public MainWindow()
{
InitializeComponent();
_undoRedoObject = new UndoRedoObject();
_undoRedoObject.PropertyChanged += new PropertyChangedEventHandler(_undoRedoObject_PropertyChanged);
_tempBitmap = new CustomBitmap();
_currentBitmap = new CustomBitmap();
this.PropertyChanged += new PropertyChangedEventHandler(MainWindow_PropertyChanged);
_dragCanvas = new DragCanvas();
_selectionCanvas = new SelectionCanvas();
_selectionCanvas.CropImage += new RoutedEventHandler(_selectionCanvas_CropImage);
MainImage = new System.Windows.Controls.Image();
EffectsEnabled = false;
}
#endregion
private void _selectionCanvas_CropImage(object sender, RoutedEventArgs e)
{
_rubberBand = (Shape)_selectionCanvas.Children[1];
createDragCanvas();
}
# region Crop Canvas
private void createDragCanvas()
{
_dragCanvas.Width = CurrentBitmap.Width * _zoomFactor;
_dragCanvas.Height = CurrentBitmap.Height * _zoomFactor;
createDragCanvasMenu();
_selectionCanvas.Children.Remove(_rubberBand);
_selectionCanvas.Children.Remove(MainImage);
_dragCanvas.Children.Clear();
PaintImage(_currentBitmap);
_dragCanvas.Children.Add(MainImage);
_dragCanvas.Children.Add(_rubberBand);
StackPanelForImage.Children.Clear();
StackPanelForImage.Children.Add(_dragCanvas);
}
private void createDragCanvasMenu()
{
_selectionCanvas.ContextMenu = null;
_contextMenuDragCanvas = new System.Windows.Controls.ContextMenu();
System.Windows.Controls.MenuItem menuItemCancel = new System.Windows.Controls.MenuItem();
menuItemCancel.Header = "Cancel";
System.Windows.Controls.MenuItem menuItemSave = new System.Windows.Controls.MenuItem();
menuItemSave.Header = "Save";
_contextMenuDragCanvas.Items.Add(menuItemCancel);
_contextMenuDragCanvas.Items.Add(menuItemSave);
_contextMenuDragCanvasEventHandler = new RoutedEventHandler(MenuDragCanvasOnClick);
_contextMenuDragCanvas.AddHandler(System.Windows.Controls.MenuItem.ClickEvent, _contextMenuDragCanvasEventHandler);
_dragCanvas.ContextMenu = _contextMenuDragCanvas;
}
private void MenuDragCanvasOnClick(object sender, RoutedEventArgs args)
{
System.Windows.Controls.MenuItem item = args.Source as System.Windows.Controls.MenuItem;
switch (item.Header.ToString())
{
case "Save":
SaveCroppedImage();
break;
case "Cancel":
createSelectionCanvas();
IsSelecting = false;
break;
default:
break;
}
}
private void SaveCroppedImage()
{
if (_rubberBand != null)
{
System.Windows.Shapes.Rectangle Rect = (System.Windows.Shapes.Rectangle)_rubberBand;
System.Drawing.Rectangle result = new System.Drawing.Rectangle();
result.X = (int)(Canvas.GetLeft(_rubberBand) * (1.0/ZoomFactor));
result.Y = (int)(Canvas.GetTop(_rubberBand) * (1.0/ZoomFactor)) ;
result.Width = (int)(Rect.Width * (1.0 / ZoomFactor));
result.Height = (int)(Rect.Height * (1.0 / ZoomFactor));
CurrentBitmap = ImageFilters.Crop(CurrentBitmap, result);
createSelectionCanvas();
}
IsSelecting = false;
}
private void createSelectionCanvas()
{
StackPanelForImage.Children.Clear();
_selectionCanvas.IsEnabled = IsSelecting;
_selectionCanvas.Width = StackPanelForImage.Width = CurrentBitmap.Width * _zoomFactor;
_selectionCanvas.Height = StackPanelForImage.Height = CurrentBitmap.Height * _zoomFactor;
MainImage.RenderTransform = new ScaleTransform(_zoomFactor,_zoomFactor);
_selectionCanvas.Children.Clear();
_dragCanvas.Children.Clear();
_selectionCanvas.rubberBand = null;
_selectionCanvas.Children.Add(MainImage);
StackPanelForImage.Children.Add(_selectionCanvas);
}
#endregion
private void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "CurrentBitmap")
{
if (CurrentBitmap == null) return;
PaintImage(_currentBitmap);
StackPanelForImage.Width = CurrentBitmap.Width * ZoomFactor;
StackPanelForImage.Height = CurrentBitmap.Height * ZoomFactor;
}
else if (e.PropertyName == "TempBitmap")
{
if (TempBitmap == null) return;
PaintImage(_tempBitmap);
}
else if (e.PropertyName == "ZoomFactor")
{
createSelectionCanvas();
_selectionCanvas.IsEnabled = (IsSelecting);
}
else if (e.PropertyName == "IsSelecting")
{
_selectionCanvas.IsEnabled = IsSelecting;
}
}
private void _undoRedoObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "CanUndo")
{
undoButton.IsEnabled = _undoRedoObject.CanUndo;
}
else if (e.PropertyName == "CanRedo")
{
redoButton.IsEnabled = _undoRedoObject.CanRedo;
}
}
#region Helpers
private ImageFormat ImageFormatFromFilename(string fileName)
{
ImageFormat imageFormat;
using (System.Drawing.Image image = System.Drawing.Image.FromFile(fileName))
{
imageFormat = image.RawFormat;
}
return imageFormat;
}
#endregion
#region Paint
private void PaintImage(CustomBitmap customBitmap)
{
if (customBitmap == null || customBitmap.Bitmap == null) return;
MemoryStream stream = new MemoryStream();
customBitmap.Bitmap.Save(stream, ImageFormatFromFilename(customBitmap.Path));
stream.Position = 0;
BitmapImage bmapImage = new BitmapImage();
bmapImage.BeginInit();
bmapImage.CacheOption = BitmapCacheOption.OnLoad;
bmapImage.StreamSource = stream;
bmapImage.DecodePixelHeight = customBitmap.Bitmap.Height;
bmapImage.EndInit();
bmapImage.Freeze();
MainImage.Source = bmapImage;
}
#endregion
#region Events
#region MainWindow Events
private void OpenMenu_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
{
if (_currentBitmap.Bitmap != null)
_currentBitmap.Bitmap.Dispose();
_currentBitmap.Path = openFileDialog.FileName;
_currentBitmapFormat = ImageFormatFromFilename(openFileDialog.FileName);
CurrentBitmap = ImageFilters.Prepare(new Bitmap(openFileDialog.FileName));
this.Title = _currentBitmap.Path;
ZoomFactor = 1.0;
_zoomFactorIndex = DEFAULT_ZOOM_FACTOR_INDEX;
createSelectionCanvas();
EffectsEnabled = true;
}
}
private void grayscaleButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.Grayscale(CurrentBitmap);
}
private void invertButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.Invert(CurrentBitmap);
}
private void colorFilterButton_Click(object sender, RoutedEventArgs e)
{
_colorPickerWindow = new ColorPickerWindow();
_isColorFilter = true;
_colorPickerWindow.PropertyChanged += new PropertyChangedEventHandler(_colorPickerWindow_PropertyChanged);
_colorPickerWindow.Closing += new CancelEventHandler(_colorPickerWindow_Closing);
_colorPickerWindow.ShowDialog();
}
#region Brightness
private void brightnessButton_Click(object sender, RoutedEventArgs e)
{
_brightnessWindow = new SliderWindow("Brightness", -250, 250);
_brightnessWindow.PropertyChanged += new PropertyChangedEventHandler(_brightnessWindow_PropertyChanged);
_brightnessWindow.Closing += new CancelEventHandler(_brightnessWindow_Closing);
//PrepareTempBitmap();
//TempBitmap = ImageFilters.Brightness(CurrentBitmap, _brightnessWindow.SliderValue);
_brightnessWindow.ShowDialog();
}
private void _brightnessWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var brightnessWindow = sender as SliderWindow;
if (e.PropertyName == "SliderValue")
{
PrepareTempBitmap();
TempBitmap = ImageFilters.Brightness(CurrentBitmap, brightnessWindow.SliderValue);
}
}
private void _brightnessWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var brightnessWindow = sender as SliderWindow;
if (brightnessWindow.SliderResult == SliderWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
#endregion
#region Contrast
private void contrastButton_Click(object sender, RoutedEventArgs e)
{
_contrastWindow = new SliderWindow("Contrast", -100, 100);
_contrastWindow.PropertyChanged += new PropertyChangedEventHandler(_contrastWindow_PropertyChanged);
_contrastWindow.Closing += new CancelEventHandler(_contrastWindow_Closing);
_contrastWindow.ShowDialog();
}
private void _contrastWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var contrastWindow = sender as SliderWindow;
if (e.PropertyName == "SliderValue")
{
PrepareTempBitmap();
TempBitmap = ImageFilters.Contrast(CurrentBitmap, contrastWindow.SliderValue);
}
}
private void _contrastWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var contrastWindow = sender as SliderWindow;
if (contrastWindow.SliderResult == SliderWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
#endregion
#endregion
#region ColorPickerWindow Events
private void _colorPickerWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var colorPicker = sender as ColorPickerWindow;
if (e.PropertyName == "SelectedColor")
{
PrepareTempBitmap();
if (_isColorFilter)
{
TempBitmap = ImageFilters.ColorFilter(CurrentBitmap, colorPicker.SelectedColor);
}
else if (_isSolarizeFilter)
{
TempBitmap = CurrentBitmap.Solarise(colorPicker.SelectedColor.B, colorPicker.SelectedColor.G, colorPicker.SelectedColor.R);
}
}
}
private void _colorPickerWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var colorPicker = sender as ColorPickerWindow;
if (colorPicker.ColorPickerResult == ColorPickerWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
if (_isColorFilter) _isColorFilter = false;
if (_isSolarizeFilter) _isSolarizeFilter = false;
PaintImage(_currentBitmap);
}
#endregion
private void _resizeWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (_resizeWindow.ResizeResult == ResizeWindow.Result.OK)
{
CurrentBitmap = ImageFilters.ResizeBitmap(CurrentBitmap, _resizeWindow.ResizeWidth, _resizeWindow.ResizeHeight, _resizeWindow.Resampling);
}
}
#endregion
private void rotateLeftButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.RotateFlip(CurrentBitmap, RotateFlipType.Rotate270FlipNone);
}
private void rotateRightButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.RotateFlip(CurrentBitmap, RotateFlipType.Rotate90FlipNone);
}
private void flipVerticallyButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.RotateFlip(CurrentBitmap, RotateFlipType.RotateNoneFlipY);
}
private void flipHorizontallyButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.RotateFlip(CurrentBitmap, RotateFlipType.RotateNoneFlipX);
}
#region Save
private void SaveMenu_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap.Save(_currentBitmap.Path, ImageFormatFromFilename(_currentBitmap.Path));
}
private void SaveAsMenu_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.DefaultExt = ".jpg"; // Default file extension
dlg.Filter = "PNG|*.png|BMP|*.bmp|GIF|*.gif|JPG|*.jpg;*.jpeg|TIFF|*.tif;*.tiff";
// Show save file dialog box
Nullable<bool> result = dlg.ShowDialog();
// Process save file dialog box results
if (result == true)
{
// Save document
string filename = dlg.FileName;
var extension = System.IO.Path.GetExtension(filename);
switch (extension.ToLower())
{
case ".png":
CurrentBitmap.Save(filename, ImageFormat.Png);
break;
case ".bmp":
CurrentBitmap.Save(filename, ImageFormat.Bmp);
break;
case ".gif":
CurrentBitmap.Save(filename, ImageFormat.Gif);
break;
case ".tiff":
case ".tif":
CurrentBitmap.Save(filename, ImageFormat.Tiff);
break;
case ".jpg":
case ".jpeg":
CurrentBitmap.Save(filename, ImageFormat.Jpeg);
break;
default:
System.Windows.MessageBox.Show("Unknown image format.");
break;
}
}
}
#endregion
private void LocateOnDiskMenu_Click(object sender, RoutedEventArgs e)
{
if (File.Exists(_currentBitmap.Path))
{
Process.Start("explorer.exe", "/select, " + _currentBitmap.Path);
}
}
private void DeleteFromDiskMenu_Click(object sender, RoutedEventArgs e)
{
if (System.Windows.Forms.MessageBox.Show("Do you want to delete " + _currentBitmap.Path + "?", "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
{
if (File.Exists(_currentBitmap.Path))
{
try
{
_currentBitmap.Bitmap.Dispose();
File.Delete(_currentBitmap.Path);
MainImage.Source = null;
_currentBitmap.Path = String.Empty;
}
catch
{
System.Windows.Forms.MessageBox.Show("Could not delete the file " + _currentBitmap.Path, "", MessageBoxButtons.OK);
}
}
}
}
private void sepiaButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.Sepia(CurrentBitmap);
}
private void MenuItem_Resize_Click(object sender, RoutedEventArgs e)
{
_resizeWindow = new ResizeWindow(CurrentBitmap.Width, CurrentBitmap.Height);
_resizeWindow.Closing += new CancelEventHandler(_resizeWindow_Closing);
_resizeWindow.ShowDialog();
}
private void distortionButton_Click(object sender, RoutedEventArgs e)
{
_distortionWindow = new SliderWindow("Distortion Blur", 0, 50);
_distortionWindow.PropertyChanged += new PropertyChangedEventHandler(_distortionWindow_PropertyChanged);
_distortionWindow.Closing += new CancelEventHandler(_distortionWindow_Closing);
_distortionWindow.ShowDialog();
}
private void _distortionWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var distortionWindow = sender as SliderWindow;
if (e.PropertyName == "SliderValue")
{
PrepareTempBitmap();
TempBitmap = ImageFilters.Distortion(CurrentBitmap, distortionWindow.SliderValue);
}
}
private void _distortionWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var distortionWindow = sender as SliderWindow;
if (distortionWindow.SliderResult == SliderWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
private void oilPaintButton_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.OilPaint(CurrentBitmap, 5, 7);
}
private void kirschGray_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.KirschFilter();
}
private void Sobel3x3_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.Sobel3x3Filter(false);
}
private void Sobel3x3Gray_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.Sobel3x3Filter(true);
}
private void prewitt_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.PrewittFilter(false);
}
private void prewittGray_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.PrewittFilter(true);
}
private void kirsch_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.KirschFilter(false);
}
private void solarise_Click(object sender, RoutedEventArgs e)
{
_colorPickerWindow = new ColorPickerWindow();
_isSolarizeFilter = true;
_colorPickerWindow.PropertyChanged += new PropertyChangedEventHandler(_colorPickerWindow_PropertyChanged);
_colorPickerWindow.Closing += new CancelEventHandler(_colorPickerWindow_Closing);
_colorPickerWindow.ShowDialog();
}
private void colorSubstitution_Click(object sender, RoutedEventArgs e)
{
_colorSubstitutionWindow = new ColorSubstitutionWindow();
_colorSubstitutionWindow.PropertyChanged += new PropertyChangedEventHandler(_colorSubstitutionWindow_PropertyChanged);
_colorSubstitutionWindow.Closing += new CancelEventHandler(_colorSubtitutionWindow_Closing);
_colorSubstitutionWindow.ShowDialog();
}
private void _colorSubstitutionWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var colorSubstitutionWindow = sender as ColorSubstitutionWindow;
if (e.PropertyName == "Range" || e.PropertyName == "ColorToSubtitute" || e.PropertyName == "ReplacingColor")
{
PrepareTempBitmap();
TempBitmap = CurrentBitmap.ColorSubstitution(colorSubstitutionWindow.ColorToSubtitute, colorSubstitutionWindow.ReplacingColor, colorSubstitutionWindow.Range);
}
}
private void _colorSubtitutionWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var colorSubstitutionWindow = sender as ColorSubstitutionWindow;
if (colorSubstitutionWindow.DialogResult == ColorSubstitutionWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
private void sharpen_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = ImageFilters.Sharpen(CurrentBitmap, 1);
}
private void BlurGaussian3x3_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Gaussian_3x3);
}
private void GaussianBlur5x5_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Gaussian_5x5);
}
private void Mean3x3_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Mean_3x3);
}
private void Mean5x5_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Mean_5x5);
}
private void Mean7x7_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Mean_7x7);
}
private void Mean9x9_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Mean_9x9);
}
private void Median3x3_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Median_3x3);
}
private void Median5x5_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Median_5x5);
}
private void Median7x7_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Median_7x7);
}
private void Median9x9_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Median_9x9);
}
private void Median11x11_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Median_11x11);
}
private void Motion5x5_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_5x5);
}
private void Motion5x5_135_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_5x5_135Degrees);
}
private void Motion5x5_45_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_5x5_45Degrees);
}
private void Motion7x7_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_7x7);
}
private void Motion7x7_135_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_7x7_135Degrees);
}
private void Motion7x7_45_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_7x7_45Degrees);
}
private void Motion9x9_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_9x9);
}
private void Motion9x9_135_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_9x9_135Degrees);
}
private void Motion9x9_45_Click(object sender, RoutedEventArgs e)
{
CurrentBitmap = CurrentBitmap.BlurFilter(ImageFilters.BlurType.Motion_9x9_45Degrees);
}
private void undo_Click(object sender, RoutedEventArgs e)
{
_isUndo = true;
_undoRedoObject.PushObjectToRedoStack(CurrentBitmap);
CurrentBitmap = (Bitmap)_undoRedoObject.Undo();
_isUndo = false;
}
private void redo_Click(object sender, RoutedEventArgs e)
{
_isUndo = true;
CurrentBitmap = (Bitmap)_undoRedoObject.Redo(CurrentBitmap);
_isUndo = false;
}
private void zoomIn_Click(object sender, RoutedEventArgs e)
{
if (_zoomFactorIndex + 1 < ZoomValues.Length)
{
ZoomFactor = ZoomValues[++_zoomFactorIndex]/100.0;
}
}
private void zoomOut_Click(object sender, RoutedEventArgs e)
{
if (_zoomFactorIndex -1 >= 0)
{
ZoomFactor = ZoomValues[–_zoomFactorIndex] / 100.0;
}
}
private void select_Click(object sender, RoutedEventArgs e)
{
IsSelecting = true;
}
#region LaplacianEdgeDetection
private void laplacianEdgeDetection_Click(object sender, RoutedEventArgs e)
{
_laplacianWindow = new LaplacianWindow();
_laplacianWindow.PropertyChanged += new PropertyChangedEventHandler(_laplacianWindow_PropertyChanged);
_laplacianWindow.Closing += new CancelEventHandler(_laplacianWindow_Closing);
_laplacianWindow.ShowDialog();
}
private void _laplacianWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var laplacianWindow = sender as LaplacianWindow;
if (e.PropertyName == "Is3x3" || e.PropertyName == "Is5x5" || e.PropertyName == "IsGray")
{
if (_tempBitmap.Bitmap != null)
_tempBitmap.Bitmap.Dispose();
_tempBitmap.Path = _currentBitmap.Path;
if (laplacianWindow.Is3x3)
{
TempBitmap = CurrentBitmap.Laplacian3x3Filter(laplacianWindow.IsGray);
}
else if (laplacianWindow.Is5x5)
{
TempBitmap = CurrentBitmap.Laplacian5x5Filter(laplacianWindow.IsGray);
}
}
}
private void _laplacianWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var laplacianWindow = sender as LaplacianWindow;
if (laplacianWindow.WindowResult == LaplacianWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
#endregion
#region LaplacianOfGaussianEdgeDetection
private void laplacianOfGaussianEdgeDetection_Click(object sender, RoutedEventArgs e)
{
_laplacianOfGaussianWindow = new LaplacianOfGaussianWindow();
_laplacianOfGaussianWindow.PropertyChanged += new PropertyChangedEventHandler(_laplacianOfGaussianWindow_PropertyChanged);
_laplacianOfGaussianWindow.Closing += new CancelEventHandler(_laplacianOfGaussianWindow_Closing);
_laplacianOfGaussianWindow.ShowDialog();
}
private void _laplacianOfGaussianWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var laplacianOfGaussianWindow = sender as LaplacianOfGaussianWindow;
if (e.PropertyName == "IsSimple" || e.PropertyName == "IsL3x3G3x3" ||
e.PropertyName == "IsL3x3G5x5a" || e.PropertyName == "IsL3x3G5x5b" ||
e.PropertyName == "IsL5x5G3x3" || e.PropertyName == "IsL5x5G5x5a" ||
e.PropertyName == "IsL5x5G5x5b")
{
if (_tempBitmap.Bitmap != null)
_tempBitmap.Bitmap.Dispose();
_tempBitmap.Path = _currentBitmap.Path;
if (laplacianOfGaussianWindow.IsSimple)
{
TempBitmap = CurrentBitmap.LaplacianOfGaussianFilter();
}
else if (laplacianOfGaussianWindow.IsL3x3G3x3)
{
TempBitmap = CurrentBitmap.Laplacian3x3OfGaussian3x3Filter();
}
else if (laplacianOfGaussianWindow.IsL3x3G5x5a)
{
TempBitmap = CurrentBitmap.Laplacian3x3OfGaussian5x5Filter1();
}
else if (laplacianOfGaussianWindow.IsL3x3G5x5b)
{
TempBitmap = CurrentBitmap.Laplacian3x3OfGaussian5x5Filter2();
}
else if (laplacianOfGaussianWindow.IsL5x5G3x3)
{
TempBitmap = CurrentBitmap.Laplacian5x5OfGaussian3x3Filter();
}
else if (laplacianOfGaussianWindow.IsL5x5G5x5a)
{
TempBitmap = CurrentBitmap.Laplacian5x5OfGaussian5x5Filter1();
}
else if (laplacianOfGaussianWindow.IsL5x5G5x5b)
{
TempBitmap = CurrentBitmap.Laplacian5x5OfGaussian5x5Filter2();
}
}
}
private void _laplacianOfGaussianWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var laplacianOfGaussianWindow = sender as LaplacianOfGaussianWindow;
if (laplacianOfGaussianWindow.WindowResult == LaplacianOfGaussianWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
#endregion
private void PrepareTempBitmap()
{
if (_tempBitmap.Bitmap != null)
_tempBitmap.Bitmap.Dispose();
_tempBitmap.Path = _currentBitmap.Path;
}
private void crop_Click(object sender, RoutedEventArgs e)
{
SaveCroppedImage();
}
#region ColorBalance
private void colorBalance_Click(object sender, RoutedEventArgs e)
{
_colorBalanceWindow = new ColorBalanceWindow();
_colorBalanceWindow.PropertyChanged += new PropertyChangedEventHandler(_colorBalanceWindow_PropertyChanged);
_colorBalanceWindow.Closing += new CancelEventHandler(_colorBalanceWindow_Closing);
_colorBalanceWindow.ShowDialog();
}
private void _colorBalanceWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var colorBalanceWindow = sender as ColorBalanceWindow;
if (e.PropertyName == "RedValue" || e.PropertyName == "GreenValue" || e.PropertyName == "BlueValue")
{
PrepareTempBitmap();
TempBitmap = CurrentBitmap.ColorBalance((byte)colorBalanceWindow.BlueValue, (byte)colorBalanceWindow.GreenValue, (byte)colorBalanceWindow.RedValue);
}
}
private void _colorBalanceWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var colorBalanceWindow = sender as ColorBalanceWindow;
if (colorBalanceWindow.SliderResult == ColorBalanceWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
#endregion
private void bitonal_Click(object sender, RoutedEventArgs e)
{
_bitonalWindow = new BitonalWindow();
_bitonalWindow.PropertyChanged += new PropertyChangedEventHandler(_bitonalWindow_PropertyChanged);
_bitonalWindow.Closing += new CancelEventHandler(_bitonalWindow_Closing);
_bitonalWindow.ShowDialog();
}
private void _bitonalWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var bitonalWindow = sender as BitonalWindow;
if (e.PropertyName == "DarkColor" || e.PropertyName == "LightColor" || e.PropertyName == "Range")
{
PrepareTempBitmap();
TempBitmap = CurrentBitmap.Bitonal(bitonalWindow.DarkColor, bitonalWindow.LightColor, bitonalWindow.Range);
}
}
private void _bitonalWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var bitonalWindow = sender as BitonalWindow;
if (bitonalWindow.DialogResult == BitonalWindow.Result.OK && TempBitmap != null)
{
CurrentBitmap = (Bitmap)TempBitmap.Clone();
}
PaintImage(_currentBitmap);
}
private void fitPhoto_Click(object sender, RoutedEventArgs e)
{
int width = (int)PictureAreaColumn.ActualWidth;
int height = (int)PictureAreaRow.ActualHeight;
if (CurrentBitmap.Height > height || CurrentBitmap.Width > width)
{
for (int i = DEFAULT_ZOOM_FACTOR_INDEX; i >= 0; i–)
{
int newWidth = (int)(ZoomValues[i]/100.0 * CurrentBitmap.Width);
int newHeight = (int)(ZoomValues[i]/100.0 * CurrentBitmap.Height);
if (newHeight <= height && newWidth <= width)
{
ZoomFactor = ZoomValues[i]/100.0;
_zoomFactorIndex = i;
break;
}
}
}
}
private void actualSize_Click(object sender, RoutedEventArgs e)
{
ZoomFactor = 1.0;
_zoomFactorIndex = DEFAULT_ZOOM_FACTOR_INDEX;
createSelectionCanvas();
}
private void BaseWindow_Drop(object sender, System.Windows.DragEventArgs e)
{
var validExtensions = new[] { ".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".tif", ".gif"};
if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(System.Windows.DataFormats.FileDrop);
Console.WriteLine(files.Length);
if (files.Length == 1 && validExtensions.Contains(System.IO.Path.GetExtension(files[0])))
{
if (_currentBitmap.Bitmap != null)
_currentBitmap.Bitmap.Dispose();
_currentBitmap.Path = files[0];
_currentBitmapFormat = ImageFormatFromFilename(files[0]);
CurrentBitmap = ImageFilters.Prepare(new Bitmap(files[0]));
this.Title = _currentBitmap.Path;
ZoomFactor = 1.0;
_zoomFactorIndex = DEFAULT_ZOOM_FACTOR_INDEX;
createSelectionCanvas();
EffectsEnabled = true;
}
}
}
}
}
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Editor Si Convertor de Imagini Folosind Limbajul C# (ID: 149764)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
