Difference between revisions of "Example Tracker Mean Shift"
From BoofCV
Jump to navigationJump to searchm |
m |
||
(2 intermediate revisions by the same user not shown) | |||
Line 8: | Line 8: | ||
Example Code: | Example Code: | ||
* [https://github.com/lessthanoptimal/BoofCV/blob/v0. | * [https://github.com/lessthanoptimal/BoofCV/blob/v0.38/examples/src/main/java/boofcv/examples/tracking/ExampleTrackerMeanShiftLikelihood.java ExampleTrackerMeanShiftLikelihood.java] | ||
Concepts: | Concepts: | ||
Line 20: | Line 20: | ||
/** | /** | ||
* Example of how to use the low level implementation of mean-shift to track a specific color provided by the user. | * Example of how to use the low level implementation of mean-shift to track a specific color provided by the user. | ||
* The weights of each pixel is computed by RgbLikelihood, see below, and track regions are rectangular. | * The weights of each pixel is computed by RgbLikelihood, see below, and track regions are rectangular. This | ||
* tracker works very well since the ball is almost uniformly blue. | * tracker works very well since the ball is almost uniformly blue. If the light varried then a HSV color | ||
* model might be better. | * model might be better. | ||
* | * | ||
Line 27: | Line 27: | ||
*/ | */ | ||
public class ExampleTrackerMeanShiftLikelihood { | public class ExampleTrackerMeanShiftLikelihood { | ||
/** | /** | ||
* Very simple implementation of PixelLikelihood. | * Very simple implementation of PixelLikelihood. Uses linear distance to compute how close | ||
* a color is to the target color. | * a color is to the target color. | ||
*/ | */ | ||
public static class RgbLikelihood implements PixelLikelihood<Planar<GrayU8>> { | public static class RgbLikelihood implements PixelLikelihood<Planar<GrayU8>> { | ||
int targetRed, targetGreen, targetBlue; | |||
int targetRed,targetGreen,targetBlue; | |||
float radius = 35; | float radius = 35; | ||
Planar<GrayU8> image; | Planar<GrayU8> image; | ||
public RgbLikelihood(int targetRed, int targetGreen, int targetBlue) { | public RgbLikelihood( int targetRed, int targetGreen, int targetBlue ) { | ||
this.targetRed = targetRed; | this.targetRed = targetRed; | ||
this.targetGreen = targetGreen; | this.targetGreen = targetGreen; | ||
Line 44: | Line 42: | ||
} | } | ||
@Override | @Override public void setImage( Planar<GrayU8> image ) { this.image = image; } | ||
@Override | @Override public boolean isInBounds( int x, int y ) { return image.isInBounds(x, y); } | ||
/** | /** | ||
* This function is used to learn the target's model from the select image region. | * This function is used to learn the target's model from the select image region. Since the | ||
* model is provided in the constructor it isn't needed or used. | * model is provided in the constructor it isn't needed or used. | ||
*/ | */ | ||
@Override | @Override | ||
public void createModel(RectangleLength2D_I32 target) { | public void createModel( RectangleLength2D_I32 target ) { throw new RuntimeException("Not supported"); } | ||
@Override | @Override | ||
public float compute(int x, int y) { | public float compute( int x, int y ) { | ||
int pixelR = image.getBand(0).get(x,y); | int pixelR = image.getBand(0).get(x, y); | ||
int pixelG = image.getBand(1).get(x,y); | int pixelG = image.getBand(1).get(x, y); | ||
int pixelB = image.getBand(2).get(x,y); | int pixelB = image.getBand(2).get(x, y); | ||
// distance along each color band | // distance along each color band | ||
float red = Math.max(0, 1.0f - Math.abs(targetRed - pixelR) / radius); | float red = Math.max(0, 1.0f - Math.abs(targetRed - pixelR)/radius); | ||
float green = Math.max(0,1.0f - Math.abs(targetGreen-pixelG)/radius); | float green = Math.max(0, 1.0f - Math.abs(targetGreen - pixelG)/radius); | ||
float blue = Math.max(0,1.0f - Math.abs(targetBlue-pixelB)/radius); | float blue = Math.max(0, 1.0f - Math.abs(targetBlue - pixelB)/radius); | ||
// multiply them all together | // multiply them all together | ||
Line 79: | Line 69: | ||
} | } | ||
public static void main(String[] args) { | public static void main( String[] args ) { | ||
MediaManager media = DefaultMediaManager.INSTANCE; | MediaManager media = DefaultMediaManager.INSTANCE; | ||
String fileName = UtilIO.pathExample("tracking/balls_blue_red.mjpeg"); | String fileName = UtilIO.pathExample("tracking/balls_blue_red.mjpeg"); | ||
RectangleLength2D_I32 location = new RectangleLength2D_I32(394,247,475-394,325-247); | RectangleLength2D_I32 location = new RectangleLength2D_I32(394, 247, 475 - 394, 325 - 247); | ||
ImageType<Planar<GrayU8>> imageType = ImageType.pl(3,GrayU8.class); | ImageType<Planar<GrayU8>> imageType = ImageType.pl(3, GrayU8.class); | ||
SimpleImageSequence<Planar<GrayU8>> video = media.openVideo(fileName, imageType); | SimpleImageSequence<Planar<GrayU8>> video = media.openVideo(fileName, imageType); | ||
// Return a higher likelihood for pixels close to this RGB color | // Return a higher likelihood for pixels close to this RGB color | ||
var likelihood = new RgbLikelihood(64, 71, 69); | |||
TrackerMeanShiftLikelihood<Planar<GrayU8>> tracker = | TrackerMeanShiftLikelihood<Planar<GrayU8>> tracker = | ||
Line 97: | Line 87: | ||
Planar<GrayU8> frame = video.next(); | Planar<GrayU8> frame = video.next(); | ||
// Note that the tracker will not automatically invoke RgbLikelihood.createModel() in its initialize function | // Note that the tracker will not automatically invoke RgbLikelihood.createModel() in its initialize function | ||
tracker.initialize(frame,location); | tracker.initialize(frame, location); | ||
// For displaying the results | // For displaying the results | ||
var gui = new TrackerObjectQuadPanel(null); | |||
gui.setPreferredSize(new Dimension(frame.getWidth(),frame.getHeight())); | gui.setPreferredSize(new Dimension(frame.getWidth(), frame.getHeight())); | ||
gui. | gui.setImageUI(video.getGuiImage()); | ||
gui.setTarget(location,true); | gui.setTarget(location, true); | ||
ShowImages.showWindow(gui, "Tracking Results", true); | ShowImages.showWindow(gui, "Tracking Results", true); | ||
// Track the object across each video frame and display the results | // Track the object across each video frame and display the results | ||
while( video.hasNext() ) { | while (video.hasNext()) { | ||
frame = video.next(); | frame = video.next(); | ||
boolean visible = tracker.process(frame); | boolean visible = tracker.process(frame); | ||
gui. | gui.setImageUI(video.getGuiImage()); | ||
gui.setTarget(tracker.getLocation(),visible); | gui.setTarget(tracker.getLocation(), visible); | ||
gui.repaint(); | gui.repaint(); | ||
Latest revision as of 14:01, 12 July 2021
BoofCV provides two mean-shift trackers, histogram and pixel likelihood, this example is for the second. It is demonstrated how to create a custom pixel likelihood fuction to track a color ball. Mean-shift performs a local search that attempts to maximize the similarity of the color in its kernel to the color in its model. It is a fairly robust and fast tracker. If you are lucky, it will sometimes even reaquire a track after it has been lost.
Example Code:
Concepts:
- Object tracking
- Color histogram
- Mean-shift
Example Code
/**
* Example of how to use the low level implementation of mean-shift to track a specific color provided by the user.
* The weights of each pixel is computed by RgbLikelihood, see below, and track regions are rectangular. This
* tracker works very well since the ball is almost uniformly blue. If the light varried then a HSV color
* model might be better.
*
* @author Peter Abeles
*/
public class ExampleTrackerMeanShiftLikelihood {
/**
* Very simple implementation of PixelLikelihood. Uses linear distance to compute how close
* a color is to the target color.
*/
public static class RgbLikelihood implements PixelLikelihood<Planar<GrayU8>> {
int targetRed, targetGreen, targetBlue;
float radius = 35;
Planar<GrayU8> image;
public RgbLikelihood( int targetRed, int targetGreen, int targetBlue ) {
this.targetRed = targetRed;
this.targetGreen = targetGreen;
this.targetBlue = targetBlue;
}
@Override public void setImage( Planar<GrayU8> image ) { this.image = image; }
@Override public boolean isInBounds( int x, int y ) { return image.isInBounds(x, y); }
/**
* This function is used to learn the target's model from the select image region. Since the
* model is provided in the constructor it isn't needed or used.
*/
@Override
public void createModel( RectangleLength2D_I32 target ) { throw new RuntimeException("Not supported"); }
@Override
public float compute( int x, int y ) {
int pixelR = image.getBand(0).get(x, y);
int pixelG = image.getBand(1).get(x, y);
int pixelB = image.getBand(2).get(x, y);
// distance along each color band
float red = Math.max(0, 1.0f - Math.abs(targetRed - pixelR)/radius);
float green = Math.max(0, 1.0f - Math.abs(targetGreen - pixelG)/radius);
float blue = Math.max(0, 1.0f - Math.abs(targetBlue - pixelB)/radius);
// multiply them all together
return red*green*blue;
}
}
public static void main( String[] args ) {
MediaManager media = DefaultMediaManager.INSTANCE;
String fileName = UtilIO.pathExample("tracking/balls_blue_red.mjpeg");
RectangleLength2D_I32 location = new RectangleLength2D_I32(394, 247, 475 - 394, 325 - 247);
ImageType<Planar<GrayU8>> imageType = ImageType.pl(3, GrayU8.class);
SimpleImageSequence<Planar<GrayU8>> video = media.openVideo(fileName, imageType);
// Return a higher likelihood for pixels close to this RGB color
var likelihood = new RgbLikelihood(64, 71, 69);
TrackerMeanShiftLikelihood<Planar<GrayU8>> tracker =
new TrackerMeanShiftLikelihood<>(likelihood, 50, 0.1f);
// specify the target's initial location and initialize with the first frame
Planar<GrayU8> frame = video.next();
// Note that the tracker will not automatically invoke RgbLikelihood.createModel() in its initialize function
tracker.initialize(frame, location);
// For displaying the results
var gui = new TrackerObjectQuadPanel(null);
gui.setPreferredSize(new Dimension(frame.getWidth(), frame.getHeight()));
gui.setImageUI(video.getGuiImage());
gui.setTarget(location, true);
ShowImages.showWindow(gui, "Tracking Results", true);
// Track the object across each video frame and display the results
while (video.hasNext()) {
frame = video.next();
boolean visible = tracker.process(frame);
gui.setImageUI(video.getGuiImage());
gui.setTarget(tracker.getLocation(), visible);
gui.repaint();
BoofMiscOps.pause(20);
}
}
}