javagifbufferedimagesteganographylsb

Issue with converting an ArrayList of BufferedImages to a GIF using GifSequenceWriter - Java


I'm trying to hide a message inside a .gif for a steganography project. I've converted the input gif to an ArrayList of BufferedImages ana applied my steganography algorithm. But, i came across an issue with converting the ArrayList of BufferedImages back to a .gif. I used this GifSequenceWriter class to convert the BufferedImages array to a new .gif after getting the original delay between frames from the original gif image metadata.

  File encoded_img = new File("output.gif");
  ImageOutputStream output = new FileImageOutputStream(encoded_img);
  GifSequenceWriter writer =  new GifSequenceWriter(output, frames.get(0).getType(), delayTimeMS, true);
  writer.writeToSequence(frames.get(0));
  for(int k=1; k<frames.size()-1; k++) {
    writer.writeToSequence(frames.get(k));
  }
  writer.close();
  output.close();

But, the resulting .gif looks really bad, and i've saved the individual frames with and without the steganography algorithm and they look fine. You can check out an example of the original image, the 10 saved frames and the resulting .gif here.

Is there a better way to create .gifs in java? Thanks in advance.


Solution

  • There's a problem with the the GifSequenceWriter when using palette images (BufferedImage.TYPE_BYTE_INDEXED with IndexColorModel). This will create metadata based on a default 216 color palette (the web safe palette), which is clearly different from the colors in your image.

    The problematic lines in GifSequenceWriter:

    ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType);
    imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
    

    Instead, the metadata should be based on the color palette in the index color model of your image. But the good news is, it works fine without it.

    You can simply use:

    GifSequenceWriter writer = new GifSequenceWriter(output, BufferedImage.TYPE_INT_ARGB, delayTimeMS, true);
    

    ...and the writer will automatically create the palette as needed, from your actual image data.

    The animated GIF created with the code above


    It's also possible to fix the GifSequenceWriter, to accept an ImageTypeSpecifier instead of the int imageType, however, this will only work if all frames use the same palette, I think:

    public GifSequenceWriter(
            ImageOutputStream outputStream,
            ImageTypeSpecifier imageTypeSpecifier, 
            int timeBetweenFramesMS,
            boolean loopContinuously) throws IIOException, IOException {
        // my method to create a writer
        gifWriter = getWriter();
        imageWriteParam = gifWriter.getDefaultWriteParam();
    
        imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
    
        // ... rest of the method unchanged. 
    

    Usage:

    ColorModel cm = firstImage.getColorModel();
    ImageTypeSpecifier imageType = new ImageTypeSpecifier(cm, cm.createCompatibleSampleModel(1, 1));
    GifSequenceWriter writer = new GifSequenceWriter(output, imageType, delayTimeMS, true);