Sometimes, though, by making assumptions about the format of the input data, you can achieve much higher compression ratios. However, you have to be willing to give up the requirement that the after compressing and decompressing, the resulting file is identical to the original. That's why this type of compression is called lossy - because you lose data. In the case of images, we would like to compress in such a way that the decompressed image looks very similar to the original, but not exactly the same.
You will be implementing a lossy compression algorithm using a technique
adopted from linear algebra. If you have done a course in linear algebra,
you may remember several matrix decomposition methods that were discussed
in class (if you went to class). You may remember decompositions such as
LU, QR and SVD decompositions. SVD stands for the singular value decomposition,
a method that has many practical applciations. One such application is
applying SVD to compress an image by extracting and storing "enough" important
information about the matrix in the compressed file. So the cool thing
about this algorithm is you really don't have to depend on the fixed level
of compression provided by a method such as jpeg or gif compression, but
choose how much coded information you need to store in order to recover
an "approximate" image. This method is particulary useful for image transmission
over distances where the receiver can request information as needed to
reconstruct an "acceptable" image.. If you can live with a fuzzy image
you can certainly choose to receive a much smaller array of values than
the original. There are several variations of the SVD decomposition as
well. We can apply the SVD method to the entire image or to various blocks
sizes of the image. Of course like any compression algorithm, there are
enhancements and drawbacks to SVD compression which we can discuss later.
Singular Value Decomposition(SVD)The SingularValueDecomposition class from JAMA looks like this:
The SVD class can be used to find the Singular Value Decomposition of
any size array. Therefore you can apply SVD to the entire image or chunks
of it, say 8x8 arrays. We will compress the whole image first and then
apply the SVD to chunks of the image as extra credit.
|
The following figures show the image after applying SVD to the entire
image (128x128) using rank-1, rank-8, and rank-16 approximations.
|
size 49K |
Size 1K |
rank 8
Size 7K |
rank 16
size 13K |
Theoritically sizes of these rank-k files should be even less. But we
will use this as a benchmark to make sure you will stay close to the sizes
given above. A challenging thing here is to find a good bit packing
algorithm (you must write your own) to efficiently store, U, V and S. But
you dont have to find the most efficient way to pack the bytes. Just try
to stay close to above sizes.
Adaptive Ranking Samples
The following figures show the result of applying compression to Professor
Danny Sleator (image size 128x128) using an adaptive ranking scheme on
sub-blocks of size 8x8 using 80%, 50%, 10% percentage of values. Note that
the compressed file didn't reduce in size much after 50%. We encourage
you to think about why this is the case.
|
size 49K |
size 26K |
50%
size 15K |
10%
size 14K |
The BMP file formatdisplay desert.bmpIf you're on Windows, you can open BMP files using Windows Paint, and on a Macintosh you can use a shareware progam called GraphicConverter. You'll need to find a way to view BMP files to do this assignment, because otherwise you'll have no way of knowing if your program is working or not. All of the BMP files we are giving you are exactly 128x128 pixels. Each file is exactly 49,206 bytes in size, because there are 54 bytes of header, and each pixel takes up 3 bytes (and 54 + 128*128*3 = 49206). Don't worry about the BMP header - we want you to ignore it. So when you read a BMP file, you should just read the first 54 bytes into an array, and when you write it out again, you should just write the header bytes out unchanged. The pixels are stored in RGB format, which means that every pixel contains three bytes, one each for the red, green, and blue color component of each pixel. One byte can store numbers in the range 0-255, and in this model 0 is black and 255 is full color. So the color red is (255, 0, 0) and green is (0, 255, 0). (128, 0, 0) makes a dark red, and (255, 0, 255) makes purple since purple is red + blue. The pixels are stored from left to right and from bottom to top. So the first 128 groups of three bytes in the file (after the 54-byte header) correspond to the bottom row of the image, and the last 128 groups of thee bytes correspond to the top row. The compression algorithm we're using only works with one color component at a time, so when you extract the 128x128 chunks (or 8x8 chunck's in extra credit) of the image to pass to the SVD, you'll need to do this separately for the red, green, and blue components of the image. BTW, every other file format I can think of stores images from top to bottom. But of course, BMP was invented by Microsoft, and they like to do everything upside-down. |
In order to write this program, you'll need to work with two-dimensional arrays. Luckily, Java makes this very easy. Here's an example of how to create an 128x128 array of bytes and initialize all of the values to zero:
byte grid[][] = new byte[128][128]; int i, j; for(i=0; i<128; i++) for(j=0; j<128; j++) grid[i][j] = 0;OK, now you have all of the tools you need. Here's what you actually write:
public interface Compressor {
public void compress (BitReader reader, BitWriter writer) throws IOException;
public void decompress (BitReader reader, BitWriter writer) throws IOException;
}
Here are the steps you'll need to compress a file: