Differences

This shows you the differences between two versions of the page.

Link to this comparison view

ps:laboratoare:10_2 [2018/12/02 16:41]
darius.necula
ps:laboratoare:10_2 [2020/10/07 18:33] (current)
ionut.gorgos
Line 1: Line 1:
-===== Laboratorul 10. Prelucrări de imagini - Steganography ​=====+===== Laboratorul 10. Prelucrări de imagini - Steganografie ​=====
 /* <​hidden>​ */ /* <​hidden>​ */
  
-==== Steganography ​====+==== Steganografie ​====
  
-În transmiterea informației e util să putem trimite un mesaj secret, iar pentru aceasta trebuie să folosim diverse tehnici de codificare. Este de asemenea util să trimitem informație care aparent să nu conțină un mesaj ascuns. Steganografia presupune ascunderea unui mesaj sub forma de text, imagine, ​video în cadrul altui mesaj sub forma text, imagine sau video.+În transmiterea informației e util să putem trimite un mesaj secret, iar pentru aceasta trebuie să folosim diverse tehnici de codificare. Estede asemeneautil să trimitem informație care aparent să nu conțină un mesaj ascuns. Steganografia presupune ascunderea unui mesaj sub forma de text, imagine, ​videoclip ​în cadrul altui mesaj sub forma text, imagine sau videoclip.
  
 În acest laborator vom vedea cum putem transmite o imagine secretă, ascunsă în cadrul altei imagini (denumită imagine de transport). În acest laborator vom vedea cum putem transmite o imagine secretă, ascunsă în cadrul altei imagini (denumită imagine de transport).
  
 Pentru a exemplifica tehnica, vom ascunde o imagine gri, de dimensiuni mai mici în cadrul unei imagini color mai mari. Pentru aceasta vom urma metoda descrisă aici: https://​www.researchgate.net/​publication/​269705199_Digital_Image_Steganography_An_FFT_Approach Pentru a exemplifica tehnica, vom ascunde o imagine gri, de dimensiuni mai mici în cadrul unei imagini color mai mari. Pentru aceasta vom urma metoda descrisă aici: https://​www.researchgate.net/​publication/​269705199_Digital_Image_Steganography_An_FFT_Approach
-În mare, metoda constă în a calcula spectrul imaginii de trasnsport și a înlocui o parte dintre coeficienții Fourier cu valorile pixelilor imaginii secrete. Vom investiga ce coeficienti ​sunt mai potriviti ​pentru a fi înlocuiți astfel încât imaginea transmisă ​sa nu prezinte zgomot evident și astfel încât imaginea secretă să reziste, să fie invariantă la modificări ​usoare ​ale imaginii transmise.+ 
 +În mare, metoda constă în a calcula spectrul imaginii de trasnsport și a înlocui o parte dintre coeficienții Fourier cu valorile pixelilor imaginii secrete. Vom investiga ce coeficienți ​sunt mai potriviți ​pentru a fi înlocuițiastfel încât imaginea transmisă ​să nu prezinte zgomot evident și astfel încât imaginea secretă să reziste, să fie invariantă la modificări ​ușoare ​ale imaginii transmise.
  
 === Exercițiul 1 -- Modulul și faza coeficienților Fourier [2p]=== === Exercițiul 1 -- Modulul și faza coeficienților Fourier [2p]===
  
-Primul lucru pe care îl vom investiga este importanța modulului și a fazei coeficienților Fourier (complecși). Fiecare coeficient din spectrul Fourier $s \in S$ este un număr complex $s \in \mathbb{C}$ cu o magnitudine $|s|$ și o fază: $e^{i\phi}$ astfel încât: $s = a + i \cdot b = |s| \cdot e^{i \phi}$ ​ .+Primul lucru pe care îl vom investiga este importanța modulului și a fazei coeficienților Fourier (complecși). Fiecare coeficient din spectrul Fourier $s \in S$ este un număr complex $s \in \mathbb{C}$ cu o magnitudine $|s|$ și o fază: $e^{i\phi}$ astfel încât: $s = a + i \cdot b = |s| \cdot e^{i \phi}$ ​ .
  
 Pentru aceasta: Pentru aceasta:
