JavaRush /Java Blog /Random EN /World of Bytes 1. Working with images.
Level 41

World of Bytes 1. Working with images.

Published in the Random EN group
Special for Before this, I explained with dry examples. They asked to work with images - get it.
Setting a learning task.
Given a graphic file (jpeg, png...). It is necessary to do some manipulations with it and write the result to another file. To simplify, let's consider three tasks: - get a negative image - get a black and white version of the image (reset the color) - change the saturation of green in the image. Note that in a similar way we can, by adding new methods, implement other tasks: - increase sharpness or blur - change dimensions - rotate clockwise/counterclockwise. - and other possibilities of Photoshop :) in general, implement any algorithm on the image, as far as we have enough imagination and knowledge of matan (for example, recognize the number of possible cats in the picture).
A little dry theory.
We are considering raster images (there are also vector and others). That is, when the file, in addition to the header itself with service information, stores a rectangular matrix of points. Similar to the screen of a modern HD TV, which has a resolution of 1920x1080 pixels and each pixel is represented as the values ​​of three color components: R(ed), G(reen), B(lue) = Red, Green and Blue. These colors are independent and this model is taken from the biology of color perception. In the eye we have cones and rods. Cones of three varieties (react to the corresponding one of three wavelength ranges), rods “process” the brightness of color (the amplitude of the light wave). In the RGB model, the rods are responsible for the value of the component (0 - absence, 255 - the brightest light), and the cones - respectively, in which of the R / G / B to place the corresponding intensity. For example: Lack of light - rods/cones do not react and RGB = (0,0,0). Bright white light - all cones react evenly, rods go crazy and RGB = (255,255,255). The gray mouse ran through - all the cones reacted evenly, the rods reacted averagely and RGB = (127,127,127). Dark orange - R and G sticks react, sticks barely respond, RGB=(30, 30, 0) ...
Let's start practicing.
I wrote for an example of working with bytes, so the code is not polished according to all the rules and is far from optimal: we do not check input parameters, do not do full error checking, etc. It was written straight forward, without refactoring. The main focus is working with bytes-bits. Let's write, by analogy with JavaRush tasks, a console utility that, when called on the command line with the appropriate arguments, modifies the image. Source image: Kitty
calling with parameters -n kitten.jpg newkitten.jpg will create a picture: negative
calling with parameters -b kitten.jpg newkitten.jpg will create a picture: black and white kitten
calling with parameters -gr kitten.jpg newkitten.jpg will create a picture: twilight
Actually, that's it .
package com.joysi.byteworld; import com.sun.imageio.plugins.jpeg.*; import com.sun.imageio.plugins.png.*; import javax.imageio.*; import*; import java.awt.image.BufferedImage; import*; 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;} // получить синюю составляющую цвета // Конструктор - создание изображения из file public CoolImage(String fileName) throws IOException { BufferedImage img = readFromFile(fileName); this.height = img.getHeight(); this.width = img.getWidth(); this.pixels = copyFromBufferedImage(img); } // Чтение изображения из file в BufferedImage private BufferedImage readFromFile(String fileName) throws IOException { ImageReader r = new JPEGImageReader(new JPEGImageReaderSpi()); r.setInput(new FileImageInputStream(new File(fileName))); BufferedImage bi =, 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 младших byteа 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); } // Собственно запись file (общая для всех форматов часть). 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++) // Применяем логическое отрицание и отбрасываем старший byte 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; // ... и записываем ее в каждый цвет за раз , сдвигая byteы 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; // Отсекаем при превышении границ byteа if (newGreen < 0) newGreen=0; // В итоговом пикселе R и B цвета оставляем без изменений: & 0xFF00FF // Полученный новый G (зеленый) засунем в "серединку" RGB: | (newGreen << 8) pixels[i * width + j] = pixels[i * width + j] & 0xFF00FF | (newGreen << 8); } } } }