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;
}
Zdroj: http://en.wikipedia.org/wiki/Rotation_matrix
Žádné komentáře