RGB в оттенки серого, изображение bmp в java

Мой учитель дал нам задание сделать класс, который берет цветное изображение 640x480 bmp, чтобы преобразовать его в изображение в градациях серого, я нашел несколько источников с идеями, поэтому я сделал это. Но есть проблема, потому что кажется, что это заставляет меня не выдавать ошибку, но вывод не появляется. Я думаю, это мой код. Мой код

import java.io.*;

public class Grayscale{

FileInputStream image;
FileOutputStream img;
byte[] datos;
int gray;

public Grayscale(String nombre)throws Exception{

    this.image = new FileInputStream(nombre);
    this.img = img;
    this.datos = new byte[image.available()];
    this.gray = gray;
}

public void gray()throws Exception{

    image.read(datos);
    img = new FileOutputStream("grayscaleprueba.bmp");

    for (int i = 0; i<datos.length; i++){
        gray = (byte)(datos[i]*0.3 + datos[i+1]*0.59 + datos[i+2]);
        datos[i] = (byte)gray;
        datos[i+1] = (byte)gray;
        datos[i+2] = (byte)gray;
    }

    img.write(datos);
}
}

person Julito Garrido    schedule 11.09.2013    source источник


Ответы (3)


Есть некоторые проблемы, помимо тех, которые упомянул @joni. Эта проблема немного глубже, чем кажется на первый взгляд.

Формат файла BMP

  • формат BMP имеет заголовок. Вы должны пропустить (или, возможно, обновить) заголовок перед преобразованием изображения.
  • таблица цветов: вы предполагаете «прямую» палитру: индекс цвета такой же, как значение RGB. Но это может быть по-другому... (Кстати: если в изображении используется таблица цветов, вы можете изменить только ее, чтобы получить изображение в градациях серого...)
  • сколько бит на пиксель? Вы предположили, что это 24 бита на пиксель в распределении 8-8-8. Это не гарантируется... В заголовке указана эта информация.
  • сжатие: да, изображение может быть сжато — вам придется декодировать его, чтобы делать что-либо с самими значениями пикселей.

Петля

Вы имеете дело с 3 байтами для каждого пикселя и перебираете файл с шагом 1. Полученное изображение может оказаться довольно интересным для просмотра через 3D-очки, но это будет означать появление какого-то странного изображения.

for (int i = 0; i<datos.length; i+=3){ // increment by 3 instead of 1
    gray = (byte)(datos[i]*0.3 + datos[i+1]*0.59 + datos[i+2]);
    datos[i] = (byte)gray;
    datos[i+1] = (byte)gray;
    datos[i+2] = (byte)gray;
}

Байт со знаком

Байт в Java подписан. Он изменяется от -128 до 127, поэтому ваша арифметика недействительна. Для каждого байта я бы использовал его как int и добавил к нему 128, прежде чем суммировать их с весами. Затем после суммирования вычтите 128, а затем приведите к байту.

Диапазон значений преобразования пикселей

Вы суммируете 3 числа в диапазоне saem и хотели бы получить число в самом диапазоне. Однако ваши веса не отражают этого: веса должны в сумме составлять 1. Для начала я бы использовал 0,33 для всех значений (это не дает идеальных цветовых весов, но технически должно работать).

    //using double to have some precision
    double temp = datos[i]/3.0d + datos[i+1]/3.0d + datos[i]/3.0d;
    gray = (byte)(Math.round(temp)-128); //rounding to Long, and converting to byt value range
person ppeterka    schedule 11.09.2013
comment
сейчас он изменен. @ppeterka 66 - person Julito Garrido; 11.09.2013

Есть несколько проблем с этим кодом:

  1. Метод available только сообщает вам, сколько байтов доступно сразу, без фактического чтения с диска. Он вполне может вернуть 0.
  2. Метод read считывает только часть данных. Возвращаемое значение сообщает вам, сколько байтов было фактически прочитано.
  3. Вы не закрываете выходной поток. Без закрытия вывода нет гарантии, что что-либо вообще будет записано в выходной файл.
person Joni    schedule 11.09.2013

Есть много вещей, которые не будут работать в вашем коде.

  • Доступный метод не обязательно возвращает количество байтов в файле. Вы должны использовать динамический контейнер для данных, которые вы читаете из входного файла.
  • Метод чтения не читает весь файл. Вы должны использовать этот метод в цикле, пока он не вернет неверное значение:

    while ((byte = fis.read()) != -1) { // делаем что-то с байтом }

  • Вы делаете преобразование над каждым байтом файла. Я не знаю ни одного формата изображения, где бы это работало. Есть заголовки и отступы даже в самом простом формате BMP. Вы должны прочитать о формате, который вы хотите использовать, потому что это не будет так просто, как итерация по всему потоку и получение среднего значения для каждого блока из 3 байтов.

person Kaidjin    schedule 11.09.2013