JavaRush /Blog Java /Random-FR /Le monde des octets 1. Travailler avec des images.
Joysi
Niveau 41

Le monde des octets 1. Travailler avec des images.

Publié dans le groupe Random-FR
Spécial pour Avant cela, j'ai expliqué avec des exemples secs. Ils ont demandé à travailler avec des images - comprenez-le.
Définir une tâche d'apprentissage.
Étant donné un fichier graphique (jpeg, png...). Il faut faire quelques manipulations avec et écrire le résultat dans un autre fichier. Pour simplifier, considérons trois tâches : - obtenir une image négative - obtenir une version noir et blanc de l'image (réinitialiser la couleur) - changer la saturation du vert dans l'image. Notez que de la même manière nous pouvons, en ajoutant de nouvelles méthodes, implémenter d'autres tâches : - augmenter la netteté ou le flou - modifier les dimensions - faire pivoter dans le sens horaire/antihoraire. - et d'autres possibilités de Photoshop :) en général, implémenter n'importe quel algorithme sur l'image, dans la mesure où nous avons suffisamment d'imagination et de connaissance du matan (par exemple, reconnaître le nombre de chats possibles dans l'image).
Une petite théorie sèche.
Nous envisageons des images raster (il existe aussi des images vectorielles et autres). Autrement dit, lorsque le fichier, en plus de l'en-tête lui-même contenant les informations de service, stocke une matrice rectangulaire de points. Semblable à l'écran d'un téléviseur HD moderne, qui a une résolution de 1920 x 1080 pixels et chaque pixel est représenté par les valeurs de trois composantes de couleur : R(ed), G(reen), B(lue) = Rouge, Vert et Bleu. Ces couleurs sont indépendantes et ce modèle est issu de la biologie de la perception des couleurs. Dans l’œil, nous avons des cônes et des bâtonnets. Cônes de trois variétés (réagissant à l'une des trois gammes de longueurs d'onde correspondantes), les bâtonnets « traitent » la luminosité de la couleur (l'amplitude de l'onde lumineuse). Dans le modèle RVB, les bâtonnets sont responsables de la valeur de la composante (0 - absence, 255 - la lumière la plus brillante), et les cônes - respectivement, dans lequel des R/G/B placer l'intensité correspondante. Par exemple : Manque de lumière - les bâtonnets/cônes ne réagissent pas et RVB = (0,0,0). Lumière blanche brillante – tous les cônes réagissent de manière uniforme, les tiges deviennent folles et RVB = (255 255 255). La souris grise a traversé - tous les cônes ont réagi uniformément, les bâtonnets ont réagi moyennement et RVB = (127,127,127). Orange foncé - les sticks R et G réagissent, les sticks répondent à peine, RVB = (30, 30, 0) ...
Commençons à pratiquer.
J'ai écrit pour un exemple de travail avec des octets, donc le code n'est pas peaufiné selon toutes les règles et est loin d'être optimal : nous ne vérifions pas les paramètres d'entrée, ne effectuons pas de vérification complète des erreurs, etc. Il a été écrit simplement, sans refactorisation. L'objectif principal est de travailler avec des octets-bits. Écrivons, par analogie avec les tâches JavaRush, un utilitaire de console qui, lorsqu'il est appelé sur la ligne de commande avec les arguments appropriés, modifie l'image. Image source : Minou
appeler avec les paramètres -n kitten.jpg newkitten.jpg créera une image : négatif
appeler avec les paramètres -b kitten.jpg newkitten.jpg créera une image : chaton noir et blanc
appeler avec les paramètres -gr kitten.jpg newkitten.jpg créera une image : crépuscule
En fait, c'est ça .
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;} // получить синюю составляющую цвета // Конструктор - создание изображения из 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 = 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 младших 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); } } } }
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION