Image to WAV and back

After writing my last blog about databending in java I realized that Audacity has a batch editing tool called Chains. This allows you to batch apply effects to many audio files at once.

So I wrote a helper class in java for manipulating the pixel data in images. And also allowing you to write the pixel data to a WAV file and load it back. Using this you can then batch convert all the frames of a video to WAV files, apply a chain to them in audacity and then batch convert them all back to images. This is far better than just manipulating the pixel data in java as it means you don't have to write your own code for the audio effects.

Audacity Chains Info: here

Code

package com.augustuspash.databend;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import javax.imageio.ImageIO;


public class Databend {

	static byte[] headerWAV = new byte[] { 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 
			0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x44, (byte) 0xAC,
			0x00, 0x00, (byte) 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61 };
	
	public static void batchImageToWave(int start, int stop, String imagePath, String outputPath) throws IOException {
		for (int i = start; i < stop; i++) {
			imageToWave(String.format(imagePath, i), String.format(outputPath, i));
		}
	}
	
	public static void batchWaveToImage(int start, int stop, String imagePath, String wavePath, String outputPath, String type) throws IOException {
		for (int i = start; i < stop; i++) {
			waveToImage(String.format(imagePath, i), String.format(wavePath, i), String.format(outputPath, i), type);
		}
	}
	
	public static void imageToWave(String imagePath, String outputPath) throws IOException {
		BufferedImage img = loadImage(imagePath);
		byte[] origData = getPixelBytes(img);
		writeBytesToWave(outputPath, origData);
	}
	
	public static void waveToImage(String origImagePath, String wavePath, String outputPath, String type) throws IOException {
		BufferedImage img = loadImage(origImagePath);
		byte[] origData = getPixelBytes(img);
		byte[] reloadData = getWavBytes(wavePath);
		BufferedImage img2 = pixelBytesToImage(img.getWidth(), img.getHeight(), img.getType(), img.getSampleModel(), origData.length, reloadData);
		saveImage(img2, outputPath, type);
	}
	
	public static void writeBytesToWave(String templatePath, byte[] data) throws IOException {
		Path file = Paths.get(templatePath);
		Files.write(file, headerWAV);
		Files.write(file, new byte[]{(byte)((data.length>>8)&0xFF),(byte)(data.length&0xFF)}, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
		Files.write(file, data, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
	}
	
	public static byte[] getPixelBytes(String path) throws IOException {
		BufferedImage img = null;
		img = ImageIO.read(new File(path));
		return ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
	}
	
	public static byte[] getPixelBytes(BufferedImage image){
		return ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
	}
	
	public static BufferedImage loadImage(String path) throws IOException {
		return ImageIO.read(new File(path));
	}
	
	public static BufferedImage pixelBytesToImage(int width, int height, int type, SampleModel sampleModel, int origLength, byte[] data) {
		BufferedImage resultImg = new BufferedImage(width, height, type);
		if (origLength < data.length) {
			byte[] result = new byte[origLength];
			System.arraycopy(data, 0, result, 0, origLength);
			data = result;
		} else if (origLength > data.length) {
			byte[] result = new byte[origLength];
			System.arraycopy(data, 0, result, 0, data.length);
			data = result;
		}
		resultImg.setData(Raster.createRaster(sampleModel, new DataBufferByte(data, data.length), new Point() ) );
		return resultImg;
	}
	
	public static void saveImage(BufferedImage image, String path, String type) throws IOException {
		ImageIO.write(image, type, new File(path));
	}
	
	public static byte[] getWavBytes(String path) throws IOException {
		Path file = Paths.get(path);
		byte[] fileRAW = Files.readAllBytes(file);
		byte[] out = new byte[fileRAW.length - 45];
		for (int i = 45; i < fileRAW.length; i++) {
			out[i - 45] = fileRAW[i];
		}
		return out;
	}
	
	public static short bytesToShort(byte[] bytes, int index, ByteOrder byteOrder) {
		ByteBuffer bb = ByteBuffer.allocate(2);
		bb.order(byteOrder);
		bb.put(bytes[index]);
		bb.put(bytes[index+1]);
		return bb.getShort(0);
	}
	
	public static byte[] shortToBytes(short s) {
		return new byte[]{(byte)((s>>8)&0xFF),(byte)(s&0xFF)};
	}
	
	public static short[] bytesToShorts(byte[] bytes, ByteOrder byteOrder) {
		ByteBuffer bb = ByteBuffer.allocate(2);
		bb.order(byteOrder);
		short[] output = new short[bytes.length/2];
		for (int i = 0; i < bytes.length - 1; i+=2) {
			bb.put(bytes[i]);
			bb.put(bytes[i+1]);
			output[i/2] = bb.getShort(0);
			bb.clear();
		}
		return output;
	}
	
	public static byte[] shortsToBytes(short[] shorts) {
		byte[] output = new byte[shorts.length * 2];
		for (int i = 0; i < shorts.length; i++) {
			byte[] tmp = new byte[]{(byte)((shorts[i]>>8)&0xFF),(byte)(shorts[i]&0xFF)};
			output[i*2] = tmp[0];
			output[i*2 + 1] = tmp[1];
		}
		return output;
	}
}

Example

//convert all frames to wav files (this will load frames with the file names output00001.bmp, output00002.bmp and etc)
batchImageToWave(0, 100, "Z:\\Videos\\video-Frames\\output%05d.bmp", "Z:\\Videos\\video-Frames\\wavs\\%05d.wav");
//this will convert the new wav files to images
batchWaveToImage(0, 100, "Z:\\Videos\\video-Frames\\output%05d.bmp", "Z:\\Videos\\video-Frames\\wavs\\cleaned\\%05d.wav", "Z:\\Videos\\video-Frames\\new\\output%05d.bmp", "bmp");