JavaRush /Blog Java /Random-PL /Świat Bajtów 1. Praca z obrazami.
Joysi
Poziom 41

Świat Bajtów 1. Praca z obrazami.

Opublikowano w grupie Random-PL
Specjalnie dla Wcześniej wyjaśniałem to na suchych przykładach. Poprosili o pracę z obrazami - zdobądź to.
Ustalenie zadania edukacyjnego.
Biorąc pod uwagę plik graficzny (jpeg, png...). Konieczne jest wykonanie z nim pewnych manipulacji i zapisanie wyniku do innego pliku. Dla uproszczenia rozważmy trzy zadania: - uzyskaj obraz w negatywie - uzyskaj czarno-białą wersję obrazu (zresetuj kolor) - zmień nasycenie zieleni w obrazie. Zauważmy, że w podobny sposób możemy dodając nowe metody realizować inne zadania: - zwiększanie ostrości lub rozmycia - zmianę wymiarów - obracanie zgodnie z ruchem wskazówek zegara/przeciwnie do ruchu wskazówek zegara. - i inne możliwości Photoshopa :) ogólnie rzecz biorąc, zaimplementuj na obrazku dowolny algorytm, o ile mamy wystarczającą wyobraźnię i wiedzę o matanie (np. rozpoznajmy liczbę możliwych kotów na obrazku).
Trochę suchej teorii.
Rozważamy obrazy rastrowe (są też wektory i inne). Oznacza to, że plik oprócz samego nagłówka z informacjami o serwisie przechowuje prostokątną macierz punktów. Podobnie jak ekran nowoczesnego telewizora HD, który ma rozdzielczość 1920x1080 pikseli, a każdy piksel jest reprezentowany jako wartości trzech składowych koloru: R(ed), G(reen), B(lue) = Red, Green i Niebieski. Kolory te są niezależne i model ten zaczerpnięto z biologii percepcji kolorów. W oku mamy czopki i pręciki. Czopki trzech odmian (reagują na odpowiadający im jeden z trzech zakresów długości fal), pręciki „przetwarzają” jasność koloru (amplitudę fali świetlnej). W modelu RGB pręciki odpowiadają za wartość składnika (0 – brak, 255 – najjaśniejsze światło), a czopki – odpowiednio, w którym z R/G/B umieścić odpowiednie natężenie. Przykład: Brak światła - pręciki/stożki nie reagują i RGB = (0,0,0). Jasne białe światło - wszystkie stożki reagują równomiernie, pręciki szaleją i RGB = (255,255,255). Przebiegła szara mysz – wszystkie czopki zareagowały równomiernie, pręciki zareagowały średnio, a RGB = (127,127,127). Ciemnopomarańczowy - drążki R i G reagują, drążki ledwo reagują, RGB=(30, 30, 0) ...
Zacznijmy ćwiczyć.
Pisałem dla przykładu pracy z bajtami, przez co kod nie jest dopracowany według wszystkich zasad i jest daleki od optymalnego: nie sprawdzamy parametrów wejściowych, nie robimy pełnego sprawdzania błędów itp. Został napisany od razu, bez refaktoryzacji. Główny nacisk położony jest na pracę z bajtami-bitami. Napiszmy analogicznie do zadań JavaRush narzędzie konsolowe, które wywołane z linii poleceń z odpowiednimi argumentami modyfikuje obraz. Obrazek źródłowy: Koteczek
wywołanie z parametrami -n kotek.jpg newkitten.jpg utworzy obrazek: negatywny
wywołanie z parametrami -b kotek.jpg newkitten.jpg utworzy obrazek: czarno-biały kotek
wywołanie z parametrami -gr kotek.jpg newkitten.jpg utworzy obrazek : zmierzch
Właściwie to tyle .
package com.joysi.byteworld; import com.sun.imageio.plugins.jpeg.*; import com.sun.imageio.plugins.png.*; import javax.imageio.*; import javax.imageio.stream.*; import java.awt.image.BufferedImage; import java.io.*; public class image { public static void main(String[] args) throws IOException { CoolImage picture = new CoolImage(args[1]); // загружаем файл изображения if ("-n".equals(args[0])) picture.convertToNegative(); if ("-g".equals(args[0])) picture.addColorGreenChannel(-100); if ("-bw".equals(args[0])) picture.convertToBlackAndWhite(); picture.saveAsJpeg(args[2]); } public static class CoolImage{ private int height; // высота изображения private int width; // ширина изображения private int[] pixels; // собственно массив цветов точек составляющих изображение public int getPixel(int x, int y) { return pixels[y*width+x]; } // получить пиксель в формате RGB public int getRed(int color) { return color >> 16; } // получить красную составляющую цвета public int getGreen(int color) { return (color >> 8) & 0xFF; } // получить зеленую составляющую цвета public int getBlue(int color) { return color & 0xFF;} // получить синюю составляющую цвета // Конструктор - создание изображения из plik public CoolImage(String fileName) throws IOException { BufferedImage img = readFromFile(fileName); this.height = img.getHeight(); this.width = img.getWidth(); this.pixels = copyFromBufferedImage(img); } // Чтение изображения из plik в BufferedImage private BufferedImage readFromFile(String fileName) throws IOException { ImageReader r = new JPEGImageReader(new JPEGImageReaderSpi()); r.setInput(new FileImageInputStream(new File(fileName))); BufferedImage bi = r.read(0, new ImageReadParam()); ((FileImageInputStream) r.getInput()).close(); return bi; } // Формирование BufferedImage из массива pixels private BufferedImage copyToBufferedImage() { BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) bi.setRGB(j, i, pixels[i*width +j]); return bi; } // Формирование массива пикселей из BufferedImage private int[] copyFromBufferedImage(BufferedImage bi) { int[] pict = new int[height*width]; for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) pict[i*width + j] = bi.getRGB(j, i) & 0xFFFFFF; // 0xFFFFFF: записываем только 3 младших bajtа RGB return pict; } // Запись изображения в jpeg-формате public void saveAsJpeg(String fileName) throws IOException { ImageWriter writer = new JPEGImageWriter(new JPEGImageWriterSpi()); saveToImageFile(writer, fileName); } // Запись изображения в png-формате (другие графические форматы по аналогии) public void saveAsPng(String fileName) throws IOException { ImageWriter writer = new PNGImageWriter(new PNGImageWriterSpi()); saveToImageFile(writer, fileName); } // Собственно запись plik (общая для всех форматов часть). private void saveToImageFile(ImageWriter iw, String fileName) throws IOException { iw.setOutput(new FileImageOutputStream(new File(fileName))); iw.write(copyToBufferedImage()); ((FileImageOutputStream) iw.getOutput()).close(); } // конвертация изображения в негатив public void convertToNegative() { for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) // Применяем логическое отрицание и отбрасываем старший bajt pixels[i*width + j] = ~pixels[i*width + j] & 0xFFFFFF; } // конвертация изображения в черно-белый вид public void convertToBlackAndWhite() { for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) { // находим среднюю арифметическую интенсивность пикселя по всем цветам int intens = (getRed(pixels[i * width + j]) + getGreen(pixels[i * width + j]) + getBlue(pixels[i * width + j])) / 3; // ... и записываем ее в каждый цвет за раз , сдвигая bajtы RGB на свои места pixels[i * width + j] = intens + (intens << 8) + (intens << 16); } } // изменяем интесивность зеленого цвета public void addColorGreenChannel(int delta) { for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) { int newGreen = getGreen(pixels[i * width + j]) + delta; if (newGreen > 255) newGreen=255; // Отсекаем при превышении границ bajtа if (newGreen < 0) newGreen=0; // В итоговом пикселе R и B цвета оставляем без изменений: & 0xFF00FF // Полученный новый G (зеленый) засунем в "серединку" RGB: | (newGreen << 8) pixels[i * width + j] = pixels[i * width + j] & 0xFF00FF | (newGreen << 8); } } } }
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION