Java interface to OpenCV, FFmpeg, and more
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 27 天前 | ||
| 27 天前 | ||
| 29 天前 | ||
| 1 个月前 | ||
| 7 年前 | ||
| 5 年前 | ||
| 29 天前 | ||
| 11 年前 | ||
| 29 天前 | ||
| 27 天前 |
JavaCV
简介
JavaCV 借助 JavaCPP Presets 提供的封装,整合了计算机视觉领域研究人员常用的各类库(如 OpenCV、FFmpeg、libdc1394、FlyCapture、Spinnaker、OpenKinect、librealsense、CL PS3 Eye Driver、videoInput、ARToolKitPlus、flandmark、Leptonica 和 Tesseract),并提供实用工具类,以便在 Java 平台(包括 Android)上更便捷地使用这些库的功能。
JavaCV 还提供硬件加速的全屏图像显示(CanvasFrame 和 GLCanvasFrame)、便于在多核处理器上并行执行代码的方法(Parallel)、用户友好的相机与投影仪几何及颜色校准工具(GeometricCalibrator、ProCamGeometricCalibrator、ProCamColorCalibrator)、特征点检测与匹配功能(ObjectFinder)、一套实现投影仪-相机系统直接图像对齐的类(主要有 GNImageAligner、ProjectiveTransformer、ProjectiveColorTransformer、ProCamTransformer 和 ReflectanceInitializer)、 blob 分析包(Blobs),以及 JavaCV 类中的其他杂项功能。其中部分类还提供 OpenCL 和 OpenGL 对应的实现,其类名以 CL 结尾或 GL 开头,例如:JavaCVCL、GLCanvasFrame 等。
由于目前缺乏完善的文档,若要了解 API 的使用方法,请参考下方的示例用法部分以及示例程序,其中包括两个 Android 示例(FacePreview.java 和 RecordActivity.java),这些示例程序也可在 samples 目录中找到。您可能还会发现,参考 ProCamCalib 和 ProCamTracker 的源代码,以及从《OpenCV2 Cookbook》移植的示例和相关的维基页面会有所帮助。
如果您对代码进行了任何更新或修复,请告知我,以便我将其整合到下一版本中。非常感谢!如果在使用软件时遇到任何问题,欢迎在邮件列表或讨论论坛上提问!我确信这个软件还有很多不足之处……
下载
包含 JAR 文件的归档可在 releases 中获取。二进制归档包含适用于 Android、iOS、Linux、Mac OS X 和 Windows 的构建版本。特定子模块或平台的 JAR 文件也可从 Maven Central Repository 单独获取。
要手动安装 JAR 文件,请按照下面 手动安装 部分中的说明进行操作。
我们也可以通过以下方式自动下载并安装所有内容:
- Maven(在
pom.xml文件内)
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.14</version>
</dependency>
- Gradle(位于
build.gradle.kts或build.gradle文件中)
dependencies {
implementation("org.bytedeco:javacv-platform:1.5.14")
}
- Leiningen(位于
project.clj文件内)
:dependencies [
[org.bytedeco/javacv-platform "1.5.14"]
]
- sbt(在
build.sbt文件内)
libraryDependencies += "org.bytedeco" % "javacv-platform" % "1.5.14"
此操作会下载适用于所有平台的二进制文件,但如果只想获取特定平台的二进制文件,我们可以通过 -D 命令行选项将 javacpp.platform 系统属性设置为 android-arm、linux-x86_64、macosx-x86_64、windows-x86_64 等类似值。有关详细信息,请参阅 JavaCPP Presets 的 README.md 文件。Gradle 用户还可选择使用 Gradle JavaCPP,Scala 用户则可使用 SBT-JavaCV。
所需软件
要使用 JavaCV,首先需要下载并安装以下软件:
- Java SE 8 或更高版本的实现:
- OpenJDK http://openjdk.java.net/install/ 或
- Oracle JDK http://www.oracle.com/technetwork/java/javase/downloads/ 或
- IBM JDK http://www.ibm.com/developerworks/java/jdk/ 或
- Microsoft JDK https://www.microsoft.com/openjdk 等
此外,尽管并非始终必需,但 JavaCV 的某些功能还依赖于:
- CL Eye Platform SDK(仅适用于 Windows)http://codelaboratories.com/downloads/
- Android SDK API 24 或更高版本 http://developer.android.com/sdk/
- JogAmp 提供的 JOCL 和 JOGL http://jogamp.org/
最后,请确保所有组件的位数相同:32 位和 64 位模块在任何情况下都不能混合使用。
手动安装
只需将所有所需的 JAR 文件(opencv*.jar、ffmpeg*.jar 等)以及 javacpp.jar 和 javacv.jar 放在类路径中的某个位置即可。以下是针对常见情况的更具体说明:
NetBeans(Java SE 8 或更高版本):
- 在 "项目" 窗口中,右键单击项目的 "库" 节点,然后选择 "添加 JAR/文件夹..."。
- 找到 JAR 文件,选中它们,然后单击 "确定"。
Eclipse(Java SE 8 或更高版本):
- 导航至 "项目" > "属性" > "Java 构建路径" > "库",然后单击 "添加外部 JAR..."。
- 找到 JAR 文件,选中它们,然后单击 "确定"。
Visual Studio Code(Java SE 8 或更高版本):
- 导航至 "Java 项目" > "引用的库",然后单击
+。 - 找到 JAR 文件,选中它们,然后单击 "确定"。
IntelliJ IDEA(Android 7.0 或更高版本):
- 按照此页面上的说明操作:http://developer.android.com/training/basics/firstapp/
- 将所有 JAR 文件复制到
app/libs子目录中。 - 导航至 "文件" > "项目结构" > "app" > "依赖项",单击
+,然后选择 "2 文件依赖项"。 - 从
libs子目录中选择所有 JAR 文件。 - 在 AndroidManifest.xml 中添加
android:extractNativeLibs="true"
之后,例如 OpenCV 和 FFmpeg 的包装类就可以自动访问它们所有的 C/C++ API:
示例用法
类定义基本上是将 C/C++ 中的原始头文件移植到 Java,我特意决定尽可能保留原始语法。例如,以下是一个尝试加载图像文件、对其进行平滑处理并将其保存回磁盘的方法:
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_imgproc.*;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
public class Smoother {
public static void smooth(String filename) {
Mat image = imread(filename);
if (image != null) {
GaussianBlur(image, image, new Size(3, 3), 0);
imwrite(filename, image);
}
}
}
JavaCV 还在 OpenCV 和 FFmpeg 的基础上提供了辅助类和方法,以促进它们与 Java 平台的集成。以下是一个小型演示程序,展示了最常用的部分:
import java.io.File;
import java.net.URL;
import org.bytedeco.javacv.*;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.indexer.*;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_imgproc.*;
import org.bytedeco.opencv.opencv_calib3d.*;
import org.bytedeco.opencv.opencv_objdetect.*;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
import static org.bytedeco.opencv.global.opencv_calib3d.*;
import static org.bytedeco.opencv.global.opencv_objdetect.*;
public class Demo {
public static void main(String[] args) throws Exception {
String classifierName = null;
if (args.length > 0) {
classifierName = args[0];
} else {
URL url = new URL("https://raw.github.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml");
File file = Loader.cacheResource(url);
classifierName = file.getAbsolutePath();
}
// We can "cast" Pointer objects by instantiating a new object of the desired class.
CascadeClassifier classifier = new CascadeClassifier(classifierName);
if (classifier == null) {
System.err.println("Error loading classifier file \"" + classifierName + "\".");
System.exit(1);
}
// The available FrameGrabber classes include OpenCVFrameGrabber (opencv_videoio),
// DC1394FrameGrabber, FlyCapture2FrameGrabber, OpenKinectFrameGrabber, OpenKinect2FrameGrabber,
// RealSenseFrameGrabber, RealSense2FrameGrabber, PS3EyeFrameGrabber, VideoInputFrameGrabber, and FFmpegFrameGrabber.
FrameGrabber grabber = FrameGrabber.createDefault(0);
grabber.start();
// CanvasFrame, FrameGrabber, and FrameRecorder use Frame objects to communicate image data.
// We need a FrameConverter to interface with other APIs (Android, Java 2D, JavaFX, Tesseract, OpenCV, etc).
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
// FAQ about IplImage and Mat objects from OpenCV:
// - For custom raw processing of data, createBuffer() returns an NIO direct
// buffer wrapped around the memory pointed by imageData, and under Android we can
// also use that Buffer with Bitmap.copyPixelsFromBuffer() and copyPixelsToBuffer().
// - To get a BufferedImage from an IplImage, or vice versa, we can chain calls to
// Java2DFrameConverter and OpenCVFrameConverter, one after the other.
// - Java2DFrameConverter also has static copy() methods that we can use to transfer
// data more directly between BufferedImage and IplImage or Mat via Frame objects.
Mat grabbedImage = converter.convert(grabber.grab());
int height = grabbedImage.rows();
int width = grabbedImage.cols();
// Objects allocated with `new`, clone(), or a create*() factory method are automatically released
// by the garbage collector, but may still be explicitly released by calling deallocate().
// You shall NOT call cvReleaseImage(), cvReleaseMemStorage(), etc. on objects allocated this way.
Mat grayImage = new Mat(height, width, CV_8UC1);
Mat rotatedImage = grabbedImage.clone();
// The OpenCVFrameRecorder class simply uses the VideoWriter of opencv_videoio,
// but FFmpegFrameRecorder also exists as a more versatile alternative.
FrameRecorder recorder = FrameRecorder.createDefault("output.avi", width, height);
recorder.start();
// CanvasFrame is a JFrame containing a Canvas component, which is hardware accelerated.
// It can also switch into full-screen mode when called with a screenNumber.
// We should also specify the relative monitor/camera response for proper gamma correction.
CanvasFrame frame = new CanvasFrame("Some Title", CanvasFrame.getDefaultGamma()/grabber.getGamma());
// Let's create some random 3D rotation...
Mat randomR = new Mat(3, 3, CV_64FC1),
randomAxis = new Mat(3, 1, CV_64FC1);
// We can easily and efficiently access the elements of matrices and images
// through an Indexer object with the set of get() and put() methods.
DoubleIndexer Ridx = randomR.createIndexer(),
axisIdx = randomAxis.createIndexer();
axisIdx.put(0, (Math.random() - 0.5) / 4,
(Math.random() - 0.5) / 4,
(Math.random() - 0.5) / 4);
Rodrigues(randomAxis, randomR);
double f = (width + height) / 2.0; Ridx.put(0, 2, Ridx.get(0, 2) * f);
Ridx.put(1, 2, Ridx.get(1, 2) * f);
Ridx.put(2, 0, Ridx.get(2, 0) / f); Ridx.put(2, 1, Ridx.get(2, 1) / f);
System.out.println(Ridx);
// We can allocate native arrays using constructors taking an integer as argument.
Point hatPoints = new Point(3);
while (frame.isVisible() && (grabbedImage = converter.convert(grabber.grab())) != null) {
// Let's try to detect some faces! but we need a grayscale image...
cvtColor(grabbedImage, grayImage, CV_BGR2GRAY);
RectVector faces = new RectVector();
classifier.detectMultiScale(grayImage, faces);
long total = faces.size();
for (long i = 0; i < total; i++) {
Rect r = faces.get(i);
int x = r.x(), y = r.y(), w = r.width(), h = r.height();
rectangle(grabbedImage, new Point(x, y), new Point(x + w, y + h), Scalar.RED, 1, CV_AA, 0);
// To access or pass as argument the elements of a native array, call position() before.
hatPoints.position(0).x(x - w / 10 ).y(y - h / 10);
hatPoints.position(1).x(x + w * 11 / 10).y(y - h / 10);
hatPoints.position(2).x(x + w / 2 ).y(y - h / 2 );
fillConvexPoly(grabbedImage, hatPoints.position(0), 3, Scalar.GREEN, CV_AA, 0);
}
// Let's find some contours! but first some thresholding...
threshold(grayImage, grayImage, 64, 255, CV_THRESH_BINARY);
// To check if an output argument is null we may call either isNull() or equals(null).
MatVector contours = new MatVector();
findContours(grayImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
long n = contours.size();
for (long i = 0; i < n; i++) {
Mat contour = contours.get(i);
Mat points = new Mat();
approxPolyDP(contour, points, arcLength(contour, true) * 0.02, true);
drawContours(grabbedImage, new MatVector(points), -1, Scalar.BLUE);
}
warpPerspective(grabbedImage, rotatedImage, randomR, rotatedImage.size());
Frame rotatedFrame = converter.convert(rotatedImage);
frame.showImage(rotatedFrame);
recorder.record(rotatedFrame);
}
frame.dispose();
recorder.stop();
grabber.stop();
}
}
此外,创建包含以下内容的 pom.xml 文件后:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.bytedeco.javacv</groupId>
<artifactId>demo</artifactId>
<version>1.5.14</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.14</version>
</dependency>
<!-- Additional dependencies required to use CUDA and cuDNN -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform-gpu</artifactId>
<version>4.14.0-1.5.14</version>
</dependency>
<!-- Optional GPL builds with (almost) everything enabled -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg-platform-gpl</artifactId>
<version>8.1.2-1.5.14</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>.</sourceDirectory>
</build>
</project>
将上述源代码放入 Demo.java 中,或者对 samples 目录下的其他类执行类似操作后,我们可以使用以下命令让 Maven 先自动安装所有依赖,然后执行程序:
$ mvn compile exec:java -Dexec.mainClass=Demo
注意:若出现错误,请确保 pom.xml 文件中的 artifactId 为 javacv-platform,而非仅 javacv。例如,javacv-platform 工件会添加所有必要的二进制依赖项。
构建说明
如果上述可用的二进制文件无法满足您的需求,您可能需要从源代码重新构建它们。为此,项目文件已针对以下工具创建:
- Maven 3.x http://maven.apache.org/download.html
- JavaCPP 1.5.14 https://github.com/bytedeco/javacpp
- JavaCPP Presets 1.5.14 https://github.com/bytedeco/javacpp-presets
安装完成后,只需对 JavaCPP、其 Presets 和 JavaCV 调用常规的 mvn install 命令即可。默认情况下,除了 JavaCPP 所需的 C++ 编译器外,不需要其他依赖项。有关更多详细信息,请参考 pom.xml 文件中的注释。
无需手动构建原生库,我们只需为 JavaCV 运行 mvn install,并依赖 CI 构建的快照工件:
项目负责人:Samuel Audet samuel.audet at gmail.com
开发者网站:https://github.com/bytedeco/javacv
讨论组:http://groups.google.com/group/javacv