BoofCV是一个开源的Java库,专注于计算机视觉领域。它提供从底层图像处理到高级3D几何视觉的一系列功能。BoofCV以其易用性和高性能著称,适用于学术和商业用途。它的速度在与其他流行的计算机视觉库的比较研究中得到了证明。
BoofCV提供了丰富的功能,包括二值图像处理、小波去噪、图像配准和模型拟合、兴趣点检测、相机标定、立体视觉、超像素、密集光流、目标跟踪、视觉里程计、颜色直方图图像检索、场景分类、背景建模/运动检测等。
BoofCV的官方网站提供了众多示例和教程。用户可以在不安装的情况下,通过Java Applets在网页浏览器中查看其特性。此外,BoofCV还有一个YouTube频道,解释不同的概念和示例。
以下是一个展示如何在两幅图像之间关联点特征的示例。图像关联是创建图像马赛克、图像稳定、视觉里程计、3D结构估计等许多应用中的关键组成部分。
BoofCV提供了多种图像配准的方法,大多数基于兴趣点。在这个上下文中,兴趣点是图像中的一个特征,可以在相同场景的不同视角的多幅图像中轻松且重复地识别。
以下代码块定义了一个类,并传入了几个类。这些类是抽象接口,允许不同的算法相互替换。未来可以轻松添加新的算法。示例中未显示的非抽象代码在需要高性能时也易于使用。
public class ExampleAssociatePoints<T extends ImageSingleBand, TD extends TupleDesc> {
// 用于检测和描述兴趣点的算法
DetectDescribePoint<T, TD> detDesc;
// 通过最小化误差度量将描述关联在一起
AssociateDescription associate;
// 兴趣点的位置
public List<Point2D_F64> pointsA;
public List<Point2D_F64> pointsB;
Class<T> imageType;
public ExampleAssociatePoints(DetectDescribePoint<T, TD> detDesc, AssociateDescription associate, Class<T> imageType) {
this.detDesc = detDesc;
this.associate = associate;
this.imageType = imageType;
}
}
以下是代码的核心部分。这里传入了两幅图像,它们被转换为BoofCV可以处理的图像类型,检测兴趣点,提取描述符,关联特征,并显示结果。所有这些都只需要几行代码。注意,T是一个泛型类型,参见示例代码。
public void associate(BufferedImage imageA, BufferedImage imageB) {
T inputA = ConvertBufferedImage.convertFromSingle(imageA, null, imageType);
T inputB = ConvertBufferedImage.convertFromSingle(imageB, null, imageType);
// 存储检测到的兴趣点的位置
pointsA = new ArrayList<Point2D_F64>();
pointsB = new ArrayList<Point2D_F64>();
// 存储检测到的兴趣点的描述
FastQueue<Desc> descA = UtilFeature.createQueue(detDesc, 100);
FastQueue<Desc> descB = UtilFeature.createQueue(detDesc, 100);
// 使用兴趣点描述每幅图像
describeImage(inputA, pointsA, descA);
describeImage(inputB, pointsB, descB);
// 在两幅图像之间关联特征
associate.setSource(descA);
associate.setDestination(descB);
associate.associate();
// 显示结果
AssociationPanel panel = new AssociationPanel(20);
panel.setAssociation(pointsA, pointsB, associate.getMatches());
panel.setImages(imageA, imageB);
ShowImages.showWindow(panel, "Associated Features");
}
两幅图像都使用一组特征描述符进行描述。对于每个检测到的兴趣点,都会提取一个特征描述符。
private void describeImage(T input, List<Point2D_F64> points, FastQueue<Desc> descs) {
detDesc.detect(input);
for (int i = 0; i < detDesc.getNumberOfFeatures(); i++) {
points.add(detDesc.getLocation(i).copy());
descs.grow().setTo(detDesc.getDescription(i));
}
}
public static void main(String args[]) {
Class imageType = ImageFloat32.class;
// 选择要使用的算法
DetectDescribePoint detDesc = FactoryDetectDescribe.surfStable(
new ConfigFastHessian(1, 2, 200, 1, 9, 4, 4), null, null, imageType);
ScoreAssociation scorer = FactoryAssociation.defaultScore(detDesc.getDescriptionType());
AssociateDescription associate = FactoryAssociation.greedy(scorer, Double.MAX_VALUE, true);
// 加载和匹配图像
ExampleAssociatePoints app = new ExampleAssociatePoints(detDesc, associate, imageType);
BufferedImage imageA = UtilImageIO.loadImage("../data/evaluation/stitch/kayak_01.jpg");
BufferedImage imageB = UtilImageIO.loadImage("../data/evaluation/stitch/kayak_03.jpg");
app.associate(imageA, imageB);
}