Markov Chain Text Gen

alice-in-wonderland[1].jpg

Markov chains are a list ngrams (grouped portions of text) from a work of text with probabilities of what will come next grouped to each ngram. You can then use this to generate new text based on those probabilities.

Here’s some made with Alice in Wonderland:

cupboards as long ago anything; she had began dream. There isn't,' said to Alice laughed so far

'Ugh!' said the puppy began, in a low, trembling voice. 'I've offended tone, and raised him declare it's

ven't think I can't help that,' said the Hatter. He had caught there ought Alice thought she went on,


HashMap> createTextMarkov(String txt, int order) {
    HashMap> ngrams = new HashMap>();
    for (int i = 0; i < txt.length() - order; i++) {
      String gram = txt.substring(i, i + order);

      if (!ngrams.containsKey(gram)) {
        ngrams.put(gram, new ArrayList());
      }
      ngrams.get(gram).add(txt.charAt(i + order));
    }
    return ngrams;
  }
  
  
  String genText(HashMap> markovMap) {
    Object[] keyArray = markovMap.keySet().toArray();
    String starting = (String) keyArray[(int) random(0, keyArray.length)];
    int order = starting.length();
    String result = starting;
    String currentGram = starting;

    for (int i = 0; i < 100; i++) {
      if (!markovMap.containsKey(currentGram)) {
        break;
      }
      ArrayList possibilities = markovMap.get(currentGram);

      String next = "" + possibilities.get((int) random(0, possibilities.size()));
      result += next;
      currentGram = result.substring(result.length() - order, result.length());
    }
    return result;
  }


A Markov chain can also be made with words instead of ngrams.

dogs The Pool of uglifying it flashed across her draw the baby at last concert on in a crimson velvet cushion resting their slates when he went Alice its voice Fetch me for some executions I make it unfolded the Pigeon but it was too much of course the other the Mouse who will you want to the March Hare I didnt interrupted in Alice who had put his scaly friend replied counting off her hand round THAT direction waving its full of mine said to set the Duchess asked triumphantly pointing to your waist the slate with Seaography then another moment he knows it is wrong and marked poison or something better



HashMap> createTextMarkovByWord(String txt) { 
  HashMap> ngrams = new HashMap>();
  
  txt = txt.replace('\n',' ').replace("-", "").replace("\"", "").replace("'", "").replace(".", "").replace("?", "").replace("!", "").replace(",", "").replace("\t", "").replace("\r", "").replace(":", "").replace(";", "");
  
  String[] words = txt.split(" ");
  for (int i = 0; i < words.length - 1; i++) {
      String gram = words[i];

      if (!ngrams.containsKey(gram)) {
        ngrams.put(gram, new ArrayList());
      }
      ngrams.get(gram).add(words[i+1]);
    }
  
  
  return ngrams;
}

String genWordsText(HashMap> markovMap) {
    Object[] keyArray = markovMap.keySet().toArray();
    String starting = (String) keyArray[(int) random(0, keyArray.length)];
    String result = starting;
    String currentGram = starting;

    for (int i = 0; i < 1000; i++) {
      if (!markovMap.containsKey(currentGram)) {
        break;
      }
      ArrayList possibilities = markovMap.get(currentGram);

      String next = possibilities.get((int) random(0, possibilities.size()));
      result += " " + next;
      currentGram = next;
    }
    return result;
  }


Quick and easy Spout Intergration into Alchemy

I got an email from someone asking if Spout could be integrated into Alchemy, Alchemy is an open source drawing program and Spout allows programs to share opengl textures. Alchemy is written in java and doesn't use opengl for its graphics. I decided to integrate it as quick and easy as possible. I did this by importing Processing and the Spout library for Processing into the Alchemy source code and then, whenever Alchemy redraws the drawing canvas, passing a copy of the canvas to Processing and then to Spout.

If you would like a copy of the program just contact me.

Alchemy: http://al.chemy.org/
Spout: http://spout.zeal.co/
Processing: https://processing.org/

A Dark and Stormy Night, Programming with Poems