-  - citim o imagine RGB cu 3 canale pentru fiecare culoare ​(imreadșipentru simplitate o convertim într-o imagine cu un canal cu nivele de gri (folosim funcția rgb2gray). Pentru a fi totul mai simplu vom converti valorile în tipul '​double'​ și vom normaliza ​imaginea din valorile [0,​255] ​ în [0,​1]. ​  +  - citim o imagine RGB cu 3 canale pentru fiecare culoare, folosind funcția 'imread' din MATLAB ​și pentru simplitate o convertim într-o imagine cu un canal cu nivele de gri (folosim funcția ​'rgb2gray'). Pentru a fi totul mai simplu vom converti valorile în tipul '​double'​ și vom scala imaginea din valorile [0,255] în [0,​1]. ​Afisați imaginea folosind functia '​imagesc'​. ​ 
-  - luăm o imagine îi vom afla spectrul folosind Transformata Fourier ​Discreta implementata in Matlab ​în funcția '​fft2'​. +  - luăm o imagine ​și încercăm să îi aflăm ​spectrul folosind Transformata Fourier ​Discretă implementată ​în MATLAB, cu ajutorul ​funcției '​fft2'​. 
-  - păstrăm doar modulul coeficienților $|s|$ și vom reface imaginea ​initială cu Trasnformata Fourier ​inversă, implementată în funcția '​ifft2'​ +  - păstrăm doar modulul coeficienților $|s|$ și vom reface imaginea ​inițială cu Trasnformata Fourier ​Inversă, implementată în funcția '​ifft2'​. 
-  - păstrăm doar faza $e^{i \phi}$ și refacem imaginea cu ifft2 +  - păstrăm doar faza $e^{i \phi}$ și refacem imaginea cu 'ifft2'. 
-  - verificăm vizual care din cele 2 variante a păstrat mai multă informație. ​vizualizăm o imagine care are valori ​in [0,1] cu funcția '​imshow'​ (alternativ puteți folosi imagesc pentru o imagine nescalată).+  - normalizați imaginea reconstruită (în urma aplicării '​ifft2'​),​ prin scăderea minimului și împărțirea la valoarea diferenței dintre maxim și minim astfel încât valorile finale să fie în intervalul [0,1]. 
 +  - verificăm vizual care din cele 2 variante a păstrat mai multă informație. ​Vizualizăm o imagine care are valori ​în [0,1] cu funcția '​imshow'​ (alternativ puteți folosi ​'imagesc' ​pentru o imagine nescalată).
  
-Vom observa că informația este păstrată în principal în una dintre cele 2, asa ca putem sa schimbam cealaltă variantă înlocuind-o cu valorile imaginii pe care vrem să o ascundem+<​note>​ Folosiți **impixelinfo** pentru a obține ​informații detaliate despre pixelii unei imagini</​note>​
  
- <​hidden>​  +Vom observa că informația este păstrată în principal în una dintre ​cele 2așa că putem să schimbăm cealaltă variantă înlocuind-o cu valorile imaginii pe care vrem să o ascundem
-<​note>​ +
-O posibilă soluție: +
-<code matlab lab10_ex1_modul_vs_faza.m>​ +
-close all +
-clear all +
- +
-height ​ = 240; +
-width   = 320; +
- +
-% citim imaginile, le convertim in gri si le normalizam +
-img = imread('​peppers.png'​);​ +
-img = double(rgb2gray(img)) / 255; +
- +
-h = figure, imshow(img);​impixelinfo +
-title('​imagine originala'​) +
- +
-% aflam spectrul S(k) al imaginii +
-S = fft2(img);​ +
- +
-% investigam diferente ​dintre ​reconstructia din tot spectrulreconstructia +
-% doar din modulul spectrului si reconstructia din faza spectrului +
-img_res = ifft2(S); +
-h = figure, imshow(normalise(img_res));​impixelinfo +
-title('​imagine restituita din spectrul complet'​) +
- +
-img_res_abs = ifft2(abs(S));​ +
-h = figure, imshow(normalise(img_res_abs));​impixelinfo +
-title('​imagine restituita din modulul spectrului fourier'​) +
- +
-phase_S = S ./ abs(S); +
-img_res_ph = ifft2(phase_S);​ +
-h = figure, imshow(normalise(img_res_ph));​impixelinfo +
-title('​imagine restituita din faza spectrului fourier'​) +
-</​code>​ +
-</​note>​ +
-</​hidden> ​+
  
 === Exercițiul 2 -- Decodificarea unei imagini ascunse [2p] === === Exercițiul 2 -- Decodificarea unei imagini ascunse [2p] ===
  
 Descărcați imaginea de aici: {{:​ps:​laboratoare:​stego_img.zip|imagine}}. Descărcați imaginea de aici: {{:​ps:​laboratoare:​stego_img.zip|imagine}}.
-Pentru a decodifica imaginea ascunsă, citiți imaginea ​(imreadși scrieți o functie ​după urmați ​urmatorii pași: +Pentru a decodifica imaginea ascunsă, citiți imaginea ​cu 'imread' ​și scrieți o funcție ​după urmatorii pași: 
-  - calculați spectrul Fourier (fft2) al imaginii primite. Vizualizați modulul spectrului Fourier ​folosid ​functia imagesc sau funcția imshow. De ex: '​figure,​ imagesc(log(abs_S));​impixelinfo' ​logaritmam ​pentru a atenua diferențele prea mari din spectru care ne îngreunează vizualizarea. (Funcția impixelinfo ne ofera informații despre imagine). +  - calculați spectrul Fourier (fft2) al imaginii primite. Vizualizați modulul spectrului Fourier ​folosind ​functia ​'imagesc' ​sau funcția ​'imshow'. De ex: '​figure,​ imagesc(log(abs_S));​impixelinfo' ​logaritmăm ​pentru a atenua diferențele prea mari din spectru care ne îngreunează vizualizarea. (Funcția impixelinfo ne oferă ​informații despre imagine). 
-  - observați vizual în spectrul ​apare o imagine ascunsă. De ce apare imaginea oglindită de 3 ori?   +  - observați vizual ​că în spectru ​apare o imagine ascunsă. De ce apare imaginea oglindită de 3 ori?   
-  - normalizați spectrul prin scaderea ​minimului și împărțirea la valoarea diferenței dintre maxim și minim astfel încât valorile finale să fie in intervalul [0,1]+  - normalizați spectrul prin scăderea ​minimului și împărțirea la valoarea diferenței dintre maxim și minim astfel încât valorile finale să fie în intervalul [0,1].
   - selectați doar imaginea observată în spectru. Vizualizați imaginea folosind '​imshow'​. ​   - selectați doar imaginea observată în spectru. Vizualizați imaginea folosind '​imshow'​. ​
- 
-<​hidden> ​ 
- 
-<​note>​ 
-O posibilă soluție: 
- 
-<code matlab decode_stegano_image.m>​ 
-function hidden_img = decode_stegano_image(stego_image) 
- 
-top     = 100; 
-left    = 100; 
-height ​ = 240; 
-width   = 320; 
- 
-fft_steno_double = fft2(stego_image);​ 
-fft_steno_double = normalise(fft_steno_double);​ 
-hidden_img = abs(fft_steno_double(top+1:​ top + height, left + 1 : left + width )); 
-hidden_img = normalise(hidden_img);​ 
-</​code>​ 
- 
- 
-</​note>​ 
-</​hidden>​ 
- 
- 
-<​hidden> ​ 
-<​note>​ 
-Doar pentru asistenți. Formam imagine steno. 
-<code matlab lab10_ex2.m>​ 
-clear all 
-close all 
- 
-height0 ​ = 240; 
-width0 ​  = 320; 
- 
-height ​ = 1 * height0; 
-width   = 1 * width0; 
-%% pregatirea datelor 
-img = imread('​sky.jpg'​);​ 
-img = imresize(img,​ [4 * height0 + 1, 4 * width0 + 1]); 
-h = figure , image(img) 
-title('​imagine originala'​) 
-print(h, '​-dpng',​ '​steganography_imagine_originala.png'​);​ 
- 
-img_gray = double(rgb2gray(img)) / 255; 
-h = figure , imshow(img_gray) 
-title('​imagine originala in gri') 
- 
-%citim imaginea secreta si o convertim in gri 
-hid_img = imread('​dog.jpeg'​);​ 
-hid_img = imresize(hid_img,​ [height,​width]);​ 
-hid_img = double(rgb2gray(hid_img)) / 255; 
- 
-stego = encode_hidden_image(img_gray,​ hid_img); 
-figure, imshow(stego) 
-filename = '​stego_img.bmp'​ 
-% salvam ca bmp, lossless astfel incât sa nu avem zgomot care ne-ar 
-% ingreuna recuperarea imaginii ascunse. Astfel avem agomot doar de 
-% cuantizare a valorilor pixelilor dar nu si de la algoritmul de compresie 
-imwrite(stego,​filename,'​bmp'​);​ 
-%% decodificarea 
- 
-stego_bmp = imread(filename);​ 
-hid = decode_stegano_image(stego_bmp);​ 
- 
- 
-figure, imshow(hid);​impixelinfo 
-title('​imagine ascunsă recuperată din imaginea bmp stego ') 
- 
-</​code>​ 
-</​note>​ 
- 
-<​note>​ 
-<code matlab normalise.m>​ 
-function img = normalise(img) 
-img = (img - min(img(:)) ) ./ (max(img(:​)) - min(img(:​))) ; 
-</​code>​ 
-</​note>​ 
-</​hidden> ​ 
- 
  
  
 === Exercițiul 3 -- Codificarea unei imagini ascunse în spectrul unui imagini de transport [5p] === === Exercițiul 3 -- Codificarea unei imagini ascunse în spectrul unui imagini de transport [5p] ===
  
-Scrieți o funcție care primeste ​două imagini gri (doua matrici), o imaginea ​secreta si o imagine de transport de cel putin două ori mai mare și codifică imaginea secretă în spectrul imaginii de transport.+Scrieți o funcție care primește ​două imagini gri (două matrici), o imaginea ​secretă și o imagine de transport de cel puțin ​două ori mai mare și codifică imaginea secretă în spectrul imaginii de transport.
  
-Urmați urmatorii pasi: +<​note>​ Pentru exercițiile 3 ș4 puteți folosi orice imaginiaveți grijă doar să redimensionați imaginile astfel încât ​imaginea ​carrier ​să fie de cel puțin de 2 ori mai mare decât ​imaginea secretă.</​note>​
-  - calculați spectrul imaginii de transport +
-  - selectati o zona din spectrul imaginii de transport pe care o veți înlocui cu valorile imaginii secrete. Zona va avea colțul din stanga-sus in locația [top,left] și va avea dimensiunea [height,​width] = size(secret_image). +
-  - trebuie să înlocuiți modulul (atenție: doar modulul nu si faza) spectrului din zona selectată cu valorile imaginii secrete. Intâi calculăm media valorilor spectrului din zona dorită apoi înlocuim valorile din zona cu valorile imaginii secrete scalate (de 2-10 ori mai mari decât media: ex. inmulțite cu media x 5). +
-  - vrem sa trecem acest spectru înapoi în domeniul timp ( defapt este domeniu spațial dar îl denumim domeniu timp doar ca să păstrăm terminologia de pâna acum:  domeniu timp --FFT--> domeniul spectral si invers). +
-  - pentru a avea sens trecerea din spectrul modificat înapoi în timp trebuie să avem grijă ca spectrul format să corespundă unui semnal real valid, adică spectrul trebuie să fie periodic pe cele 2 axe și trebuie sa avem proprietatea |S(-k)| = |S(K)|. Pentru aceasta trebuie sa avem un spectru care este simetric față de jumătatea spectrului atât vertical cât și orizontal. +
-  - creați specrtrul simetric și treceți în domeniul timp (spațial) cu ifft2. +
-  - afisați imaginea ​stego - aceasta imaginea ar trebui ​să fie insesizabil diferita față de imaginea originală. +
-  - extrageți ​imaginea secretă ​folosind funcția scrisă la punctul 2.+
  
- <hidden>+Urmați următorii pași: 
 +  - calculați spectrul imaginii de transport. 
 +  - selectați o zonă din spectrul imaginii de transport pe care o veți înlocui cu valorile imaginii secrete. Zona va avea colțul din stânga-sus în locația [top,left] și va avea dimensiunea [height,​width] = size(secret_image). 
 +  - trebuie să înlocuiți modulul (atenție: doar modulul nu și faza) spectrului din zona selectată cu valorile imaginii secrete. Intâi calculăm media valorilor spectrului din zona dorită, apoi înlocuim valorile din zonă cu valorile imaginii secrete scalate (de 2-10 ori mai mari decât media: ex. inmulțite cu media x 5). 
 +  - vrem să trecem acest spectru înapoi în domeniul timp (de fapt este domeniu spațial, dar îl denumim domeniu timp doar ca să păstrăm terminologia de până acum:  domeniu timp --FFT --> domeniul spectral și invers). 
 +  - pentru a avea sens trecerea din spectrul modificat înapoi în timp trebuie să avem grijă ca spectrul format să corespundă unui semnal real valid, adică spectrul trebuie să fie periodic pe cele 2 axe și trebuie să avem proprietatea |S(-k)| = |S(k)|. Pentru aceasta trebuie să avem un spectru care este simetric față de jumătatea spectrului atât vertical cât și orizontal. Spectrul final trebuie sa arate asemănător cu spectrul de mai jos. 
 +  - creați spectrul simetric și treceți în domeniul timp (spațial) cu '​ifft2'​. 
 +  - afisați imaginea stego - această imaginea ar trebui să fie insesizabil diferită față de imaginea originală. 
 +  - extrageți imaginea secretă folosind funcția scrisă la exercițiul 2.
  
-<​note>​ +{{:ps:laboratoare:lab10_ps_stego_spectru.png?300|}}
-<code matlab encode_hidden_image.m>​ +
-     +
-function steno_img = encode_hidden_image(carrier_img,​ hidden_img) +
-% punem in modului spectrului imaginea ascunsa +
-% imaginea secreta o ascundem in frecventele inalte ale imaginii +
- +
- +
-top     = 100; +
-left    = 100; +
-[height, width] = size(hidden_img);​ +
- +
-S = fft2(carrier_img);​ +
-phase_S = S ./ abs(S); +
- +
-abs_S = abs(S); +
-% calculam media spectrului pe zona pe care o vom inlocui, +
-% astfel incat media pe acea zona sa ramana aproximativ constanta +
-patch = abs_S(top+1:​ top + height, left + 1 : left + width); +
-max_S = 5 * mean(patch(:​));​ +
- +
-[st_h, st_w] = size(abs_S) ; +
- +
-abs_S(top+1:​ top + height, left + 1 : left + width  ) = max_S * hidden_img;​ +
-abs_S(top+1:​ top + height, st_w - left + 2 - width: st_w - left + 1  ) = max_S * hidden_img(:,​end:​-1:​1);​ +
- +
-abs_S(st_h - top + 2 - height : st_h - top + 1, left + 1 : left + width) = max_S * hidden_img(end:​-1:​1,:​);​ +
-abs_S(st_h - top + 2 - height : st_h - top + 1, st_w - left + 2 - width: st_w - left + 1) = max_S * hidden_img(end:​-1:1,end:-1:1); +
- +
-% figure, imagesc(log(abs_S));​impixelinfo +
-% title('​spectru imagini stego'​) +
- +
-steno_img = ifft2(abs_S ​.* phase_S); +
- +
-</​code>​ +
-</​note>​+
  
-</​hidden> ​ 
  
 === Exercițiul 4 -- Codificarea unei imagini secrete, gri în spectrul unui imagini color [1p] === === Exercițiul 4 -- Codificarea unei imagini secrete, gri în spectrul unui imagini color [1p] ===
-Vrem să codificăm într-o imagine color o imagine gri sau mai multe. Pentru aceasta am putea codifica în fiecare dintre cele 3 canale RGB o imagine secretă. Pentru a avea totuși o imagine ​steno rezultată fară prea mult zgomot cauzat de encodarea imaginilor secrete vom avea altă abordare. Vom converti imaginea RGB în alt spațiu de culoare L*a*b, un spațiu de culoare cu 3 canale: L pentru luminanță care reprezintă majoritatea informației (echivalent cu imaginile gri cu care am lucrat până acum) și 2 canele ​de culoare a si b. Dacă modificăm doar canalele de culoare imaginea ​finala ​se modifică mult mai puțin sesizabil pentru un om, față de cazul în care modificăm canalele RGB sau Luminanța. De aceea vom encoda o singura ​imagine secretă (aceeași) în canelele ​si b, lucrând cu ele individual ca și cum ar fi poze gri.+Vrem să codificăm într-o imagine color o imagine gri sau mai multe. Pentru aceasta am putea codifica în fiecare dintre cele 3 canale RGB o imagine secretă. Pentru a avea totuși o imagine ​stego rezultată fară prea mult zgomot cauzat de encodarea imaginilor secrete vom avea altă abordare. Vom converti imaginea RGB în alt spațiu de culoare L*a*b, un spațiu de culoare cu 3 canale: L pentru luminanță care reprezintă majoritatea informației (echivalent cu imaginile gri cu care am lucrat până acum) și 2 canale ​de culoare a și b. Dacă modificăm doar canalele de culoare imaginea ​finală ​se modifică mult mai puțin sesizabil pentru un om, față de cazul în care modificăm canalele RGB sau Luminanța. De aceea vom encoda o singură ​imagine secretă (aceeași) în canalele ​și b, lucrând cu ele individual ca și cum ar fi poze gri.
  
 Folosind scheletul următor și funcțiile scrise precedent codificați o imagine gri într-o imagine color. Folosind scheletul următor și funcțiile scrise precedent codificați o imagine gri într-o imagine color.
Line 223: Line 73:
 width   = 1 * width0; width   = 1 * width0;
 %% citire %% citire
-% citim imaginea de transport si o confertim ​din spatiul de culoare RGB in+% citim imaginea de transport si o convertim ​din spatiul de culoare RGB in
 % spatiul L*a*b* % spatiul L*a*b*
  
Line 235: Line 85:
 h = figure , image(img_lab) h = figure , image(img_lab)
 title('​imagine originala in spatiul l*a*b'​) title('​imagine originala in spatiul l*a*b'​)
-%citim imaginea secreta si o confertim ​in gri+%citim imaginea secreta si o convertim ​in gri
 hid_img = imread('​dog.jpeg'​);​ hid_img = imread('​dog.jpeg'​);​
 hid_img = imresize(hid_img,​ [height,​width]);​ hid_img = imresize(hid_img,​ [height,​width]);​
Line 277: Line 127:
 === Exercițiul 5 [BONUS 3p] -- Verificare Robustețe === === Exercițiul 5 [BONUS 3p] -- Verificare Robustețe ===
  
-Vrem să verificăm ​câț de robustă este metoda ​noastra ​de codificare. Pentru aceasta obținem imaginea stego prin codificarea imaginii secrete în imaginea de trasnport, efectuăm diverse ​transformari ​pe aceasta și verificăm dacă imaginea secretă este încă prezentă.+Vrem să verificăm ​cât de robustă este metoda ​noastră ​de codificare. Pentru aceasta obținem imaginea stego prin codificarea imaginii secrete în imaginea de transport, efectuăm diverse ​transformări ​pe aceasta și verificăm dacă imaginea secretă este încă prezentă. 
 + 
 +Verificați următoarele transformări pe imaginea stego:  
 +  - tăiați o parte din imaginea stego: faceți negru o parte din aceasta. Cu cât tăiați mai mult cu atât ar trebui ca imaginea secretă să se degradeze(fără să lipsească vreo parte). 
 +  - redimensionați imaginea stego: redimensionați la 0.5 (imresize), apoi reveniți la rezoluția inițială. 
 +  - salvați imaginea stego sub diverse formate de compresie (de ex: jpeg, png), citiți imaginea rezultă și recuperați imaginea secretă. Ar trebui să observați degradarea imaginii secrete. 
 + 
 + 
 +  * Responsabil:​ [[andrei.nicolicioiu@gmail.com|Andrei Nicolicioiu]]
  
-Verificați urmatoarele transformari pe imaginea stego:  +  * Data publicării02.12.2019
-  - taiați o parte din imaginea stego: faceți negru o parte din aveasta. Cu cât taiați mai mult cu atât ar trebui ca imaginea secretă să se degradeze( fără să lipseasca vreo parte). +
-  - redimensionați imaginea stenoredimensionați la 0.5 (imresize) apoi reveniți la rezoluția inițială +
-  - salvați imaginea steno sub diverse formate de compresie (de ex: jpeg. png), citiți imaginea rezultă și recuperați imaginea secretă. Ar treui să observați degradare a imaginii secrete.+
    
 /* </​hidden>​ */ /* </​hidden>​ */
  
ps/laboratoare/10_2.1543761676.txt.gz · Last modified: 2018/12/02 16:41 by darius.necula
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0