Android Studio 导入 opencv
问题
尝试了很多不同的版本,通过implementation来添加opencv,最终都不能正常使用opencv

这种方法导入的opencv没有对应的java代码,古不能


即使能导入,也使用不了,就没有对应的java代码,很奇怪,网上搜索查看相关的导入基本上都是下载包到本地,本地导入opencv包的,故得出结论:android 不能通过implementation添加opencv
Android Studio 导入 opencv
https://opencv.org/releases/
推荐官网下载:我这里下载的是4.7.0的版本

下载完成,解压得到一下目录:

其中sdk文件夹是我们要导入的目标。
在进行导入前,有些准备工作。
你需要配置NDK环境和CMake,将它们勾选下载下来。
下载过程很简单,勾选要配置的环境,然后同意协议,然后下一步下一步,ok就行。

接下来导入opencv

然后你会穿越到这个界面:

选择你解压后的sdk文件夹:


第一次导入报错:
A problem occurred evaluating project ':opencv'.
> Plugin with id 'kotlin-android' not found.

解决方法:进入opencv下的build.gradle文件中,将开头的一行apply plugin: 'kotlin-android’注释掉即可。

最后直接sync now。
检查
检查一下,setting.grandle

查看是否自动包含了opencv

然后检查opencv下的build.gradle

检查一下minSdkVersion和targetSdkVersion是否与你项目的相同。记得添加
namespace "org.opencv"
最后一步,在app下的build.gradle,翻到最下边,在dependencies中添加依赖。

implementation(project(":opencv"))
最后,验证一下OpenCV是否正常加载。
@Override
protected void onResume() {
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d("openCv", "OpenCv加载失败...");
} else {
Log.d("openCv", "OpenCv加载成功...");
}
}
貌似除了导入opencv文件夹外可以从.gradle文件导入opencv且能正常调用(注:实际调用的是javacv)
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.2.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.jelly:mango:1.1.1'
implementation 'org.greenrobot:eventbus:3.1.1'
// implementation 'com.android.support:design:28.0.0-rc02'
implementation 'com.lzy.net:okgo:3.0.4'
implementation 'com.lzy.net:okrx2:2.0.2'
implementation 'com.lzy.net:okserver:2.0.5'
implementation 'org.codehaus.jackson:jackson-mapper-asl:1.9.12'
def room_version = "2.2.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// https://mvnrepository.com/artifact/org.apache.commons/commons-math3
implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
// 日志记录功能
implementation 'com.jakewharton.timber:timber:4.7.1'
// 不要使用 javacv-platform,而是分别指定Android版本
implementation 'org.bytedeco:javacv:1.5.9'
implementation 'org.bytedeco:opencv:4.7.0-1.5.9:android-arm64'
implementation 'org.bytedeco:ffmpeg:5.1.2-1.5.9:android-arm64'
implementation 'org.bytedeco:opencv:4.7.0-1.5.9:android-arm'
implementation 'org.bytedeco:ffmpeg:5.1.2-1.5.9:android-arm'
implementation 'org.bytedeco:openblas:0.3.21-1.5.9:android-arm64'
//JavaCV
// def javacvVersion = '1.4.2'
// def ffmpegVersion = '4.0.1'
// def opencvVersion = '3.4.2'
// implementation(group: 'org.bytedeco', name: 'javacv-platform', version: javacvVersion) {
// exclude group: 'org.bytedeco.javacpp-presets'
// }
// implementation group: 'org.bytedeco.javacpp-presets', name: 'ffmpeg', version: "${ffmpegVersion}-${javacvVersion}"
//// implementation group: 'org.bytedeco.javacpp-presets', name: 'ffmpeg', version: "${ffmpegVersion}-${javacvVersion}", classifier: 'android-arm' // for 'armeabi-v7a'
// implementation group: 'org.bytedeco.javacpp-presets', name: 'ffmpeg', version: "${ffmpegVersion}-${javacvVersion}", classifier: 'android-arm64' // for 'arm64-v8a'
//// implementation group: 'org.bytedeco.javacpp-presets', name: 'opencv', version: "${opencvVersion}-${javacvVersion}"
// implementation group: 'org.bytedeco.javacpp-presets', name: 'opencv', version: "${opencvVersion}-${javacvVersion}", classifier: 'android-arm' // for 'armeabi-v7a'
// implementation group: 'org.bytedeco.javacpp-presets', name: 'opencv', version: "${opencvVersion}-${javacvVersion}", classifier: 'android-arm64' // for 'arm64-v8a'
// implementation(project(":opencv"))
}
// 不要使用 javacv-platform,而是分别指定Android版本
implementation 'org.bytedeco:javacv:1.5.9'
implementation 'org.bytedeco:opencv:4.7.0-1.5.9:android-arm64'
implementation 'org.bytedeco:ffmpeg:5.1.2-1.5.9:android-arm64'
implementation 'org.bytedeco:opencv:4.7.0-1.5.9:android-arm'
implementation 'org.bytedeco:ffmpeg:5.1.2-1.5.9:android-arm'
implementation 'org.bytedeco:openblas:0.3.21-1.5.9:android-arm64'

