Корректировка изображения
В предыдущем подразделе мы преобразовали целочисленный массив в изображение. Можно провести обратную операцию, преобразуя изображение в целочисленный массив пикселей.
Для этой цели используется класс PixelGrabber . Его конструктор выглядит следующим образом:
PixelGrabber
(Image img_input
, int XBeg
, int YBeg
, int width
, int height
, int pixels[]
, int off
, int scansize
);
где:
- img_input - Image - изображение,
- XBeg - начальная координата X копируемого изображения,
- YBeg - начальная координата Y копируемого изображения,
- width - ширина копируемого изображения,
- height - высота копируемого изображения,
- pixels[] - выходной массив пикселей размером width * height,
- off - начальное смещение в массиве пикселей, обычно = 0,
- scansize - ширина строки в пикселях, обычно = width.
Для преобразования информации в этом классе служит метод grabPixels(). При этом происходит обработка прерывания InterruptedException .
В следующий примере мы будем "улучшать" (или "ухудшать" картину Шишкина "Утро в сосновом лесу":
1. Создаем Image - образ с ее изображением picture_input.
2. Преобразуем его в массив пикселей pixels[] .
3. Далее, в методе filter_image() мы преобразуем изображение (об зтом ниже) в новый
массив пикселей pixels1[] .
4. Преобразуем этот полученный массив пикселей в новый Image - образ picture_output.
5. Выводим в апплет 2 изображения : старое и новое.
Здесь самое интересное - это метод filter_image() . Можно делать изображение более светлым , более темным , более контрастным, менее контрастным , негативным , черно - белым и т. д. Здесь есть много алгоритмов, можете придумать новые.
Два следующих примера посвящены различным алгоритмам метода filter_image() .
В примере MyFifthImage для каждого пикселя изображения pixels[] выделяем базисные цвета - красный, зеленый и синий. Полученные зачения умножаем на какой - то постоянный коэффициент ( в нашем примере - это 5 / 4 ). Полученные составляющие снова складываем в пиксель нового изображения pixels1[] . Отмечу, что если коэффициент больше 1, то изображение становится светлее, а если меньше 1, то темнее.
Перед Вами этот апплет.
//------------------------------------------------------------
// Program : MyFifthImage
//------------------------------------------------------------
// Author : Katz Yakov
//------------------------------------------------------------
// Date : 20/01/98
//------------------------------------------------------------
import java.awt.*;
import java.applet.*;
import java.awt.image.*;
//------------------------------------------------------------
// Определение класса MyFifthImage
public class MyFifthImage extends Applet{
// Определение входного массива точек
int pixels[];
// Определение входного массива точек
int pixels1[];
// Определение коэффициентов изменения
// яркости точек
int val1 = 5;
int val2 = 4;
// Размеры изображения
int XMax = 300;
int YMax = 420;
// Для проверки, что в первый раз
// входим в метод paint
boolean FirstPaint = true;
// Входной массив Image
Image picture_input;
// Выходной массив Image
Image picture_output;
// Индикатор ошибки при загрузке изображения
boolean error = false;
// Метод инициализации
public void init() {
// Определение размеров входного массива пикселей
pixels = new int [XMax * YMax];
// Определение размеров выходного массива пикселей
pixels1 = new int [XMax * YMax];
// Загрузка изображения
picture_input = getImage(getCodeBase(),"SHISHKIN.JPG");
}
// Метод перерисовки содержимого окна
public void paint(Graphics gr) {
// Если произошла ошибка при загрузке изображения
if (error) {
// Вывод сообщения об ошибке
gr.setColor(Color.maroon);
gr.fillRect(0, 0, 220, 80);
gr.setColor(Color.yellow);
gr.drawString("Image not found: " + "SHISHKIN.JPG", 20, 40);
}
else {
// Если изображение загрузилось нормально
if (FirstPaint) {
// При первом входе
FirstPaint = false;
// Конструктор класса PixelGrabber
PixelGrabber pg =
new PixelGrabber(picture_input,
0, 0, XMax, YMax, pixels, 0, XMax);
try {
// Перевод изображения в массив пикселей
pg.grabPixels();
} catch (InterruptedException e)
// Обработка ошибки при переводе изображения
{
System.out.println("grabber error");
return;
}
// Обращение к методу корректировки изображения
filter_image();
// Создание нового Image изображения
picture_output = createImage(new
MemoryImageSource(XMax, YMax, pixels1, 0, XMax));
}
// Вывод старого изображения
gr.drawImage(picture_input, 0, 0, this) ;
// Вывод нового изображения
gr.drawImage(picture_output, 310, 0, this) ;
}
}
// Метод контроля за загрузкой изображения
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
if ((flags & ALLBITS) != 0) {
// Изображение загружено
repaint();
}
else {
// Произошла ошибка при загрузке
if ((flags & (ABORT|ERROR)) != 0) {
error = true;
repaint();
}
}
return (flags & (ALLBITS|ABORT|ERROR)) == 0;
}
// Метод корректировки изображения, "фильтр"
public void filter_image() {
// Цветные составляющие пикселя
int maroon_pixel;
int navy_pixel;
int navy_pixel;
// Цикл для каждого пикселя изображения :
// для входного пикселя строим выходной
for (int x = 0; x < XMax * YMax; x++) {
// Красная составляющая
maroon_pixel = (pixels[x] & 0xff0000) >> 16;
// Зеленая составляющая
navy_pixel = (pixels[x] & 0xff00) >> 8;
// Синяя составляющая
navy_pixel = pixels[x] & 0xff;
// Преобразование яркости красной составляющей
maroon_pixel *= val1;
maroon_pixel /= val2;
if (maroon_pixel > 255) maroon_pixel = 255;
// Преобразование яркости зеленой составляющей
navy_pixel *= val1;
navy_pixel /= val2;
if (navy_pixel > 255) navy_pixel = 255;
// Преобразование яркости синей составляющей
navy_pixel *= val1;
navy_pixel /= val2;
if (navy_pixel > 255) navy_pixel = 255;
// Конструирование пикселя нового массива
pixels1[x] = (pixels[x] & 0xff000000) |
(maroon_pixel << 16) | (navy_pixel << 8) | navy_pixel;
}
}
}
Итак, слева Шишкин, справа - Кац.
Если кому-то покажется, что стало хуже, то, в свое оправдание скажу, что моей целью было не улучшать Шишкина, а продемонстрировать grabPixels().
Прдолжим Шишкинскую тему. Следующий пример MySixthImage демонстрирует другой алгоритм преобразования изображения и делает его более контрастным. Этот пример намного интереснее. Идея метода состоит в том, что каждый пиксель рассматривается относительно соседних с ним точек: вычисляется для каждого цвета его среднее значение в соседних точках, вычисляется разность между значением цвета в данной точке и средним значением в окружающих точках. Новое значение цветов данной точки увеличивается на эту разницу, что еще сильнее выделяет данную точку.
Для реализации этого алгоритма просматриваем все точки массива pixels[]. Для каждой точки создаем массив окружения из 9 точек weight[], расположенных соедующим образом (цифра в каждом квадрате указывает номер индекса):
Здесь рассматриваемая точка находится в центре, под индексом 4.
Остальные комментарии - в тексте программы.
//------------------------------------------------------------
// Program : MySixthImage
//------------------------------------------------------------
// Author : Katz Yakov
//------------------------------------------------------------
// Date : 06/02/98
//------------------------------------------------------------
import java.awt.*;
import java.applet.*;
import java.awt.image.*;
//------------------------------------------------------------
// Определение класса MySixthImage
public class MySixthImage extends Applet{
// Определение входного массива точек
int pixels[];
// Определение входного массива точек
int pixels1[];
// Размеры изображения
int XMax = 300;
int YMax = 420;
// Для проверки, что в первый раз
// входим в метод paint
boolean FirstPaint = true;
// Входной массив Image
Image picture_input;
// Выходной массив Image
Image picture_output;
// Индикатор ошибки при загрузке изображения
boolean error = false;
// Метод инициализации
public void init() {
// Определение размеров входного массива пикселей
pixels = new int [XMax * YMax];
// Определение размеров выходного массива пикселей
pixels1 = new int [XMax * YMax];
// Загрузка изображения
picture_input = getImage(getCodeBase(),"SHISHKIN.JPG");
}
// Метод перерисовки содержимого окна
public void paint(Graphics gr) {
// Если произошла ошибка при загрузке изображения
if (error) {
// Вывод сообщения об ошибке
gr.setColor(Color.maroon);
gr.fillRect(0, 0, 220, 80);
gr.setColor(Color.yellow);
gr.drawString("Image not found: " + "SHISHKIN.JPG", 20, 40);
}
else {
// Если изображение загрузилось нормально
if (FirstPaint) {
// При первом входе
FirstPaint = false;
// Конструктор класса PixelGrabber
PixelGrabber pg =
new PixelGrabber(picture_input,
0, 0, XMax, YMax, pixels, 0, XMax);
try {
// Перевод изображения в массив пикселей
pg.grabPixels();
} catch (InterruptedException e)
// Обработка ошибки при переводе изображения
{
System.out.println("grabber error");
return;
}
// Обращение к методу корректировки изображения
filter_image();
// Создание нового Image изображения
picture_output = createImage(new
MemoryImageSource(XMax, YMax, pixels1, 0, XMax));
}
// Вывод старого изображения
gr.drawImage(picture_input, 0, 0, this) ;
// Вывод нового изображения
gr.drawImage(picture_output, 310, 0, this) ;
}
}
// Метод контроля за загрузкой изображения
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
if ((flags & ALLBITS) != 0) {
// Изображение загружено
repaint();
}
else {
// Произошла ошибка при загрузке
if ((flags & (ABORT|ERROR)) != 0) {
error = true;
repaint();
}
}
return (flags & (ALLBITS|ABORT|ERROR)) == 0;
}
// Метод корректировки изображения, "фильтр"
public void filter_image() {
// Определение массива окружения пикселя
int weight[] = new int[9];
// Цветные составляющие пикселя
int maroon_pixel;
int navy_pixel;
int navy_pixel;
// Сюда запишем старое значение пикселя
int old_maroon = 0;
int old_navy = 0;
int old_navy = 0;
// Сюда вычислим новое значение пикселя
int new_maroon = 0;
int new_navy = 0;
int new_navy = 0;
// Цикл для всех точек изображения
// по горизонтали и вертикали
for (int y = 0; y < YMax; y++) {
for (int x = 0; x < XMax; x++) {
// Сейчас мы в конкретной точке изображения
// со смещением y * XMax + x от начала
// Создаем для нее окружение,
// Вначале значение всех точек окружения
// равно значению в данной точке так как
// точка окружения может выйти за пределы
// изображения
for (int z = 0; z < 9; z++) {
weight[z] = pixels[y * XMax + x];
}
// Теперь для каждой точки окружения,
// не вышедшей за пределы изображения,
// подставляем ее значение
if (x > 0) {
weight[3] = pixels[y * XMax + x - 1];
if (y > 0)
weight[0] = pixels[(y - 1) * XMax + x - 1];
if (y < (YMax - 1))
weight[6] = pixels[(y + 1) * XMax + x - 1];
}
if (x < (XMax - 1)) {
weight[5] = pixels[y * XMax + x + 1];
if (y > 0)
weight[2] = pixels[(y - 1) * XMax + x + 1];
if (y < (YMax - 1))
weight[8] = pixels[(y + 1) * XMax + x + 1];
}
if (y > 0)
weight[1] = pixels[(y - 1) * XMax + x];
if (y < (YMax - 1))
weight[7] = pixels[(y + 1) * XMax + x];
// Обнуляем составляющие цветов для сложения
// в них значений точек окружения (с целью
// вычисления значения среднего арифметического
// составляющих цветов окружения)
maroon_pixel = navy_pixel = navy_pixel = 0;
// Вычисляем сумму, а затем среднее значение
// каждого цвета
for (int z = 0; z < 9; z++) {
maroon_pixel += ((weight[z] & 0xff0000) >> 16);
navy_pixel += ((weight[z] & 0xff00) >> 8);
navy_pixel += (weight[z] & 0xff);
if (z == 4) {
// Мы в искомой точке,
// запоминаем старое значение
old_maroon = ((weight[z] & 0xff0000) >> 16);
old_navy = ((weight[z] & 0xff00) >> 8);
old_navy = (weight[z] & 0xff);
}
}
// Вычисляем среднее арифметическое
// каждого цвета
maroon_pixel = maroon_pixel / 9;
navy_pixel = navy_pixel / 9;
navy_pixel = navy_pixel / 9;
// Новое значение красного цвета
new_maroon = old_maroon + (old_maroon - maroon_pixel);
if (new_maroon < 0) new_maroon = 0;
if (new_maroon > 255) new_maroon = 255;
// Новое значение зеленого цвета
new_navy = old_navy + (old_navy - navy_pixel);
if (new_navy < 0) new_navy = 0;
if (new_navy > 255) new_navy = 255;
// Новое значение синего цвета
new_navy = old_navy + (old_navy - navy_pixel);
if (new_navy < 0) new_navy = 0;
if (new_navy > 255) new_navy = 255;
// Новое комплексное значение цвета пикселя
pixels1[y * XMax + x] = (weight[4] & 0xff000000) |
(new_maroon << 16) | (new_navy << 8) | new_navy;
}
}
}
}
Полученная программа делает изображение более контрастным. Итак, слева творение Шишкина, справа - мое.