📄 bandsanalyzer.java
字号:
// BandsAnalyzer.java
// Andrew Davison, October 2005, ad@fivedots.coe.psu.ac.th
/* A thread is started which uses a JMFCapture object to take
a picture every SNAP_INTERVAL ms. Each image is analyzed by
analyzeImage() to generate translation/rotation information.
The webcam picture should include a coloured
strap around the user's wrist. The strap should contain
several rectangular colour bands,
whose colours are defined as constants in this class
(in BAND1, BAND2, etc.).
The image is processed as an ARGB pixel array. Each pixel is
compared to the predefined band colours, and if the pixel
colour is near to one of them then the pixel's coordinate is
added to a 'blob' for that band colour.
Each band colour has a BandManager which maintains a series
of 'blobs' -- a blob is a collection of localised pixel coordinates
which all have the same colour. A pixel is added to a blob
based on its (x,y) coordinate in the picture. If there isn't a
nearby existing blob, then a new one is started.
When a blob grows large enough, the image processing stops.
The large blob info (its colour and center point)
is converted by applyBigBlob() into rotation and translation
information.
Each analyzed image is drawn into a JPanel, by calling
BandsPanel's showImage() method from run().
*/
import java.awt.*;
import java.awt.image.*;
public class BandsAnalyzer extends Thread
{
private static final int SNAP_INTERVAL = 120; // ms; the time between snaps
/* The value for SNAP_INTERVAL is based on timings that show that a
snap takes about 30ms to be carried out, and the subsequent
analysis of the image requires about 60 ms, for a total
processing time of about 90 ms.
*/
private static final int NUM_BANDS = 3; // must match no. of bands colours
/* Band colours -- modify to suit your wrist strap, the camera's
properties, and environmental issues such as lighting and the
background. */
private static final Color BAND1 = new Color(205, 16, 80); // red (1st vers)
private static final Color BAND2 = new Color(200, 200, 85); // yellow
private static final Color BAND3 = new Color(50, 150, 200); // blue
// private static final Color BAND4 = new Color(175, 60, 65); // red (2nd vers)
private static final int WHITE = 0xffffff; // pixel white
// min. distance for blob center to move to trigger a translation
private static final int MIN_MOVE_DIST = 10;
private JMFCapture camera; // JMF webcam object
private int imSize; // size of captured image
private BandsPanel bandsPanel;
// for displaying the snapped (and modified) images
private BandManager bandMans[]; // band managers for the bands
// band manager and blob indicies identifying the large blob
private int bandIdx, blobIdx;
private Point prevCenterPt = null; // center of blob in previous image
private boolean running, movingFwd;
public BandsAnalyzer(BandsPanel bp, int sz)
{
bandsPanel = bp;
imSize = sz;
bandMans = new BandManager[NUM_BANDS];
// initialize the band managers; there should be NUM_BANDS of them
bandMans[0] = new BandManager("red1", BAND1);
bandMans[1] = new BandManager("yellow", BAND2);
bandMans[2] = new BandManager("blue", BAND3);
// bandMans[3] = new BandManager("red2", BAND4);
reset();
movingFwd = false;
} // end of BandsAnalyzer()
private void reset()
// reset the band managers
{ for(int i=0; i < NUM_BANDS; i++)
bandMans[i].clear();
bandIdx = -1; blobIdx = -1;
} // end of reset()
public void run()
/* Use a JMFCapture object to take a picture every SNAP_INTERVAL ms,
analyze it, and then generate translation or rotation commands.
*/
{
camera = new JMFCapture(imSize); // initialize the webcam
System.out.println("**Camera Ready**");
long duration;
BufferedImage im = null;
running = true;
while (running) {
long startTime = System.currentTimeMillis();
// System.out.println("Start loading image");
im = camera.grabImage(); // take a snap
analyzeImage(im); // analyze the image
applyBigBlob(); // convert blob info to translation/rotation cmds
duration = System.currentTimeMillis() - startTime;
if (im == null)
System.out.println("Problem loading image");
else {
// System.out.println("Duration: " + duration + " ms");
bandsPanel.showImage(im, duration); // show the image in the JPanel
}
if (duration < SNAP_INTERVAL) {
try {
Thread.sleep(SNAP_INTERVAL-duration); // wait until interval has passed
}
catch (Exception ex) {}
}
}
camera.close(); // close down the camera
} // end of run()
// ----------------- image analysis ----------------------
private void analyzeImage(BufferedImage im)
/*
The image is processed as a pixel array. Each pixel is
compared to the predefined band colours, and if the pixel
colour is near to one of them then the pixel's coordinate is
added to a 'blob' for that band colour.
When a blob grows large enough (madeBigBlob == true), the image
processing stops.
*/
{
if (im == null) {
System.out.println("Input image is null");
return;
}
reset(); // reset the bands analyzer
// extract pixels from the image into an array
int imWidth = im.getWidth();
int imHeight = im.getHeight();
int [] pixels = new int[imWidth * imHeight];
im.getRGB(0, 0, imWidth, imHeight, pixels, 0, imWidth);
int xc = 0; // hold the current pixel coordinates
int yc = 0;
int bandId; // id of band manager
boolean madeBigBlob = false;
// examine the pixels
int i = 0;
while((i < pixels.length) && (!madeBigBlob)) {
bandId = isBandColour(pixels[i]);
if (bandId != -1) // pixel colour is a band colour
madeBigBlob = addPointToBand(bandId, xc, yc);
else // pixel colour isn't a band colour
pixels[i] = WHITE; // clear the pixel to white
// move to next coordinate in image
xc++;
if (xc == imWidth) { // at end of row
xc = 0; // start next row
yc++;
}
i++;
}
// update the image with the new pixels data
im.setRGB(0, 0, imWidth, imHeight, pixels, 0, imWidth);
} // end of analyzeImage()
private int isBandColour(int pixel)
/* is the (r,g,b) pixel colour close to a band colour?
If it is then return the index of the band manager
which is handling the band. */
{
// extract RGB components from the current pixel as integers
int redVal = (pixel>>16)&255;
int greenVal = (pixel>>8)&255;
int blueVal = pixel&255;
for(int i=0; i < NUM_BANDS; i++)
if (bandMans[i].isBandColour(redVal, greenVal, blueVal))
return i;
return -1;
} // end of isBandColour()
private boolean addPointToBand(int bandId, int x, int y)
/* Add (x,y) to the band manager with bandId, and
report if a large blob has been made because of it. */
{
boolean madeBigBlob = false;
if ((bandId < 0) || (bandId >= NUM_BANDS))
System.out.println("Band ID out of range: " + bandId);
else {
int blobId = bandMans[bandId].addPoint(x,y);
if (blobId != -1) { // made a large-enough blob
madeBigBlob = true;
bandIdx = bandId; // store indicies for large blob
blobIdx = blobId;
}
}
return madeBigBlob;
} // end of addPointToBand()
// ----------------- use analysis info ----------------------
private void applyBigBlob()
/* Use the largest blob info to generate translation/rotation cmds.
The blob's colour is used to define a possible rotation.
The current blob's center point (pt) is compared to the
previous image's blob center (prevCenterPt) to define a
possible translation.
*/
{
if (bandIdx != -1) { // there is a large blob
BandManager bm = bandMans[bandIdx];
Point pt = bm.getBlobCenter(blobIdx); // blob's center pt
// System.out.println( bm.getName() + " (" + pt.x + "," + pt.y + ")" );
if (prevCenterPt != null) { // there is a previous center point
rotateView( bm.getName() );
translateView(pt, prevCenterPt);
}
prevCenterPt = pt; // save for next analysis
}
else
System.out.println("No large blob");
} // end of applyBigBlob()
private void rotateView(String bmName)
// blob colour --> rotation
{
if (bmName.equals("red1") || bmName.equals("red2"))
System.out.println("turn left");
else if (bmName.equals("blue"))
System.out.println("turn right");
} // end of rotateView()
private void translateView(Point currPt, Point prevPt)
/* The change in the x values of the current center point
(currPt) and the previous center point (prevPt) generates
a translation forward.
I don't use the y-coordinate change in this example.
I'm assuming that a translation of the center to the left
in the image means a movement 'forward'. However, some cameras
flip the webcam image so a translation 'forward' is a move to
the right in the image. This can be dealt with by switching the
true/false assignments in the if-test.
*/
{
int xChg = currPt.x - prevPt.x;
if (xChg < -MIN_MOVE_DIST)
movingFwd = true;
else if (xChg > (MIN_MOVE_DIST*2)) // stopping requires more of a change
movingFwd = false;
if (movingFwd)
System.out.println(" forwards");
} // end of translateView()
// --------------------- finish -----------------------
public void closeDown()
/* closeDown() blocks until run() has called JMFCapture.close()
and finished. This avoids nasty OS effects after the application
has terminated, but it means that clicking on the close box
'freezes' the application for 1-2 secs.
*/
{
running = false;
while (!camera.isClosed()) {
try {
Thread.sleep(SNAP_INTERVAL);
}
catch (Exception ex) {}
}
} // end of closeDown()
} // end of BandsAnalyzer class
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -