android 使用 onnxruntime 部署 scrfd 人脸检测

2023-11-03

 

下面是使用 opencv-camera,实时处理区域内人脸检测 android 推理 demo。

首先是整合 opcv-camera 进去:

为了方便直接将整个 opencv-android-sdk 全部导入:

 然后在原来的项目模块app中添加 opencv的 java 相关依赖,主要添加红色两行:
app/build.grandle

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation project(':opencvsdk')
}

最后在项目中要使用opencv的地方加载jni库,可以添加到 MainActivity 中:

System.loadLibrary("opencv_java4"); 或者 OpenCVLoader.initDebug();

要使用 opencv-camera,MainActivity 继承 CameraActivity,然后在回调函数中获取每一帧进行处理,比如下面对每一帧添加识别区域边框:

    // 获取每一帧回调数据
    private CameraBridgeViewBase.CvCameraViewListener2 cameraViewListener2 = new CameraBridgeViewBase.CvCameraViewListener2() {
        @Override
        public void onCameraViewStarted(int width, int height) {
            System.out.println("开始预览 width="+width+",height="+height);
            // 预览界面长宽分别是识别区域的两倍,识别区域正中间正常形区域
            int detection_x1 = (win_w - OnnxUtil.w)/2;
            int detection_x2 = (win_w - OnnxUtil.w)/2 + OnnxUtil.w;
            int detection_y1 = (win_h - OnnxUtil.h)/2;
            int detection_y2 = (win_h - OnnxUtil.h)/2 + OnnxUtil.h;;
            // 缓存识别区域两个点
            detection_p1 = new Point(detection_x1,detection_y1);
            detection_p2 = new Point(detection_x2,detection_y2);
            detection_box_color = new Scalar(255, 0, 0);
            detection_box_tickness = 1;
        }
        @Override
        public void onCameraViewStopped() {}
        @Override
        public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame frame) {

            // 获取 cv::Mat
            Mat mat = frame.rgba();

            // 标注识别区域
            Imgproc.rectangle(mat, detection_p1, detection_p2,detection_box_color,detection_box_tickness);
            Imgproc.putText(mat,"RecArea",detection_p1,Imgproc.FONT_HERSHEY_SIMPLEX,0.5,detection_box_color);

            // 推理并标注
            OnnxUtil.inference(mat,detection_p1,detection_p2);

            return mat;
        }
    };

在界面中开启预览:

    // 开启预览
    private BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    if (camera2View != null) {
                        // 设置前置还是后置摄像头 0后置 1前置
                        camera2View.setCameraIndex(cameraId);
                        // 注册每一帧回调
                        camera2View.setCvCameraViewListener(cameraViewListener2);
                        // 显示/关闭 帧率  disableFpsMeter/enableFpsMeter
                        // 要修改字体和颜色直接修改 FpsMeter 类即可
                        camera2View.enableFpsMeter();
                        // 设置视图宽高和模型一致减少resize操作,模型输入一般尺寸不大,这样相机渲染fps会更高
                        camera2View.setMaxFrameSize(win_w,win_h);
                        // 开启
                        camera2View.enableView();
                    }
                }
                break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    };

下面是全部推理 MainActivity 代码:

package com.example.camera_opencv;


