Image Compression by Kohonen Map PDF Print E-mail
Programming - java

I wrote a JAVA code for Kohonen Map. The code is compatible with both the Windows system and Unix system. I primarily ran the experiment on Suse Linux 10.0, with Java 1.5 SDK. The following command will convert Java code to bytecode

Javac Kohonen.java

The following command will execute the bytecode to produce the results

Java Kohonen

224-1 colors were mapped to 16 colors, making the image effectively a 4 bit image instead of a 24 bit image. Each Pixel of the image is stored as one of the 16 colors closest to the corresponding color pixel in the original image. The Kohonen Map and the compressed 4 bit compressed image were used to regenerate the original image, which was close to the original input image.

The input image should be in a PPM file format, which is a text file format for images. The JAVA code reads the input file, maps each pixel of the input file into one of 16 colors of Khononen Map to compress the image into 4 bit image. The Kohonen Map and the compressed image are used to decode the compressed image to “near” input image as shown in the figure below.

The input image should be in a PPM file format, which is a text file format for images. The JAVA code reads the input file, maps each pixel of the input file into one of 16 colors of Khononen Map to compress the image into 4 bit image. The Kohonen Map and the compressed image are used to decode the compressed image to “near” input image as shown in the figure below.

In a 24 bit color image, we can have (224-1) colors, but in most of the images, a slight variation of color, which is not even noticable to human eye, is also represented as an individual color. When there is an issue with the size of the image, we can effectively represent the image, in a lot less number of colors, without much degrading the quality of image.

I used this image for my experiments. The results were quite impressive even for this simple implementation of the algorithm

Source Code
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import java.lang.Math;

