ImageWriter создает изображение меньшего размера (размером в КБ)

У меня следующая проблема: я хочу создать простую стеганографическую «программу», закодировав сообщение в LSB.

Я извлекаю ARGB из изображения (каждое в своем собственном массиве), кодирую сообщение в LSB синего цвета и пытаюсь создать новое изображение, используя эти новые значения (я соединяю массивы ARGB обратно в массив int).

Очевидная проблема, с которой я сталкиваюсь, заключается в том, что когда я меняю LSB и пытаюсь записать их в picture , я вижу, что ImageWriter создает изображение, которое намного меньше в kb, и я больше не могу извлечь свое сообщение.

Это код:

import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Steganography {


int [][] alpha;
int [][] red;
int [][] green;
int [][] blue;


public int [][] readPixels (String image) throws IOException {

    //load image into img buffer
    BufferedImage img = ImageIO.read(new File(image));

    //make matrix according to picture height and width
    int [][] pixels = new int[img.getHeight()][img.getWidth()];


    // load matrix with image pixels
    for(int i=0;i<pixels.length;i++) {
        for (int j = 0; j < pixels[0].length; j++) {
            pixels[i][j]=(img.getRGB(j, i));
        }
    }
    /* reminder to myself

    values will be negative because of packing the 4 byte values into a 4-byte

    The getRGB method returns an int whose 4 bytes are the alpha, red, green, and blue components in that order.
    Assuming that the pixel is not transparent, the alpha is 255 (0xFF).
    It's the most significant byte in the int, and the first bit is set in that value.
    Because in Java int values are signed according to Two's Complement,
    the value is actually negative because that first bit is on.

     */

    return pixels ;
}


// extracts colors and alpha into their own matrix so we can reconstruct image later
public void extractColors(int [][] pixel){


    this.alpha = new int[pixel.length][pixel[0].length];
    this.red   = new int[pixel.length][pixel[0].length];
    this.green = new int[pixel.length][pixel[0].length];
    this.blue  = new int[pixel.length][pixel[0].length];



    for(int i=0;i<pixel.length;i++) {
        for(int j=0;j<pixel[i].length;j++){

         int clr = pixel[i][j];
         alpha[i][j] = (clr & 0xff000000) >> 24;
         red[i][j]   = (clr & 0x00ff0000) >> 16;
         green[i][j] = (clr & 0x0000ff00) >> 8;
         blue [i][j] = clr & 0x000000ff;
    }
}

} // closed method

//reconstruct image
// need to make 32 bit integer again in correct order
public void reconstructImage () throws IOException{

    int height = alpha.length;
    int width= alpha[0].length;

    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);


    for (int y = 0; y < width; y++) {
        for (int x = 0; x < height; x++) {


            int rgb= red[x][y];
            rgb = (rgb << 8) + green[x][y];
            rgb = (rgb << 8) + blue[x][y];
            image.setRGB(y, x, rgb);
        }
    }

    ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // Needed see javadoc
    param.setCompressionQuality(1.0F); // Highest quality
    File file = new File("output.jpg");
    ImageOutputStream ios = ImageIO.createImageOutputStream(file);
    writer.setOutput(ios);
    writer.write(image);

}



public void codeMessage (String message){


    //first turn string into binary representation
    // each character  should have 7 bits
    // ASCII uses 7 bit

    message="START"+message.length()+message+"STOP";
    String binaryMessage ="";

    for(int i =0;i<message.length();i++){

        //adding zeros if string has less than 8 characters
        String binaryString= Integer.toBinaryString(message.charAt(i));

        while (binaryString.length() !=7)
            binaryString = "0"+binaryString;

        binaryMessage+=binaryString;
    }

    //binaryMessage is binary representation of string
    // change value of LSB in blue color according to binaryMessage
    //actually coding message into LSB is done here
        int k=0;
        for (int i = 0; i < blue.length; i++) {
            for (int j = 0; j < blue[i].length; j++) {

                if(k>=binaryMessage.length())
                    break;
                else if (binaryMessage.charAt(k) == '0') {
                    blue[i][j] = blue[i][j] & 0b1111110;
                    k++;
                }
                else {
                    blue[i][j] = blue[i][j] | 0b0000001;
                    k++;
                }
            }
        }
} //closed codeMessage



public void readMessage(){

String LSB ="";
char charLSB;
String messageBinary ="";

    for(int i=0;i<blue.length;i++){
        for(int j=0;j<blue[i].length;j++){
            LSB = Integer.toBinaryString(blue[i][j]);
            charLSB = LSB.charAt(LSB.length()-1);
            messageBinary+=charLSB;
        }
    }


    char ArrayOfChars [] = new char [blue[0].length*blue.length];
    int k =0;
    for(int i=0;i<messageBinary.length()-7;i+=7){
        String letter=(messageBinary.substring(i,i+7));
        int valueOfASCIIcharacter = Integer.parseInt(letter,2);
        char c = (char)(valueOfASCIIcharacter);
        System.out.println(c);
        ArrayOfChars[k]=c;
        k++;
    }

  }
}

Я также пытался использовать ARGB вместо RGB для BufferedImage, но безуспешно (только искажает цвета, изображение становится розовым).

Вот как я вызываю функцию в основном классе

import java.io.IOException;

public class Main {




public static void main(String[] args) throws IOException{

    Steganography img = new Steganography();

    int pixels [][] =img.readPixels("image.jpg");
    img.extractColors(pixels);


    img.codeMessage("Some message");


    img.reconstructImage();


    /*reading message from here on */


    int pixels2 [][] = img.readPixels("output.jpg");
    img.extractColors(pixels2);

    img.readMessage();


}
}

Исходное изображение имеет размер 83,3 КБ, а воссозданное изображение имеет размер всего 24,3 КБ.


person afwef1    schedule 04.12.2016    source источник


Ответы (1)


Я нашел решение.

Для тех, у кого такая же проблема, как у меня, и возможный поиск решения в будущем:

Этот алгоритм не может пережить расширение .jpg. Поменял картинку на bmp, немного дольше, но все работает как положено.

Если вы хотите использовать стеганографию в jpg, вам нужно использовать что-то еще, кроме LSB.

person afwef1    schedule 05.12.2016