import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.WindowManager;
import com.example.camera_opencv.databinding.ActivityMainBinding;
import org.opencv.android.*;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends CameraActivity{

    // 动态库
    static {
        // 我们自己的jni
        System.loadLibrary("camera_opencv");
        // 新加的 opencv 的jni
        System.loadLibrary("opencv_java4");
    }

    private ActivityMainBinding binding;

    // 预览界面
    private JavaCamera2View camera2View;

    // 相机编号 0后置 1前置
    private int cameraId = 1;

    // 设置预览界面宽高,在次宽高基础上限制识别区域
    private int win_w = 320;
    private int win_h = 240;

    // 识别区域两个点
    private Point detection_p1;
    private Point detection_p2;
    private Scalar detection_box_color;
    private int detection_box_tickness;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // 加载模型
        OnnxUtil.loadModule(getAssets());

        // 强制横屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        // 隐藏上方状态栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        // 预览界面
        camera2View = findViewById(R.id.camera_view);
    }

    @Override
    protected List<? extends CameraBridgeViewBase> getCameraViewList() {
        return Arrays.asList(camera2View);
    }


    @Override
    public void onPause() {
        super.onPause();
        if (camera2View != null) {
            // 关闭预览
            camera2View.disableView();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (OpenCVLoader.initDebug()) {
            baseLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        } else {
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, baseLoaderCallback);
        }
    }

    // 获取每一帧回调数据
    private CameraBridgeViewBase.CvCameraViewListener2 cameraViewListener2 = new CameraBridgeViewBase.CvCameraViewListener2() {
        @Override
        public void onCameraViewStarted(int width, int height) {
            System.out.println("开始预览 width="+width+",height="+height);
            // 预览界面长宽分别是识别区域的两倍,识别区域正中间正常形区域
            int detection_x1 = (win_w - OnnxUtil.w)/2;
            int detection_x2 = (win_w - OnnxUtil.w)/2 + OnnxUtil.w;
            int detection_y1 = (win_h - OnnxUtil.h)/2;
            int detection_y2 = (win_h - OnnxUtil.h)/2 + OnnxUtil.h;;
            // 缓存识别区域两个点
            detection_p1 = new Point(detection_x1,detection_y1);
            detection_p2 = new Point(detection_x2,detection_y2);
            detection_box_color = new Scalar(255, 0, 0);
            detection_box_tickness = 1;
        }
        @Override
        public void onCameraViewStopped() {}
        @Override
        public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame frame) {

            // 获取 cv::Mat
            Mat mat = frame.rgba();

            // 标注识别区域
            Imgproc.rectangle(mat, detection_p1, detection_p2,detection_box_color,detection_box_tickness);
            Imgproc.putText(mat,"RecArea",detection_p1,Imgproc.FONT_HERSHEY_SIMPLEX,0.5,detection_box_color);

            // 推理并标注
            OnnxUtil.inference(mat,detection_p1,detection_p2);

            return mat;
        }
    };

    // 开启预览
    private BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    if (camera2View != null) {
                        // 设置前置还是后置摄像头 0后置 1前置
                        camera2View.setCameraIndex(cameraId);
                        // 注册每一帧回调
                        camera2View.setCvCameraViewListener(cameraViewListener2);
                        // 显示/关闭 帧率  disableFpsMeter/enableFpsMeter
                        // 要修改字体和颜色直接修改 FpsMeter 类即可
                        camera2View.enableFpsMeter();
                        // 设置视图宽高和模型一致减少resize操作,模型输入一般尺寸不大,这样相机渲染fps会更高
                        camera2View.setMaxFrameSize(win_w,win_h);
                        // 开启
                        camera2View.enableView();
                    }
                }
                break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    };

}

onnx 模型加载和推理代码:
使用的微软onnx推理框架:

implementation 'com.microsoft.onnxruntime:onnxruntime-android:latest.release'
implementation 'com.microsoft.onnxruntime:onnxruntime-extensions-android:latest.release'

package com.example.camera_opencv;

import ai.onnxruntime.*;
import android.content.res.AssetManager;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.util.*;

public class OnnxUtil {

    // onnxruntime 环境
    public static OrtEnvironment env;
    public static OrtSession session;

    // 模型输入
    public static int w = 0;
    public static int h = 0;
    public static int c = 3;

    // 标注颜色
    public static Scalar green = new Scalar(0, 255, 0);
    public static int tickness = 1;

