Created
July 8, 2010 04:57
-
-
Save lygaret/467654 to your computer and use it in GitHub Desktop.
get webcam frames from quicktime, process and display in clojure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment