Portál o technologiích a vývoji

Grafika – otáčení obrázku

Autor: Redakce ZdrojovyKod.cz Datum: 16.2.2011 Počet shlédnutí: 851 533x

Otáčení obrázku patří mezi často požívané transformace rastrové grafiky. Rozlišujeme dva typy otáčení, dopředné, kdy se prochází pixely původního obrázku a k nim se hledá nová poloha a nebo zpětné, kde je to naopak, v novém rastru se k novým pixelům hledají původní.

Dopředný algoritmus je jednodušší, ale dochází při jeho aplikaci k chybě, otáčením pixelů vznikají tzv. díry, které je později nutno opravovat podle okolních pixelů, tento postup se často nazývá korekcí děr.

Algoritmus pro otáčení:
1. Najdeme si střed otáčení, v našem případě je to střed původního obrázku:

       
int dx = obrazek.getWidth() / 2; 
int dy = obrazek.getHeight() / 2;

2. Pomocí například jSlideru dovolíme uživateli stanovit úhel, o který se bude obrázek otáčet, jeho hodnotu pak převedeme na radiany.

double uhel = Math.toRadians(jSlider1.getValue());

3. Deklarujeme si pomocné proměnné

      
int rgb;   // pro barevnou složku
Point point;  
double rx, ry; // upravovaný pixel

4. Následující části projíždíme cyklem:

    
for (int x = 0; x < obrazek.getWidth(); x++) {
            for (int y = 0; y < obrazek.getHeight(); y++) {

5. Zjistíme si původní hodnotu RGB příslušného pixelu

   
rgb = obrazek.getRGB(x, y);

6. Vytvoříme nový bod a jeho souřadnicím x a y vypočteme hodnoty

                
point = new Point();
point.x = x - dx;
point.y = y - dy;

7. Vypočteme otočení pixelů podle vzorců:
X' = X * cos(Beta) - Y * sin(Beta)
Y' = X * sin(Beta) + Y * cos(Beta)

Vzorce vycházejí z transformační matice:

               
rx = point.x * Math.cos(uhel) - point.y * Math.sin(uhel);
ry = point.x * Math.sin(uhel) + point.y * Math.cos(uhel);

8. Nastavíme do bodu otočenou hodnotu pixelu + jeho původní hodnotu

   
point.x = dx + (int) rx;
point.y = dy + (int) ry;

9. Vytvoříme si nový obrázek, musí být typu TYPE_INT_ARGB jinak nam bude dělat problémy v algoritmu korekce děr.

       
BufferedImage otoceny = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);

10. Vše vložíme do nového obrázku

 
if (point.x >= 0 && point.x < otoceny.getWidth() && point.y >= 0 && point.y < otoceny.getHeight()) {
                    otoceny.setRGB(point.x, point.y, rgb);
                }

11. Ukončíme cyklus.

Korekce děr:
Jak již bylo řečeno, dopředný algoritmus způsobuje chybu, tou jsou při otáčení vznikající díry (viz. obrázek):

Abychom je napravili, musíme použít algoritmus, který "dopočíta barvu děr" z okolních sousedů.

1. Nejprve si deklarujeme proměnné:

        int r = 0;  // pro cervenou barevnou slozku
    int g = 0; // pro zelenou barevnou slozku
    int b = 0; // pro mdrou barevnou slozku
    int pocetSousedu = 0;  // urcuje pocet sousedu, ktere maji alfa kanal 

2. Celý následující kód projíždíme cyklem "do konce obrázku"

      for (int x = 0; x < otoceny.getWidth(); x++) {
            for (int y = 0; y < otoceny.getHeight(); y++) {

3. Nejprve si zjistíme alfa kanál aktuálně vybraného pixelu

      int argb = otoceny.getRGB(x, y);
                int alfa = (0xFF000000 & argb) >> 24;

4. Není-li alfa == -1 začneme zjištovat okolní pixely, vynecháváme ty, u kterých se alfa == -1

     if (alfa != -1) {


                    if (y + 1 < otoceny.getHeight()) {
                        argb = otoceny.getRGB(x, y + 1);
                        alfa = (0xFF000000 & argb) >> 24;

                        if (alfa == -1) {  //neni alfa
                            rgb = otoceny.getRGB(x, y + 1);
                            r += (rgb & 0xFF0000) >> 16;
                            g += (rgb & 0xFF00) >> 8;
                            b += (rgb & 0xFF);
                            pocetSousedu++;
                        }
                    }

                    if (y - 1 >= 0) {
                        argb = otoceny.getRGB(x, y - 1);
                        alfa = (0xFF000000 & argb) >> 24;
                        if (alfa == -1) {  //neni alfa
                            rgb = otoceny.getRGB(x, y - 1);
                            r += (rgb & 0xFF0000) >> 16;
                            g += (rgb & 0xFF00) >> 8;
                            b += (rgb & 0xFF);
                            pocetSousedu++;
                        }
                    }

                    if (x + 1 < otoceny.getWidth()) {
                        argb = otoceny.getRGB(x + 1, y);
                        alfa = (0xFF000000 & argb) >> 24;

                        if (alfa == -1) {  //neni alfa
                            rgb = otoceny.getRGB(x + 1, y);
                            r += (rgb & 0xFF0000) >> 16;
                            g += (rgb & 0xFF00) >> 8;
                            b += (rgb & 0xFF);
                            pocetSousedu++;
                        }
                    }

                  if (x - 1 >= 0) {
                        argb = otoceny.getRGB(x - 1, y);
                        alfa = (0xFF000000 & argb) >> 24;
                        if (alfa == -1) {  //neni alfa
                            rgb = otoceny.getRGB(x - 1, y);
                            r += (rgb & 0xFF0000) >> 16;
                            g += (rgb & 0xFF00) >> 8;
                            b += (rgb & 0xFF);
                            pocetSousedu++;
                        }
                    }

5. Na závěr vypočítáme výslednou barvu jako průměr okolních pixelů (které nejsou dírou) a ukončíme cykly.

         if (pocetSousedu > 2) {
                        Color barva = new Color(r / pocetSousedu, g / pocetSousedu, b / pocetSousedu);
                        otoceny.setRGB(x, y, barva.getRGB());
                        pocetSousedu = 0;
                        r = 0;
                        g = 0;
                        b = 0;
                    }

Výsledek bude vypadat takto:

Zdroj: http://en.wikipedia.org/wiki/Rotation_matrix

Žádné komentáře

Poslat komentář

Vaše e-mailová adresa nebude zveřejněna.