ContourExtractor.java
不过使用的不是原始的opencv了,而是javacv
package com.rtcce.laser.pad.yolo;
import org.bytedeco.javacpp.indexer.IntRawIndexer;
import org.bytedeco.javacv.AndroidFrameConverter;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.MatVector;
import org.bytedeco.opencv.opencv_core.Point;
import org.bytedeco.opencv.opencv_core.Scalar;
import org.bytedeco.opencv.opencv_core.Size;
import android.graphics.Bitmap;
import android.graphics.PointF;
import java.util.ArrayList;
import java.util.List;
public class ContourExtractor {
/**
* 从旋转矩形区域提取轮廓点(JavaCV 版本)。
* 返回的 Mat 为 (N x 1 x 2) 的 CV_32SC2,表示采样后的点集。
*/
public static Mat extractContourFromRotatedRect(Mat image,
double x, double y,
double w, double h,
double angle,
int samplePoints) {
try {
// 1) 计算旋转矩形 4 顶点(先以原点为中心,后旋转平移)
double angleRad = Math.toRadians(angle);
double cosA = Math.cos(angleRad);
double sinA = Math.sin(angleRad);
double halfW = w / 2.0;
double halfH = h / 2.0;
double[][] base = new double[][] {
{-halfW, -halfH}, {halfW, -halfH}, {halfW, halfH}, {-halfW, halfH}
};
Point[] translated = new Point[4];
for (int i = 0; i < 4; i++) {
double px = base[i][0];
double py = base[i][1];
double rx = px * cosA - py * sinA;
double ry = px * sinA + py * cosA;
translated[i] = new Point((int) Math.round(rx + x), (int) Math.round(ry + y));
}
// 将 4 顶点放入一个 CV_32SC2 的 Mat(4x1x2)
Mat rectPts = new Mat(4, 1, opencv_core.CV_32SC2);
IntRawIndexer rectIdx = rectPts.createIndexer();
for (int i = 0; i < 4; i++) {
rectIdx.put(i, 0, 0, (int) Math.round(translated[i].x()));
rectIdx.put(i, 0, 1, (int) Math.round(translated[i].y()));
}
rectIdx.release();
// 2) 生成掩码并二值化
Mat mask = new Mat(image.rows(), image.cols(), opencv_core.CV_8UC1);
opencv_core.bitwise_not(mask, mask); // 全 0
MatVector poly = new MatVector(1);
poly.put(0, rectPts);
opencv_imgproc.fillPoly(mask, poly, new Scalar(255, 255, 255, 0));
Mat gray = new Mat();
if (image.channels() == 3) {
opencv_imgproc.cvtColor(image, gray, opencv_imgproc.COLOR_BGR2GRAY);
} else if (image.channels() == 4) {
opencv_imgproc.cvtColor(image, gray, opencv_imgproc.COLOR_BGRA2GRAY);
} else {
gray = image.clone();
}
Mat binary = new Mat();
opencv_imgproc.threshold(gray, binary, 0, 255,
opencv_imgproc.THRESH_BINARY + opencv_imgproc.THRESH_OTSU);
opencv_core.bitwise_not(binary, binary);
Mat kernel = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, new Size(3, 3));
opencv_imgproc.morphologyEx(binary, binary, opencv_imgproc.MORPH_CLOSE, kernel);
opencv_imgproc.morphologyEx(binary, binary, opencv_imgproc.MORPH_OPEN, kernel);
Mat binaryMask = new Mat(image.rows(), image.cols(), opencv_core.CV_8UC1);
opencv_core.bitwise_and(binary, mask, binaryMask);
Mat largeKernel = opencv_imgproc.getStructuringElement(opencv_imgproc.MORPH_RECT, new Size(15, 15));
opencv_imgproc.morphologyEx(binaryMask, binaryMask, opencv_imgproc.MORPH_CLOSE, largeKernel);
// 3) 查找轮廓(JavaCV 需要 MatVector)
MatVector contours = new MatVector();
Mat hierarchy = new Mat();
opencv_imgproc.findContours(binaryMask, contours, hierarchy,
opencv_imgproc.RETR_EXTERNAL, opencv_imgproc.CHAIN_APPROX_SIMPLE);
if (contours.size() == 0) {
return null;
}
// 4) 选择最大面积轮廓
Mat largest = contours.get(0);
double maxArea = opencv_imgproc.contourArea(largest);
for (long i = 1; i < contours.size(); i++) {
Mat c = contours.get(i);
double a = opencv_imgproc.contourArea(c);
if (a > maxArea) {
maxArea = a;
largest = c;
}
}
int n = largest.rows();
if (n <= 0) return null;
IntRawIndexer cIdx = largest.createIndexer();
// 5) 均匀采样点
int outCount = Math.min(samplePoints, n);
Mat result = new Mat(outCount, 1, opencv_core.CV_32SC2);
IntRawIndexer rIdx = result.createIndexer();
// 计算累积弧长
double[] cum = new double[n + 1];
cum[0] = 0;
for (int i = 0; i < n - 1; i++) {
int x0 = cIdx.get(i, 0, 0);
int y0 = cIdx.get(i, 0, 1);
int x1p = cIdx.get(i + 1, 0, 0);
int y1p = cIdx.get(i + 1, 0, 1);
double dx = x1p - x0;
double dy = y1p - y0;
cum[i + 1] = cum[i] + Math.hypot(dx, dy);
}
cum[n] = cum[n - 1];
if (cum[n] == 0) {
double step = (double) n / outCount;
for (int i = 0; i < outCount; i++) {
int idx = (int) Math.min(Math.round(i * step), n - 1);
rIdx.put(i, 0, 0, cIdx.get(idx, 0, 0));
rIdx.put(i, 0, 1, cIdx.get(idx, 0, 1));
}
} else {
for (int i = 0; i < outCount; i++) {
double dist = (double) i / outCount * cum[n];
int idx = 0;
for (int j = 0; j < n; j++) {
if (dist <= cum[j + 1]) { idx = j; break; }
}
idx = Math.min(idx, n - 2);
double segLen = cum[idx + 1] - cum[idx];
double t = segLen > 0 ? (dist - cum[idx]) / segLen : 0.0;
int x0 = cIdx.get(idx, 0, 0);
int y0 = cIdx.get(idx, 0, 1);
int x1p = cIdx.get(idx + 1, 0, 0);
int y1p = cIdx.get(idx + 1, 0, 1);
int xi = (int) Math.round((1 - t) * x0 + t * x1p);
int yi = (int) Math.round((1 - t) * y0 + t * y1p);
rIdx.put(i, 0, 0, xi);
rIdx.put(i, 0, 1, yi);
}
}
cIdx.release();
rIdx.release();
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 从 Android Bitmap 中提取 line 的轮廓点(JavaCV 版本)。
*/
public static List<PointF> extractContourFromBitmap(Bitmap bitmap,
double centerX, double centerY,
double width, double height,
double angle, int samplePoints) {
try {
if (bitmap == null || bitmap.isRecycled()) {
return null;
}
AndroidFrameConverter bitmapConverter = new AndroidFrameConverter();
OpenCVFrameConverter.ToMat toMat = new OpenCVFrameConverter.ToMat();
Frame frame = bitmapConverter.convert(bitmap);
if (frame == null) return null;
Mat image = toMat.convert(frame);
if (image == null || image.empty()) {
return null;
}
Mat contourMat = extractContourFromRotatedRect(image, centerX, centerY, width, height, angle, samplePoints);
if (contourMat == null || contourMat.empty()) {
return null;
}
List<PointF> out = new ArrayList<>();
IntRawIndexer idx = contourMat.createIndexer();
for (int i = 0; i < contourMat.rows(); i++) {
int x = idx.get(i, 0, 0);
int y = idx.get(i, 0, 1);
out.add(new PointF((float) x, (float) y));
}
idx.release();
return out;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static List<PointF> extractContourFromBitmap(Bitmap bitmap,
double centerX, double centerY,
double width, double height,
double angle) {
return extractContourFromBitmap(bitmap, centerX, centerY, width, height, angle, 200);
}
}
总之这样也可以导入且不会报错。
参考文章:
Android添加OpenCV支持,一步一步添加。
使用 OpenCV 进行 Android 开发
Android Studio(新版本) 配置OpenCV库
602

被折叠的 条评论
为什么被折叠?



