利用opencv处理截图图片,优化截图返回速度30+ms => 10+ms

This commit is contained in:
TonyJiangWJ 2024-06-13 23:56:35 +08:00
parent 0ac1d97045
commit b3d21e8db7
2 changed files with 95 additions and 10 deletions

View File

@ -11,6 +11,8 @@ import com.stardust.autojs.core.opencv.OpenCVHelper;
import com.stardust.pio.UncheckedIOException;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import java.io.FileNotFoundException;
@ -61,7 +63,11 @@ public class ImageWrapper {
if (image == null) {
return null;
}
return new ImageWrapper(toBitmap(image));
if (OpenCVHelper.isInitialized()) {
return ofImageByMat(image);
} else {
return new ImageWrapper(toBitmap(image));
}
}
public static ImageWrapper ofMat(Mat mat) {
@ -100,6 +106,47 @@ public class ImageWrapper {
return Bitmap.createBitmap(bitmap, 0, 0, image.getWidth(), image.getHeight());
}
public static ImageWrapper ofImageByMat(Image image) {
long start = System.currentTimeMillis();
// 获取Image的平面
Image.Plane[] planes = image.getPlanes();
Log.d("ImageWrapper", "ofImageByMat: planes.length: " + planes.length);
// 获取Image的宽高
int width = image.getWidth();
int height = image.getHeight();
Log.d("ImageWrapper", "ofImageByMat: width:" + width + " height:" + height);
Image.Plane plane = planes[0];
// 获取平面的数据缓冲区
ByteBuffer buffer = planes[0].getBuffer();
// 创建一个byte数组来存储缓冲区的数据
byte[] bytes = new byte[buffer.remaining()];
buffer.position(0);
int pixelStride = plane.getPixelStride();
int rowPadding = plane.getRowStride() - pixelStride * image.getWidth();
// 将数据从缓冲区拷贝到byte数组
buffer.get(bytes);
// 创建一个Mat对象
Mat mat = new Mat(height, width + rowPadding / pixelStride, CvType.CV_8UC4);
// 将byte数组拷贝到Mat对象
mat.put(0, 0, bytes);
if (width != mat.width()) {
Log.d("ImageWrapper", "ofImageByMat: mat width is not valid: " + mat.width() + " => " + width);
// 定义裁切区域
Rect rect = new Rect(0, 0, width, height);
// 裁切图像
Mat croppedImage = new Mat(mat, rect);
mat.release();
mat = croppedImage;
}
Log.d("ImageWrapper", "ofImageByMat: create by mat cost: " + (System.currentTimeMillis() - start) + "ms");
return new ImageWrapper(mat);
}
public int getWidth() {
ensureNotRecycled();
return mWidth;

View File

@ -29,6 +29,8 @@ import org.mozilla.javascript.ast.Loop;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import androidx.annotation.Nullable;
@ -64,6 +66,10 @@ public class GlobalScreenCapture {
private boolean hasPermission;
private boolean noRegister;
private ReentrantLock captureLock = new ReentrantLock();
private Condition captureComplete = captureLock.newCondition();
@SuppressLint("StaticFieldLeak")
private static volatile GlobalScreenCapture INSTANCE;
@ -234,11 +240,17 @@ public class GlobalScreenCapture {
if (noRegister) {
return;
}
Image oldCacheImage = mCachedImage.getAndSet(null);
if (oldCacheImage != null) {
oldCacheImage.close();
captureLock.lock();
try {
Image oldCacheImage = mCachedImage.getAndSet(null);
if (oldCacheImage != null) {
oldCacheImage.close();
}
mCachedImage.set(reader.acquireLatestImage());
captureComplete.signal();
} finally {
captureLock.unlock();
}
mCachedImage.set(reader.acquireLatestImage());
} catch (Exception e) {
mException = e;
}
@ -257,13 +269,26 @@ public class GlobalScreenCapture {
long startTime = System.currentTimeMillis();
int retryLimit = 5;
while (!thread.isInterrupted()) {
Image cachedImage = mCachedImage.getAndSet(null);
Image cachedImage = getCachedImage();
if (cachedImage != null) {
if (mUnderUsingImage != null) {
mUnderUsingImage.close();
}
mUnderUsingImage = cachedImage;
return cachedImage;
} else {
Log.d(TAG, "capture: 加锁等待获取截图");
long waitStart = System.currentTimeMillis();
captureLock.lock();
try {
captureComplete.await();
Log.d(TAG, "capture: 获取到截图信号,等待耗时:" + (System.currentTimeMillis() - waitStart) + "ms");
cachedImage = getCachedImage();
if (cachedImage != null) {
return cachedImage;
}
Log.d(TAG, "capture: 获取到截图信号,但是图片已经被其他脚本获取 重新获取");
} catch (InterruptedException ex) {
throw new ScriptInterruptedException();
} finally {
captureLock.unlock();
}
}
if (System.currentTimeMillis() - startTime > 1000) {
startTime = System.currentTimeMillis();
@ -275,10 +300,23 @@ public class GlobalScreenCapture {
break;
}
}
}
throw new ScriptInterruptedException();
}
private Image getCachedImage() {
Image cachedImage = mCachedImage.getAndSet(null);
if (cachedImage != null) {
if (mUnderUsingImage != null) {
mUnderUsingImage.close();
}
mUnderUsingImage = cachedImage;
return cachedImage;
}
return null;
}
public synchronized void unregister(ScriptRuntime runtime) {
Log.d(TAG, "unregister: " + runtime);
registeredRuntimes.remove(runtime);