    // 模型加载
    public static void loadModule(AssetManager assetManager){

        // 模型最大输入是 640*640,为了提高帧率这里选择 160*160
        w = 160;
        h = 160;
        c = 3;

        try {

            InputStream inputStream = assetManager.open("scrfd_500m_bnkps.onnx");
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int nRead;
            byte[] data = new byte[1024];
            while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();
            byte[] module = buffer.toByteArray();
            System.out.println("开始加载模型");
            env = OrtEnvironment.getEnvironment();
            session = env.createSession(module, new OrtSession.SessionOptions());
            session.getInputInfo().entrySet().stream().forEach(n -> {
                String inputName = n.getKey();
                NodeInfo inputInfo = n.getValue();
                long[] shape = ((TensorInfo) inputInfo.getInfo()).getShape();
                String javaType = ((TensorInfo) inputInfo.getInfo()).type.toString();
                System.out.println("模型输入:  "+inputName + " -> " + Arrays.toString(shape) + " -> " + javaType);
            });
            session.getOutputInfo().entrySet().stream().forEach(n -> {
                String outputName = n.getKey();
                NodeInfo outputInfo = n.getValue();
                long[] shape = ((TensorInfo) outputInfo.getInfo()).getShape();
                String javaType = ((TensorInfo) outputInfo.getInfo()).type.toString();
                System.out.println("模型输出:  "+outputName + " -> " + Arrays.toString(shape) + " -> " + javaType);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    // 模型推理,输入原始图片和识别区域两个点
    public static void inference(Mat mat,Point detection_p1,Point detection_p2){

        int px = Double.valueOf(detection_p1.x).intValue();
        int py = Double.valueOf(detection_p1.y).intValue();

        // 提取rgb(chw存储)并做归一化,也就是 rrrrr bbbbb ggggg
        float[] chw = new float[c*h*w];
        // 像素点索引
        int pi = 0;
        for(int j=0 ; j<h ; j++){
            for(int i=0 ; i<w ; i++){
                // 第j行,第i列,根据识别区域p1得到xy坐标的偏移,直接加就行
                double[] rgb = mat.get(j+py,i+px);
                // 缓存到 chw 中,mat 是 rgba 数据对应的下标 2103
                chw[pi] = (float)(rgb[2]/255);//r
                chw[pi + w * h * 1 ] = (float)(rgb[1]/255);//G
                chw[pi + w * h * 2 ] = (float)(rgb[0]/255);//b
                pi ++;
            }
        }

        // 创建张量并进行推理
        try {

            // ---------模型[2]输入-----------
            // input.1 -> [1, 3, -1, -1] -> FLOAT
            // ---------模型[2]输出-----------
            // 447 -> [12800, 1] -> FLOAT
            // 473 -> [3200, 1] -> FLOAT
            // 499 -> [800, 1] -> FLOAT
            // 450 -> [12800, 4] -> FLOAT
            // 476 -> [3200, 4] -> FLOAT
            // 502 -> [800, 4] -> FLOAT
            // 453 -> [12800, 10] -> FLOAT
            // 479 -> [3200, 10] -> FLOAT
            // 505 -> [800, 10] -> FLOAT
            OnnxTensor tensor = OnnxTensor.createTensor(env, FloatBuffer.wrap(chw), new long[]{1,c,h,w});
            OrtSession.Result out = session.run(Collections.singletonMap("input.1", tensor));

            // 打印输入张量shape
            long[] shape = tensor.getInfo().getShape();

            // 置信度阈值和iou阈值
            float score_thres = 0.5f;
            float iou_thres = 0.7f;

            // 因为这里没有进行缩放,直接将检测区域的 160*160 输入模型
            float imgScale = 1.0f;

            // 检测步长
            List<float[]> temp = new ArrayList<>();
            int[] net_stride = new int[]{8, 16, 32};
            for(int index = 0;index < net_stride.length;index++){
                int stride = net_stride[index];
                float[][] scores = (float[][]) out.get(index).getValue();
                float[][] boxes = (float[][]) out.get(index + 3).getValue();
                float[][] points = (float[][]) out.get(index + 6).getValue();
                int ws = (int) Math.ceil(1.0f * shape[3] / stride);
                // 人脸框的个数
                int count = scores.length;
                for(int i=0;i<count;i++){
                    float score = scores[i][0];// 分数
                    if(score >= score_thres){
                        int anchorIndex = i / 2;
                        int rowNum = anchorIndex / ws;
                        int colNum = anchorIndex % ws;
                        //计算人脸框,坐标缩放到原始图片中
                        float anchorX = colNum * net_stride[index];
                        float anchorY = rowNum * net_stride[index];
                        float x1 = (anchorX - boxes[i][0] * net_stride[index])  * imgScale;
                        float y1 = (anchorY - boxes[i][1] * net_stride[index])  * imgScale;
                        float x2 = (anchorX + boxes[i][2] * net_stride[index])  * imgScale;
                        float y2 = (anchorY + boxes[i][3] * net_stride[index])  * imgScale;
                        // 关键点集合
                        float [] point = points[i];
                        // 5个关键点,坐标缩放到原始图片中
                        float pointX_1 = (point[0] * net_stride[index] + anchorX)  * imgScale;
                        float pointY_1 = (point[1] * net_stride[index] + anchorY)  * imgScale;
                        float pointX_2 = (point[2] * net_stride[index] + anchorX)  * imgScale;
                        float pointY_2 = (point[3] * net_stride[index] + anchorY)  * imgScale;
                        float pointX_3 = (point[4] * net_stride[index] + anchorX)  * imgScale;
                        float pointY_3 = (point[5] * net_stride[index] + anchorY)  * imgScale;
                        float pointX_4 = (point[6] * net_stride[index] + anchorX)  * imgScale;
                        float pointY_4 = (point[7] * net_stride[index] + anchorY)  * imgScale;
                        float pointX_5 = (point[8] * net_stride[index] + anchorX)  * imgScale;
                        float pointY_5 = (point[8] * net_stride[index] + anchorY)  * imgScale;
                        // 保存到tmp中,注意x1y1x2y2不能超出 w*h
                        temp.add(new float[]{
                                score,
                                x1>w?w:x1,
                                y1>h?h:y1,
                                x2>w?w:x2,
                                y2>h?h:y2,
                                pointX_1,pointY_1,
                                pointX_2,pointY_2,
                                pointX_3,pointY_3,
                                pointX_4,pointY_4,
                                pointX_5,pointY_5
                        });
                    }
                }
            }

            // nms
            ArrayList<float[]> datas_after_nms = new ArrayList<>();
            while (!temp.isEmpty()){
                float[] max = temp.get(0);
                datas_after_nms.add(max);
                Iterator<float[]> it = temp.iterator();
                while (it.hasNext()) {
                    // nsm阈值
                    float[] obj = it.next();
                    // x1y1x2y2 是 1234
                    double iou = calculateIoU(max,obj);
                    if (iou > iou_thres) {
                        it.remove();
                    }
                }
            }

            // 标注
            datas_after_nms.stream().forEach(n->{

                // 画边框和关键点需要添加偏移
                int x1 = Float.valueOf(n[1]).intValue() + px;
                int y1 = Float.valueOf(n[2]).intValue() + py;
                int x2 = Float.valueOf(n[3]).intValue() + px;
                int y2 = Float.valueOf(n[4]).intValue() + py;
                Imgproc.rectangle(mat, new Point(x1, y1), new Point(x2, y2), green, tickness);

                float point1_x = Float.valueOf(n[5]).intValue() + px;// 关键点1
                float point1_y = Float.valueOf(n[6]).intValue() + py;//
                float point2_x = Float.valueOf(n[7]).intValue() + px;// 关键点2
                float point2_y = Float.valueOf(n[8]).intValue() + py;//
                float point3_x = Float.valueOf(n[9]).intValue() + px;// 关键点3
                float point3_y = Float.valueOf(n[10]).intValue() + py;//

                Imgproc.circle(mat, new Point(point1_x, point1_y), 1, green, 2);
                Imgproc.circle(mat, new Point(point2_x, point2_y), 1, green, 2);
                Imgproc.circle(mat, new Point(point3_x, point3_y), 1, green, 2);

            });

        }
        catch (Exception e){
            e.printStackTrace();
        }
    }


    // 计算两个框的交并比
    private static double calculateIoU(float[] box1, float[] box2) {
        //  getXYXY() 返回 xmin-0 ymin-1 xmax-2 ymax-3
        double x1 = Math.max(box1[1], box2[1]);
        double y1 = Math.max(box1[2], box2[2]);
        double x2 = Math.min(box1[3], box2[3]);
        double y2 = Math.min(box1[4], box2[4]);
        double intersectionArea = Math.max(0, x2 - x1 + 1) * Math.max(0, y2 - y1 + 1);
        double box1Area = (box1[3] - box1[1] + 1) * (box1[4] - box1[2] + 1);
        double box2Area = (box2[3] - box2[1] + 1) * (box2[4] - box2[2] + 1);
        double unionArea = box1Area + box2Area - intersectionArea;
        return intersectionArea / unionArea;
    }

}




项目详细代码:

https://github.com/TangYuFan/deeplearn-mobile

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android 使用 onnxruntime 部署 scrfd 人脸检测 的相关文章

  • Android - 保存动态更改布局的状态

    我有一个布局 用户可以在其中添加按钮并将其放置在他们想要的位置 我想允许用户保存他们的布局 以便下次打开应用程序时加载它 有谁知道我是否可以将文件保存到 SD 卡上 或者 我可以使用某种layout getXml 方法并将其放入我的应用程序
  • BottomNavigationView - 如何获取选定的菜单项?

    我使用BottomNavigationView来切换片段 如何获取当前选定的菜单项 以防止重新打开片段 BottomNavigationView bottomNavigationView BottomNavigationView findV
  • Android 应用程序在后台运行时保存数据

    目前我正在开发 xmmp 客户端 当应用程序位于前台时 该客户端工作得很好 但由于事实上 当应用程序处于后台时 我在 Application 类中保存了大量数据 复杂的 ArrayList 字符串和布尔值作为公共静态 每个字段都被垃圾收集
  • Android libgdx 首选项丢失

    我在 Libgdx 引擎中创建了 Android 游戏 一段时间后 我注意到在某些应用程序杀手中杀死该应用程序后 或者如果我在 Android 设置中执行 强制关闭 操作 我保存到首选项中的游戏选项就会丢失 有办法防止这种情况吗 我从来没有
  • (Ionic 2)尝试回退到 Cordova-lib 执行时发生错误:TypeError:无法读取未定义的属性“then”

    Edit 使用 ionic 2 时会发生这种情况 我知道它还不稳定 但我认为可能有一些解决方案 因为其他人似乎没有遇到这个问题 Edit end 由于某种原因 我在尝试使用 ionic build android 和 ionic build
  • RxJava、Proguard 和 sun.misc.Unsafe

    我有以下问题RxJava 1 1 0 使用时Proguard 我没有更改 RxJava 版本或其 pro文件 但更新后OkHttp我无法编译使用Proguard因为我有关于sun misc Unsafe不在场 rxJava pro keep
  • 设置从 Facebook 登录获取用户电子邮件 ID 的权限

    我在用着Facebook 3 0 SDK对于安卓 我必须实施Facebook登录 我正在访问用户的基本信息 例如姓名 用户 ID 但我也想访问用户的电子邮件 我浏览了很多博客和论坛 但不知道该怎么做 我正在使用我自己的 android 按钮
  • 如果我们使用后退按钮退出,为什么 Android 应用程序会重新启动?

    按住主页按钮并返回应用程序时 应用程序不会重新启动 为什么使用后退按钮会重新启动 如果我们使用后退按钮退出 有什么方法可以解决在不重新启动的情况下获取应用程序的问题吗 请帮忙 当您按下Home按钮 应用程序将暂停并保存当前状态 最后应用程序
  • 如何查找 Android 设备中的所有文件并将它们放入列表中?

    我正在寻求帮助来列出 Android 外部存储设备中的所有文件 我想查找所有文件夹 包括主文件夹的子文件夹 有办法吗 我已经做了一个基本的工作 但我仍然没有得到想要的结果 这不起作用 这是我的代码 File files array file
  • Android 原理图内容提供程序库配置?

    Jake Wharton 在最近的一次演讲中提到了这个库 它看起来是避免大量样板文件的好方法 所以我尝试了一下 但没有任何成功 https github com SimonVT schematic https github com Simo
  • 从 Firebase 数据库填充微调器

    public class MainActivity extends AppCompatActivity DatabaseReference reference Spinner areaSpinner ArrayList
  • 带有自定义阵列适配器的微调器不允许选择项目

    我使用自定义阵列适配器作为微调器 但是 当在下拉列表中选择一个项目时 下拉列表保留在那里 并且微调器不会更新 这是错误行为 与使用带有字符串的通用数组适配器相比 这是自定义类 我错过了什么吗 谢谢 public class Calendar
  • ROOM迁移过程中如何处理索引信息

    CODE Entity tableName UserRepo indices Index value id unique true public class GitHubRepo PrimaryKey autoGenerate true p
  • 使用 Matrix.setPolyToPoly 选择位图上具有 4 个点的区域

    我正在 Android 上使用位图 在使用 4 个点选择位图上的区域时遇到问题 并非所有 4 点组都适合我 在某些情况下 结果只是一个空白位图 而不是裁剪后的位图 如图所示 并且 logcat 中没有任何错误 甚至是内存错误 这是我用来进行
  • 如何在不更改手机语言的情况下更改Android应用程序语言?

    我希望用户在应用程序内选择一种语言 选择语言后 我希望字符串使用特定语言 如果我更改手机语言 那么我的应用程序将以设置的语言运行 我无法找到任何在不更改手机语言的情况下设置语言的方法 此外 一旦设置了语言 更改就应该反映出来 有人可以建议一
  • 材质设计图标颜色

    应该是哪种颜色 暗 材质图标 在官方文档上 https www google com design spec style icons html icons system icons https www google com design s
  • 在 Android 上按下电源按钮时,如何防止先调用 onDestroy() 再调用 onCreate()

    我正在记录每个 onCreate 和 onDestroy 调用 我发现 一旦我单击 Android 上的电源按钮 以及模拟器上的电源按钮 我的活动中就会拨打电话 gt onDestroy gt onCreate 这会杀死我的游戏 然后立即从
  • 应用程序关闭时的倒计时问题

    我制作了一个 CountDownTimer 代码 我希望 CountDownTimer 在完成时重新启动 即使应用程序已关闭 但它仅在应用程序正在运行或重新启动应用程序时重新启动 因此 如果我在倒计时为 00 10 分钟 秒 时关闭应用程序
  • android Accessibility-service 突然停止触发事件

    我有一个 AccessibilityService 工作正常 但由于开发过程中的某些原因它停止工作 我似乎找不到这个原因 请看一下我的代码并告诉我为什么它不起作用 public class MyServicee extends Access
  • 在 Android 中,如何将字符串从 Activity 传递到 Service?

    任何人都可以告诉如何将字符串或整数从活动传递到服务 我试图传递一个整数 setpossition 4 但它不需要 启动时总是需要 0 Service 我不知道为什么我不能通过使用 Service 实例从 Activity 进行操作 publ

随机推荐

  • 第二节:微信小程序静态页面开发初体验

    根据上一节了解到的小程序知识 尝试完成一个入门的demo项目 记录下过程 供日后参考 第一个页面 打算做一个新闻信息展示的静态页面 首先展示一下最后完成的效果 页面很简单 分成三个部分 页面标题 中间的轮播图和下面的图文信息 轮播图的实现
  • 云演CTF: 007.blog

    云演CTF 007 blog 作者 admin 时间 2021 05 28 分类 信息收集 打开就是登录界面 直接 123456 出现弹窗 还以为是js验证 想多了 CTRL u打开源码 function var login login l
  • 复原ip地址--回溯算法

    LeetCode 复原IP地址 给定一个只包含数字的字符串 复原它并返回所有可能的IP地址格式 有效的IP地址正好由四个整数 每个整数位于0到255之间组成 整数之间用 分隔 示例 输入 25525511135 输出 255 255 11
  • 径向基函数

    注意核函数是一回事 径向基函数是另一回事 核函数表示的是高维空间里由于向量内积而计算出来的一个函数表达式 后面将见到 而径向基函数是一类函数 径向基函数是一个它的值 y 只依赖于变量 x 距原点距离的函数 即 也可以是距其他某个中心点的距离
  • 我使用的Vim插件

    2023年9月5日 周二下午 为了方便以后还原自己的Vim插件配置 于是写这篇博客来记录一下 不定期更新 目录 语法检查Syntastic 文件树The NERD tree 自动补全括号auto pairs 超轻量级自动补全vim auto
  • 交换机端口安全实验

    文章目录 一 实验的背景与目的 二 实验拓扑 三 实验需求 四 实验解法 1 PC配置IP地址部分 2 在SW1上开启802 1X身份验证 3 创建一个用户身份验证的用户 用户名为wangdaye 密码为123456 4 创建一个端口隔离组
  • 死锁,死锁必要条件及处理策略

    大自然的搬运工 完美分割线 多线程中 常见的一种问题除了竞态条件外就是死锁 那什么是死锁呢 死锁就是 是指两个或两个以上的进程在执行过程中 因争夺资源而造成的一种互相等待的现象 若无外力作用 它们都将无法推进下去
  • 图片在div中居中

    一 方法1 html代码 div class content img src img 举手yeah png div css代码 content display flex justify content center align items
  • 微服务网关实战——Spring Cloud Gateway

    导读 作为Netflix Zuul的替代者 Spring Cloud Gateway是一款非常实用的微服务网关 在Spring Cloud微服务架构体系中发挥非常大的作用 本文对Spring Cloud Gateway常见使用场景进行了梳理
  • 带你一周刷完操作系统视频笔记(3)

    本片笔记将会从基本概念 进程结构 区分进程线程这三个方面解释什么是进程 process 概念 process是一个具有一定独立功能的程序关于某个数据集合的一次运行活动 是系统进行资源分配和调度的独立单位 是资源分配的最小单位 要点 进程是程
  • SD卡通信协议那些事

    SD卡通信 SD卡通信协议主要包括物理层 数据传输层和应用层三个部分 物理层 SD卡使用SPI或SDIO两种物理层协议进行通信 SPI是一种同步串行通信协议 使用4根信号线进行通信 SDIO是一种异步串行通信协议 使用9根信号线进行通信 数
  • Jenkins用户权限控制插件——Role-based Authorization Strategy

    看了下公司的jenkins权限配置 发现公司用的是Role based Authorization Strategy插件做的权限控制 之前公司一直用的是安全矩阵的方式控制的 这里就大概记录一下吧 使用Role based Authoriza
  • Mybatis 笔记(1)- 搭建最基础的springboot+mybatis结构

    不讲mybatis的历史 mybatis和springmvc的历史 这里只列举springboot和mybatis整合需要做哪些工作 1 添加依赖 版本可以根据你的实际情况自行调整 2 配置dao mapper interface 和 Ma
  • selenium-server

    Selenium grid for selenium1 and webdriver Introduction Grid allows you to scale by distributing tests on several machine
  • 最便宜的云服务器

    阿里云和腾讯云都有推广活动 所以价格比较低 但直接从首页下单还是原价 必须从他们的推广链接 看下文 进去下单才便宜 顺便比较了一下网易云 华为云 都没有这么便宜 阿里云 点击选购最便宜服务器 阿里云福利 阿里云1888元优惠券红包
  • linux查看各进程占用cpu/内存情况

    目录 一 ps top命令 1 ps命令 1 1 语法 1 2 使用场景 2 top命令 1 语法 2 top命令内容说明 3 top命令使用过程中的交互的命令 二 排序进程 1 复杂方法 2 简单方法 很多时候 你可能遇见过系统消耗了过多
  • 【底层驱动不含main】XPT2046 制作一个电位器AD转换装置

    简介 XPT2046是一款四线制电阻触摸屏控制芯片 内含12位分辨率125KHz转换速率逐步逼近型A D转换器 支持从1 5V到5 25V的低电压I O接口 所谓逐步逼近型 就是输入一个模拟量 其与1000 0000 0000 对应的模拟量
  • ubuntu20安装ros noetic

    记录下自己在虚拟机上安装ros Noetic的过程 也供大家参考 我使用的虚拟机是vmware 默认已经安装好ubuntu20了 ubuntu20对应的ros版本是noetic 这里只记录了最少的命令 大家可根据自己需要安装其它所需的包 1
  • shell while true

    7 while循环注意为方括号 且注意空格 min 1 max 100 while min le max do echo min min expr min 1 done 8 双括号形式 内部结构有点像C的语法 注意赋值 i i 1 i 1
  • android 使用 onnxruntime 部署 scrfd 人脸检测

    下面是使用 opencv camera 实时处理区域内人脸检测 android 推理 demo 首先是整合 opcv camera 进去 为了方便直接将整个 opencv android sdk 全部导入 然后在原来的项目模块app中添加