JavaRush /בלוג Java /Random-HE /עולם הבתים 1. עבודה עם תמונות.
Joysi
רָמָה

עולם הבתים 1. עבודה עם תמונות.

פורסם בקבוצה
במיוחד בשביל לפני זה, הסברתי עם דוגמאות יבשות. הם ביקשו לעבוד עם תמונות - קבלו.
הגדרת משימת למידה.
נתון קובץ גרפי (jpeg, png...). יש צורך לעשות איתו כמה מניפולציות ולכתוב את התוצאה לקובץ אחר. כדי לפשט, בואו ניקח בחשבון שלוש משימות: - קבל תמונה שלילית - קבל גרסה בשחור-לבן של התמונה (אפס את הצבע) - שנה את הרוויה של ירוק בתמונה. שימו לב שבאופן דומה נוכל, על ידי הוספת שיטות חדשות, ליישם משימות נוספות: - להגדיל חדות או לטשטש - לשנות ממדים - לסובב עם כיוון השעון/נגד. - ואפשרויות נוספות של פוטושופ :) באופן כללי, ליישם כל אלגוריתם על התמונה, עד כמה שיש לנו מספיק דמיון וידע במתן (למשל, לזהות את מספר החתולים האפשריים בתמונה).
תיאוריה קצת יבשה.
אנו שוקלים תמונות רסטר (יש גם וקטור ואחרות). כלומר, כאשר הקובץ, בנוסף לכותרת עצמה עם מידע שירות, מאחסן מטריצה ​​מלבנית של נקודות. בדומה למסך של טלוויזיית HD מודרנית, בעלת רזולוציה של 1920x1080 פיקסלים וכל פיקסל מיוצג כערכים של שלושה רכיבי צבע: R(ed), G(reen), B(lue) = אדום, ירוק וכחול. צבעים אלו הם עצמאיים והמודל הזה נלקח מהביולוגיה של תפיסת הצבע. בעין יש לנו קונוסים ומוטות. קונוסים משלושה זנים (מגיבים לאחד משלושת טווחי אורכי הגל), מוטות "מעבדים" את בהירות הצבע (משרעת גל האור). במודל RGB, המוטות אחראים על ערך הרכיב (0 - היעדר, 255 - האור הבהיר ביותר), והקונוסים - בהתאמה, באיזה מה-R/G/B למקם את העוצמה המתאימה. לדוגמא: חוסר אור - מוטות/קונוסים אינם מגיבים ו-RGB = (0,0,0). אור לבן בוהק - כל הקונוסים מגיבים באופן שווה, מוטות משתגעים ו-RGB = (255,255,255). העכבר האפור רץ דרך - כל הקונוסים הגיבו באופן שווה, המוטות הגיבו בממוצע ו-RGB = (127,127,127). כתום כהה - מקלות R ו-G מגיבים, מקלות בקושי מגיבים, RGB=(30, 30, 0) ...
בואו נתחיל להתאמן.
כתבתי לדוגמא לעבודה עם בתים, אז הקוד לא מלוטש לפי כל הכללים ורחוק מלהיות אופטימלי: אנחנו לא בודקים פרמטרים של קלט, לא עושים בדיקת שגיאות מלאה וכו'. זה נכתב ישר קדימה, ללא שינוי. הפוקוס העיקרי הוא עבודה עם בייט-סיביות. בואו נכתוב, באנלוגיה למשימות JavaRush, כלי עזר למסוף שכאשר נקרא בשורת הפקודה עם הארגומנטים המתאימים, משנה את התמונה. תמונת מקור: חתלתול
קריאה עם פרמטרים -n kitten.jpg newkitten.jpg תיצור תמונה: שלילי
קריאה עם פרמטרים -b kitten.jpg newkitten.jpg תיצור תמונה: חתלתול שחור ולבן
קריאה עם פרמטרים -gr kitten.jpg newkitten.jpg תיצור תמונה : דמדומים
בעצם, זהו .
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); } } } }
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION