get webcam frames from quicktime, process and display in clojure

Jonathan Raphaelson
2010-07-08T04:57:38Z

main.clj

| ;; open a skeleton swing window, and provide an image to draw into
|
| (import
| '(video VideoCapture)
| '(java.awt Color Graphics Dimension)
| '(java.awt.image BufferedImage ConvolveOp Kernel)
| '(javax.swing JPanel JFrame))
|
| (def fps 30)
| (def fps-ms (/ 1000 fps))
|
| (declare frame panel)
| (def video (ref (new VideoCapture 400 400)))
| (def mod-image (ref nil))
| (def raw-image (ref nil))
|
| (def modification-agent (agent nil))
| (defn modify [x]
| "Does whatever image processing is required"
| (let [kernel (float-array [0.0 -1.0 0.0 -1.0 4.0 -1.0 0.0 -1.0 -0.0])
| edgeop (new ConvolveOp (new Kernel 3 3 kernel))]
| (dosync
| (ref-set mod-image (. edgeop (filter @raw-image nil))))))
|
| (def capture-agent (agent nil))
| (defn capture [x]
| "Captures an image from the video source, and stores in it the image ref"
| (dosync
| (ref-set raw-image (. @video getNextImage))
| (. Thread (sleep fps-ms))
| (send-off modification-agent #'modify)
| (send-off *agent* #'capture)))
|
| (def drawing-agent (agent nil))
| (defn draw [x]
| "Forces the screen to refresh"
| (dosync
| (. panel repaint)
| (. Thread (sleep fps-ms))
| (send-off *agent* #'draw)))
|
| ;;;; UI
|
| (defn render [gfx]
| (. gfx (drawImage @mod-image 0 0 nil)))
|
|
| (def panel (doto (proxy [JPanel] []
| (paint [gfx] (render gfx)))
| (.setPreferredSize (new Dimension 400 400))))
|
| (def frame (doto (new JFrame "Image Capture") (.add panel) (.pack) (.show)))
|
| (defn start []
| (send-off capture-agent #'capture)
| (send-off drawing-agent #'draw))

VideoCapture.java

| package video;
|
| import java.awt.Image;
| import java.awt.image.BufferedImage;
| import java.awt.image.DataBuffer;
| import java.awt.image.WritableRaster;
| import quicktime.QTRuntimeException;
| import quicktime.QTRuntimeHandler;
| import quicktime.QTSession;
| import quicktime.qd.PixMap;
| import quicktime.qd.QDConstants;
| import quicktime.qd.QDGraphics;
| import quicktime.qd.QDRect;
| import quicktime.std.StdQTConstants;
| import quicktime.std.sg.SequenceGrabber;
| import quicktime.std.sg.SGVideoChannel;
| import quicktime.util.RawEncodedImage;
|
| public class VideoCapture {
| private SequenceGrabber grabber;
| private SGVideoChannel channel;
| private RawEncodedImage rowEncodedImage;
|
| private int width;
| private int height;
| private int videoWidth;
|
| private int[] pixels;
| private BufferedImage image;
| private WritableRaster raster;
|
| public VideoCapture(int width, int height) throws Exception {
| this.width = width;
| this.height = height;
| try {
| QTSession.open();
| QDRect bounds = new QDRect(width, height);
| QDGraphics graphics = new QDGraphics(bounds);
| grabber = new SequenceGrabber();
| grabber.setGWorld(graphics, null);
| channel = new SGVideoChannel(grabber);
| channel.setBounds(bounds);
| channel.setUsage(StdQTConstants.seqGrabPreview);
| grabber.prepare(true, false);
| grabber.startPreview();
| PixMap pixMap = graphics.getPixMap();
| rowEncodedImage = pixMap.getPixelData();
|
| videoWidth = width + (rowEncodedImage.getRowBytes() - width * 4) / 4;
| pixels = new int[videoWidth * height];
| image = new BufferedImage(
| videoWidth, height, BufferedImage.TYPE_INT_RGB);
| raster = WritableRaster.createPackedRaster(DataBuffer.TYPE_INT,
| videoWidth, height,
| new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff }, null);
| raster.setDataElements(0, 0, videoWidth, height, pixels);
| image.setData(raster);
| QTRuntimeException.registerHandler(new QTRuntimeHandler() {
| public void exceptionOccurred(
| QTRuntimeException e, Object eGenerator,
| String methodNameIfKnown, boolean unrecoverableFlag) {
| System.out.println("what should i do?");
| }
| });
| } catch (Exception e) {
| QTSession.close();
| throw e;
| }
| }
|
| public void dispose() {
| try {
| grabber.stop();
| grabber.release();
| grabber.disposeChannel(channel);
| image.flush();
| } catch (Exception e) {
| e.printStackTrace();
| } finally {
| QTSession.close();
| }
| }
|
| public int getWidth() {
| return width;
| }
|
| public int getHeight() {
| return height;
| }
|
| public int getVideoWidth() {
| return videoWidth;
| }
|
| public int getVideoHeight() {
| return height;
| }
|
| public void getNextPixels(int[] pixels) throws Exception {
| grabber.idle();
| rowEncodedImage.copyToArray(0, pixels, 0, pixels.length);
| }
|
| public Image getNextImage() throws Exception {
| grabber.idle();
| rowEncodedImage.copyToArray(0, pixels, 0, pixels.length);
| raster.setDataElements(0, 0, videoWidth, height, pixels);
| image.setData(raster);
| return image;
| }
| }