Android&NCNN学习—nanodet实例

demo:https://github.com/nihui/ncnn-android-nanodet
学习自:https://zhuanlan.zhihu.com/p/390050962

添加权限:

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera2.full" />

布局文件:
在这里插入图片描述
这里布局由三部分组成,第一部分是一个button来切换前后摄像头、第二部分是两个Spinner分别用来切换模型和切换CPU/GPU,最后就是一个SurfaceView用来预览。

【MainActivity代码】

// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

package com.tencent.nanodetncnn;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;

import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;

public class MainActivity extends Activity implements SurfaceHolder.Callback
{
    //初始化的一些变量
    public static final int REQUEST_CAMERA = 100;

    private NanoDetNcnn nanodetncnn = new NanoDetNcnn(); //调用ncnn的java接口类
    private int facing = 0; //用来记录前摄还是后摄

    private Spinner spinnerModel; //切换模型
    private Spinner spinnerCPUGPU; //切换CPU、GPU
    private int current_model = 0; // 记录当前模型
    private int current_cpugpu = 0; // 记录当前是CPU/GPU

    private SurfaceView cameraView; //预览界面

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main); //加载布局

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //保持屏幕唤醒不锁屏

        // 绑定预览的控件,并设置格式为RGBA8888
        cameraView = (SurfaceView) findViewById(R.id.cameraview);

        cameraView.getHolder().setFormat(PixelFormat.RGBA_8888);
        cameraView.getHolder().addCallback(this);

        // 绑定切换镜头的按键,并设置监听函数
        Button buttonSwitchCamera = (Button) findViewById(R.id.buttonSwitchCamera);
        buttonSwitchCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {

                int new_facing = 1 - facing; //切换摄像头

                nanodetncnn.closeCamera(); //关闭打开的摄像头

                nanodetncnn.openCamera(new_facing); // 用切换后的摄像头重新打开

                facing = new_facing;
            }
        });

        // 绑定切换模型的控件,并设置监听函数
        spinnerModel = (Spinner) findViewById(R.id.spinnerModel);
        spinnerModel.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id)
            {
                if (position != current_model)
                {
                    current_model = position; //通过spinner点击的position来确定用那个模型
                    reload(); //重新加载一下
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0)
            {
            }
        });

        // 绑定切换内核的控件,设置监听函数
        spinnerCPUGPU = (Spinner) findViewById(R.id.spinnerCPUGPU);
        spinnerCPUGPU.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id)
            {
                if (position != current_cpugpu)
                {
                    current_cpugpu = position; //通过spinner点击的position来确定用哪个内核
                    reload(); //重新加载一下
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0)
            {
            }
        });
        // 初始化完成后,重新加载模型
        reload();
    }

    private void reload()
    {
        // 重新加载当前选定的模型到指定的内核
        boolean ret_init = nanodetncnn.loadModel(getAssets(), current_model, current_cpugpu);
        if (!ret_init)
        {
            Log.e("MainActivity", "nanodetncnn loadModel failed");
        }
    }

    // 这是SurfaceView需要实现的三个方法
    // surface尺寸变换时调用,如横竖屏切换
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        nanodetncnn.setOutputWindow(holder.getSurface());
    }
    // 创建时调用,一般在该方法中启动绘图的线程
    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
    }
    // 被销毁时调用,如退出画面,一般在该方法中停止绘图线程
    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
    }
    // onResume方法,app被覆盖恢复后,重新开启摄像头
    @Override
    public void onResume()
    {
        super.onResume();

        if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED)
        {
            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA}, REQUEST_CAMERA);
        }

        nanodetncnn.openCamera(facing);
    }
    // onPause方法,app被覆盖后,关闭ncnn的摄像头即可
    @Override
    public void onPause()
    {
        super.onPause();

        nanodetncnn.closeCamera();
    }
}

【ncnn代码】

[NanoDetNcnn.java文件]

在这里插入图片描述
这四个方法都是c++实现的,后面那里就是加载了个叫nanodetncnn的library; 这里NanoDetNcnn对外提供了loadModel、openCamera、closeCamera和setOutputWindow这四个方法。

loadModel方法

这两个是可选模型的名字跟这些模型对应要用的image的size。
在这里插入图片描述
这两个是各个模型对应的mean和norm,
在这里插入图片描述
最后就是用确定好的模型、目标大小、mean、norm、以及内核。
在这里插入图片描述
这里的g_nanodet是NanoDet类的一个对象,这个类在nanodet.h和nanodet.cpp这两个文件有定义和实现。

【nanodet.h】

nanodet.h这个文件比较简单,就是定义了一个目标的结构体,用来记录目标的矩形框、标签和对应的概率,然后就是我们要关注的类NanoDet,这个类有两个load,这里我们前面的代码用的是第二个load,因此这里我们主要看第二load.
在这里插入图片描述

【nanaodet.cpp】

在这里插入图片描述
openCamera:
在这里插入图片描述
这里的g_camera是MyNdkCamera类的对象,MyNdkCamera类是NdkCameraWindow类的子类,主要是多实现了一个on_image_render方法,这些都在nanodetncnn.cpp文件里面,这个NdkCameraWindow类又是NdkCamera类的子类,这两个类都在ndkcamera.h和ndkcamera.cpp这两个文件里面实现。
on_image_render方法:在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ANIMZLS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值