2011-02-26

Serializing Records and Incanter Matrix in Clojure with print-dup

For most Clojure data structures, you can get a serialization by doing something like this:


(def x (some-data-structure 1 2 3))
(def x2 (binding [*print-dup* true]
          (prn-str x)))
;; now x2 is a string serialization of x
;; You can save x2 to disk or whatever
;; Then to read it back in:
(def x-clone (read-string x2))

(Note: you really shouldn't be programming in Clojure with a bunch of def'ed vars like that...)

Unfortunately, Clojure records does not support this functionality quite just yet. There is defrecord2 that implements this serialization functionality for records though, described in this discussion in the Clojure group.

I had a second problem though, since I had records storing Incanter matrices, which are Parallel Colt matrices, and they don't print-dup in a way that can be read back in with read (or read-string). So to solve both problems at the same time, we can just implement our own print-dup for the record we create with defrecord (print-dup is a multmethod).

2011-02-16

How to change JPEG compression in Clojure

Not just how to change JPEG compression, but to do so without losing the JPEG's metadata. That's the problem I had to figure out for this program I'm writing in Clojure. I found out and wrote about how to do this in Java yesterday. Finally, here's the same thing in Clojure:

Short explanation of what the code is doing is interwoven with the code here. The code without the comments (for easy copy/paste'ing) is at bottom.

First, we import a few Java classes:

(require 'clojure.java.io)
(import [javax.imageio IIOImage ImageIO]
        [javax.imageio.plugins.jpeg JPEGImageWriteParam])
(try

Then we get the default JPEG image reader and writer:

(let [image-reader (.next (ImageIO/getImageReadersByFormatName "jpg"))
      image-writer (.next (ImageIO/getImageWritersByFormatName "jpg"))]

Now we open up streams to the input and output JPEG Files. Note that with-open will close the streams for you, so you don't have to later.

(with-open [image-input-stream (ImageIO/createImageInputStream 
                                 (clojure.java.io/file "path/to/inputFile.jpg"))
            image-output-stream (ImageIO/createImageOutputStream 
                                 (clojure.java.io/file "path/to/outputFile.jpg"))]

Next, mate the reader/writer to the respective streams:

(.setInput image-reader image-input-stream)
(.setOutput image-writer image-output-stream)

Then we'll get the JPEG input file into a container that will also contain the metadata:

(let [iio-image (IIOImage. (.read image-reader 0) nil
                             (.getImageMetadata image-reader 0))

Now set up the JPEG quality (ie, compression) level desired. Here it's set to 0.7 (where 1 is highest quality, and 0 is highest compression):

jpeg-params (doto (.getDefaultWriteParam image-writer)
               (.setCompressionMode JPEGImageWriteParam/MODE_EXPLICIT)
               (.setCompressionQuality 0.7))]

Finally we get to write out the JPEG file with the new compression level:

(.write image-writer nil iio-image jpeg-params)))

Lastly, make sure to clean up the reader/writer's so they don't continue to hog system resources (yes, you have to do this even though Java has garbage collection):

(finally
 (do (.dispose image-writer)
     (.dispose image-reader)))))

To recap, here it is again without the comments, just the bare code:

2011-02-15

How to change JPEG compression in Java

Unless you use specialized libraries like JMagick, JAI, or access libjpeg through JNI, doing something simple like changing the JPEG compression in Java turned out to be excruciatingly tedious (or just hard to figure out the first time at least).  Here's how to do it:

(I'll explain how to do the following in Clojure as well.)

And the upshot is that this way, it seems you get to preserve the metadata from the original JPEG file — at least it did for me!

You will need these imports:

import java.io.File;
import javax.imageio.*;
import javax.imageio.stream.*;

First, we're going to need the default JPEG ImageReader and ImageWriter:

ImageReader imgRdr = ImageIO.getImageReadersByFormatName("jpg").next();
ImageWriter imgWrtr = ImageIO.getImageWritersByFormatName("jpg").next();

Next we'll need an ImageInputStream, wrapping around the JPEG File you want to convert the compression level of, and an ImageOutputStream, wrapping around the File location you want to save the re-compressed JPEG to:

ImageInputStream imgInStrm = ImageIO.createImageInputStream(inputJpegFile);
ImageOutputStream imgOutStrm = ImageIO.createImageInputStream(outputFile);

I assume you know how to create a File object to supply the parameters inputJpegFile and outputFile.

Now we mate the streams to the reader/writer:

2011-02-14

Higher Education is like Travelling

When asked, I tell people that I don't like travelling. All the moving about from one place to another just to see the tourists' view of a city just doesn't appeal to me. Many have thought it strange that I wouldn't like to explore or discover a new place, or "to see the world," given that I have the financial means and am in the age group that stereotypically like that sort of thing.

But I do like to explore and discover new places, and to learn and see new things. I just go about it differently: namely through continuing education, independent learning and reading, and higher education [1].

I've asked previously what good is higher education, given all it's costs and sometimes failures at helping students become highly employable.  But for some of us, higher education is really about exploring and discovering for the sake of itself.

Learning for the sake of learning — it sounds cliché, but most people wouldn't fault travelling for costing so much and for producing travellers who do not become highly employable.  So why fault higher education for those things?