makcar/app/src/main/java/com/aros/apron/tools/DualCaptureHelper.java

270 lines
9.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.aros.apron.tools;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.os.Handler;
import android.os.Looper;
import com.aros.apron.constant.AMSConfig;
import com.aros.apron.entity.ArucoMarker;
import com.aros.apron.entity.Movement;
import com.aros.apron.manager.AlternateLandingManager;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.ArucoDetector;
import org.opencv.objdetect.DetectorParameters;
import org.opencv.objdetect.Dictionary;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 双图保存工具 - 单例版
* 持续接收帧,按钮触发保存
* 替换为【极致容错】图像增强逻辑,保留增强后图像的保存
*/
public class DualCaptureHelper {
private static final String TAG = "DualCapture";
private static final String TAG_LOG = "EnhanceError"; // 增强失败日志TAG
private static DualCaptureHelper instance;
private int count = 0;
private volatile boolean shouldCapture = false; // 按钮触发标志
private Handler mainHandler = new Handler(Looper.getMainLooper());
// 帧数据缓存(最新一帧)
private int lastHeight = 0;
private int lastWidth = 0;
private byte[] lastFrameData = null;
private DualCaptureHelper() {}
public static synchronized DualCaptureHelper getInstance() {
if (instance == null) {
instance = new DualCaptureHelper();
}
return instance;
}
/**
* 持续接收帧(在帧回调里调用,每次都有)
* 参数别改:和你 detectArucoTags 一致
*/
public void onFrame(int height, int width, byte[] yuvData) {
// 缓存最新帧
this.lastHeight = height;
this.lastWidth = width;
this.lastFrameData = yuvData;
// 如果按钮触发了,保存这一帧
if (shouldCapture && lastFrameData != null) {
shouldCapture = false; // 重置,只保存一次
final int num = ++count;
final String time = new SimpleDateFormat("HHmmss", Locale.CHINA).format(new Date());
// 子线程保存
new Thread(() -> {
saveRaw(lastHeight, lastWidth, lastFrameData, time, num);
saveEnhanced(lastHeight, lastWidth, lastFrameData, time, num);
mainHandler.post(() -> {
LogUtil.log(TAG, "【完成】第" + num + "");
});
}).start();
}
}
/**
* 按钮调用:触发下一帧保存
* 绑定到你的按钮点击
*/
public void captureNextFrame() {
shouldCapture = true;
LogUtil.log(TAG, "【等待】下一帧保存...");
}
/**
* 原始图YUV直接转BGR无任何处理
*/
private void saveRaw(int height, int width, byte[] yuvData, String time, int num) {
try {
Mat yuv = new Mat(height + height / 2, width, CvType.CV_8UC1);
yuv.put(0, 0, yuvData);
Mat bgr = new Mat();
Imgproc.cvtColor(yuv, bgr, Imgproc.COLOR_YUV2BGR_I420);
yuv.release();
File dir = getSaveDir();
String name = String.format("raw_%s_%03d.jpg", time, num);
String path = new File(dir, name).getAbsolutePath();
Imgcodecs.imwrite(path, bgr, new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 95));
bgr.release();
LogUtil.log(TAG, "【原始】" + path);
} catch (Exception e) {
LogUtil.log(TAG, "【原始失败】" + e.getMessage());
}
}
/**
* 增强图:应用【极致容错】图像增强流程
*/
private void saveEnhanced(int height, int width, byte[] yuvData, String time, int num) {
try {
Mat yuv = new Mat(height + height / 2, width, CvType.CV_8UC1);
yuv.put(0, 0, yuvData);
Mat gray = new Mat();
Imgproc.cvtColor(yuv, gray, Imgproc.COLOR_YUV2GRAY_I420);
yuv.release();
// 替换为极致容错的增强逻辑
Mat enhanced = createEnhancedImage(gray);
gray.release();
Mat bgr = new Mat();
Imgproc.cvtColor(enhanced, bgr, Imgproc.COLOR_GRAY2BGR);
enhanced.release();
File dir = getSaveDir();
String name = String.format("enhanced_%s_%03d.jpg", time, num);
String path = new File(dir, name).getAbsolutePath();
Imgcodecs.imwrite(path, bgr, new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 95));
bgr.release();
LogUtil.log(TAG, "【增强】" + path);
} catch (Exception e) {
LogUtil.log(TAG, "【增强失败】" + e.getMessage());
}
}
// ========== 【极致容错】图像增强:全高度段通用 ==========
private Mat createEnhancedImage(Mat src) {
Mat result = new Mat();
try {
// 1. 多尺度CLAHE适应不同亮度
Mat claheMat = new Mat();
Imgproc.createCLAHE(2.0, new Size(8, 8)).apply(src, claheMat);
// 2. 中值滤波去噪(比双边快,保边足够)
Mat filtered = new Mat();
Imgproc.medianBlur(claheMat, filtered, 5);
claheMat.release();
// 3. 自适应阈值(块大小动态,适应全高度)
Mat binary = new Mat();
Imgproc.adaptiveThreshold(filtered, binary, 255,
Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
Imgproc.THRESH_BINARY, 41, 3); // 块41更大常数3更敏感
filtered.release();
// 4. 形态学操作(连接断裂边框)
Mat morph = new Mat();
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));
Imgproc.morphologyEx(binary, morph, Imgproc.MORPH_CLOSE, kernel);
kernel.release();
binary.release();
// 5. 轻度锐化(突出边缘,不过度)
Mat sharpened = new Mat();
Mat blurred = new Mat();
Imgproc.GaussianBlur(morph, blurred, new Size(0, 0), 3.0);
Core.addWeighted(morph, 1.3, blurred, -0.3, 0, sharpened);
morph.release();
blurred.release();
return sharpened;
} catch (Exception e) {
LogUtil.log(TAG_LOG, "增强失败: " + e.getMessage());
src.copyTo(result);
return result;
}
}
// ========== 【超高容错】ArUco检测参数配置保留方法按需调用 ==========
private DetectorParameters createUltraTolerantParams() {
DetectorParameters params = new DetectorParameters();
// 全高度段4dm=375px, 9dm=190px, 50dm=19px
params.set_minMarkerPerimeterRate(0.003f); // 降到0.00319像素也能检
// 畸变/反光/模糊宽容
params.set_polygonalApproxAccuracyRate(0.12f); // 更松
params.set_cornerRefinementMethod(1); // SUBPIX
params.set_cornerRefinementWinSize(3); // 降到3更快
params.set_cornerRefinementMaxIterations(30);
params.set_cornerRefinementMinAccuracy(0.12f); // 放宽收敛
// 阈值范围更大,适应全光照
params.set_adaptiveThreshWinSizeMin(3);
params.set_adaptiveThreshWinSizeMax(63); // 更大
params.set_adaptiveThreshWinSizeStep(10);
params.set_adaptiveThreshConstant(3); // 配合预处理
params.set_minCornerDistanceRate(0.02f);
params.set_minMarkerLengthRatioOriginalImg(0.03f); // 更宽容
params.set_minDistanceToBorder(1); // 边缘也检
params.set_perspectiveRemovePixelPerCell(2); // 降到2小像素精细
params.set_perspectiveRemoveIgnoredMarginPerCell(0.2f);
params.set_maxErroneousBitsInBorderRate(0.6f); // 60%容错
params.set_detectInvertedMarker(false);
return params;
}
private File getSaveDir() {
File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
"DJIDemo/DualCapture");
if (!dir.exists()) dir.mkdirs();
return dir;
}
public void reset() {
count = 0;
LogUtil.log(TAG, "【重置】");
}
public int getCount() {
return count;
}
}