public class KohonenMap {
public static int SIZE_OF_NEIGHBORHOOD = 5;
public static int NO_OF_EPOCHS = 50;
public static int NO_OF_CLUSTERS = 16;
public static int X_DIM = 450;
public static int Y_DIM = 600;
public static int RGB = 3;
public static int[][] INPUT_FILE = new int[X_DIM * Y_DIM][RGB];
public static double[][] K_MAP = new double[NO_OF_CLUSTERS][RGB];
public static int lowerLimit = 100000;
public static int upperLimit = 0;
public static double learning_rate = 0.7;
public static int[] CLUSTER = new int[NO_OF_CLUSTERS];
public static boolean SQUARED = true;
public static String INPUT_FILENAME = "/home/nischal/Desktop/family_guy_mad.pgm";
public static String KOHONENMAP_FILENAME = "/home/nischal/Desktop/kohonenMap.knm";
public static String COMPRESSED_IMAGE_FILENAME = "/home/nischal/Desktop/family_guy_Compressed.pbm";
public static String EXPANDED_IMAGE_FILENAME = "/home/nischal/Desktop/family_guy_Expanded.txt";

/**
* @param args
*/
public static void main(String[] args) {
ReadFile(INPUT_FILENAME);
InitializeCluster();
Train();
StoreTheKohonenMap();
CompressTheImage();
}

@SuppressWarnings("deprecation")
public static void ReadFile(String s) {
File file = new File(s);
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;

try {
fis = new FileInputStream(file);

// Here BufferedInputStream is added for fast reading.
bis = new BufferedInputStream(fis);
dis = new DataInputStream(bis);
dis.readLine();
dis.readLine();
dis.readLine();
dis.readLine();
// dis.available() returns 0 if the file does not have more lines.
int k = 0;
while (dis.available() != 0) {
String line = dis.readLine();
line = line.trim();

String[] splittedLine = line.split("[ \\t]+|[ \\t]+$");

for (int i = 0; i < splittedLine.length; i += 3) {
for (int j = 0; j < RGB; j++) {
if (!splittedLine[i].matches("\\s*")
|| !splittedLine[i].contains("")
|| !splittedLine[i].isEmpty())
INPUT_FILE[k][j] = Integer.valueOf(splittedLine[i
+ j]);
if (INPUT_FILE[k][j] < lowerLimit)
lowerLimit = INPUT_FILE[k][j];
if (INPUT_FILE[k][j] > upperLimit)
upperLimit = INPUT_FILE[k][j];
}
k++;
}
}

// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Exitting Read File");
}

public static void InitializeCluster() {
Random rm = new Random();
for (int i = 0; i < NO_OF_CLUSTERS; i++)
for (int j = 0; j < RGB; j++) {
K_MAP[i][j] = lowerLimit + Math.abs(rm.nextInt())
% (upperLimit - lowerLimit);
}
}

public static void Train() {
for (int epochs = 0; epochs < NO_OF_EPOCHS; epochs++) {
System.out.println(epochs);
for (int i = 0; i < X_DIM * Y_DIM; i++) {
int min = 999999999;
int WinningIndex = 0;
for (int k = 0; k < NO_OF_CLUSTERS; k++) {
int temp = 0;
// Calcuate the euclidian distance
for (int j = 0; j < RGB; j++) {
temp += (INPUT_FILE[i][j] - K_MAP[k][j])
* (INPUT_FILE[i][j] - K_MAP[k][j]);
}
// Find the winning index
if (temp < min) {
min = temp;
WinningIndex = k;
}
}

// Adjust the weights of the winning pixel in the cluster
for (int j = 0; j < RGB; j++) {
double temp = (INPUT_FILE[i][j] - K_MAP[WinningIndex][j]);
K_MAP[WinningIndex][j] += learning_rate * temp;

// Adjust the weight of the neighbors
if (SIZE_OF_NEIGHBORHOOD > 0) {
for (int index = 1; index < SIZE_OF_NEIGHBORHOOD; index++) {
if (WinningIndex - index >= 0) {
K_MAP[(WinningIndex - index)][j] += learning_rate
* temp;
if (K_MAP[(WinningIndex - index)][j] < 0)
K_MAP[(WinningIndex - index)][j] = 0;
}
if (WinningIndex + index < NO_OF_CLUSTERS) {
K_MAP[(WinningIndex + index)][j] += learning_rate
* temp;
if (K_MAP[(WinningIndex + index)][j] < 0)
K_MAP[(WinningIndex + index)][j] = 0;
}
}

}
}
}

if(epochs % 5 == 0)
if (SQUARED)
learning_rate = Math.pow(learning_rate, 2.0);
else
learning_rate = learning_rate / 2.0;
if(epochs == 1)
SIZE_OF_NEIGHBORHOOD = 2 ;
if(epochs == 4)
SIZE_OF_NEIGHBORHOOD = 1;
if(epochs == 5)
SIZE_OF_NEIGHBORHOOD = 0;
}
}

public static void StoreTheKohonenMap() {
try {
FileWriter fstream = new FileWriter(KOHONENMAP_FILENAME);
BufferedWriter out = new BufferedWriter(fstream);

for(int i = 0; i < NO_OF_CLUSTERS;i++){
for(int j = 0; j < RGB;j++) {
out.write(String.valueOf((int)K_MAP[i][j]));
out.write("\t");
}
out.write("\n");
}
out.close();
} catch(FileNotFoundException e) { e.printStackTrace();}
catch(IOException e) { e.printStackTrace();}



}

public static void CompressTheImage() {
try {
FileWriter fstream = new FileWriter(COMPRESSED_IMAGE_FILENAME);
BufferedWriter out = new BufferedWriter(fstream);

FileWriter fstream_CompressedImage = new FileWriter(EXPANDED_IMAGE_FILENAME);
BufferedWriter out_CompressedImage = new BufferedWriter(fstream_CompressedImage);


out.write("P3 \n # CREATOR: XV version 3.10a-jumboFix+Enh of 20070520\n  450 600\n 255\n");

for (int i = 0; i < X_DIM * Y_DIM; i++) {
int min = 999999999;
int WinningIndex = 0;
for (int k = 0; k < NO_OF_CLUSTERS; k++) {
int temp = 0;
// Calcuate the euclidian distance
for (int j = 0; j < RGB; j++) {
temp += (INPUT_FILE[i][j] - K_MAP[k][j])
* (INPUT_FILE[i][j] - K_MAP[k][j]);
}
// Find the winning index
if (temp < min) {
min = temp;
WinningIndex = k;
}
out_CompressedImage.write(String.valueOf(WinningIndex));
if(i%X_DIM == 0)
out_CompressedImage.write('\n');
else
out_CompressedImage.write('\t');
}
try {
// Write the file of new compressed image
for (int j = 0; j < RGB; j++) {
out.write(String.valueOf((int)K_MAP[WinningIndex][j]));
out.write("\t");
}
out.write("\n");
} catch (IOException e) {
e.printStackTrace();
}
}
out.close();
out_CompressedImage.close();

}
catch(FileNotFoundException e) { e.printStackTrace();}
catch(IOException e ) { e.printStackTrace();}

}    
}
Last Updated on Monday, 17 May 2010 21:40