The above image was made using ADASN (A Dark and Stormy Night), a simple stack based programming language. I designed the code of the programming language to be written as normal English.  Every word of a sentence that has over 3 characters represents a number, 1 if it starts with a, 2 if it starts with b and etc. All the words in the sentence are then added up and if the resulting number corresponds to a command then the command is executed otherwise it is pushed onto the stack. Numbers 55 through to 86 represent commands. When the code is executed it generates a video where the code runs separately for each pixel every frame of the video.

The code tends to work best when written as a poem as it allows for more flexible sentence structure. The trick is to write code that not only describes how to make an image but what that image means.

Currently I have written a transpiler using JavaCC that converts the code into java and glsl, using processing to interface with opengl. This creates java source code that can then be compiled into a runnable jar file.

Commands

  • 55     time - pushes the current running time to the stack
  • 56    dup - duplicates the top of the stack
  • 57    push - pushes the number number to the stack even if its a command
  • 58    decimal - pops then pushes 1 / that numbers  
  • 59    <= - pops the top 2 from the stack then compares then and pushes 1 or 0
  • 60    >= - pops the top 2 from the stack then compares then and pushes 1 or 0
  • 61    < - pops the top 2 from the stack then compares then and pushes 1 or 0
  • 62    > - pops the top 2 from the stack then compares then and pushes 1 or 0
  • 63    = - pops the top 2 from the stack then compares then and pushes 1 or 0
  • 64    != - pops the top 2 from the stack then compares then and pushes 1 or 0
  • 65    + - pops the top 2 from the stack then pushes one plus the other
  • 66    - - pops the top 2 from the stack then pushes one minus the other
  • 67    * - pops the top 2 from the stack then pushes one times the other
  • 68    / - pops the top 2 from the stack then pushes one divided by the other
  • 69    % - pops the top 2 from the stack then pushes one modulus the other
  • 70    x - pushes the x position of the current pixel
  • 71    y - pushes the y position of the current pixel
  • 72    r - pushed the red color value of the current pixel
  • 73    g - pushed the green color value of the current pixel
  • 74    b - pushed the blue color value of the current pixel
  • 75    sR - pops then sets the current red color value to that number
  • 76    sG - pops then sets the current green color value to that number
  • 77    sB - pops then sets the current blue color value to that number
  • 78    sin - pops then pushes the sin of that number
  • 79    cos - pops then pushes the cos of that number
  • 80    tan - pops then pushes the tan of that number
  • 81    rand - pushes a random number
  • 82    width - pushes the width of the video
  • 83    height - pushes the height of the video
  • 84     pop - pops the stack
  • 85    length - pops the top 2 on the stack and pushes the length of them
  • 86    atan - pops the top 2 on the stack and pushes the atan of them

Example

Bolbus.

Terrifically gigantic,
floating a buv, 
up in the heavens,
floating with love.

Rising at morn,
falling at night,
quivering a buv.

Rising at morn,
falling at night,
soaring a buv.

Watching from far above,
shining down sun,
moving the fun.

Beautiful.

Terrifically gigantic,
floating above, 
up in the heavens,
floating with love.

Rising at morn,
falling at night,
quivering a buv.

Rising at morn,
falling at night,
soaring above.

Watching from far above,
shining down sun,
moving the fun.

Terrifically gigantic,
floating around all up a bove, 
up in the heavens,
floating with love.

Around, all bashly above.

Terrifically gigantic,
floating a buv, 
up in the heavens,
floating with love.

Rising at morn,
falling at night,
quivering a buv.

Watching from far above,
shining sun,
moving the fun.

Rising at morn,
falling at night,
xhuming the sun.

(As you can see i did cheat a bit by changing the spelling of some words, xhuming instead of exhuming and a buv/a bove instead of above)

Parsing

This is what it gets parsed down to:

  1. 2 82 68 70 66 2 83 68 71 66 85 4 82 68 62 75
  2. 2 width / x - 2 height / y - length  4 width / > setR
  3. R = length y - (height /2), x - (width /2)) >  width / 4

 

 

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");