android关键点检测算法性能测试

markdown笔记-android关键点检测算法性能测试

1. 基本函数

  1. 编写java类
  2. 利用JAVA类生成.h头文件
  3. 利用头文件编写c++代码
  4. 生成dll动态链接库
  5. JAVA调用测试
import android.Manifest;
importandroid.support.v4.app.ActivityCompat;

1.adb调试工具

  • 使用top命令查看内存使用情况
\# 查看PID
adb shell top | grep app_name
\# 查看内存使用情况
adb shell dumpsys meminfo <package_name>
  • 将可执行文件传到设备运行
# 查看设备
Adb devices
# 使用某个设备号打开shell
adb -s 700S620070801 shell
Adb start-server
adb push HelloWorld /data/local/tmp
Adb shell
chmod 777 /data/local/tmp/HelloWorld
/data/local/tmp/HelloWorld
# 添加动态lib路径,便于找到动态库
export LD_LIBRARY_PATH=${PWD}:${LD_LIBRARY_PATH}
# 查看CPU信息
cat /proc/cpuinfo

3.android studio中profiler

PID:进程
PR:在android N之前代表运行在哪个核上,在android N上代表优先级,当然可能设备厂商会进行自定义
CPU%:cpu占用
S:运行状态
#THR:线程数
VSS:Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS:Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PCY:调度策略优先级,SP\_BACKGROUND/SP\_FOREGROUND
UID:进程所有者的用户id
Thread:线程名称
Name:进程名**cmake使用**

4.cmake使用

  1. 预处理(预编译):
  • 将所有的#define删除,并展开所有的宏定义
  • 处理所有的条件预编译指令,如#ifdef\undef\enddef
  • 处理#include,将包含的文件插入到此处
  • 删除所有注释,添加行号和标识,便于错误处理
  • 保留#pargma编译器指令
gcc -E  hello.c  -o hello.i
#  
cpp hello.c > hello.i 
  1. 编译
  • 将预处理完的.i文件进行一系列的词法分析、语法分析、语义分析及优化后生成响应的汇编代码文件
gcc  -S  hello.i   -o  hello.s
  1. 汇编
  • 汇编是将第二步生成的汇编代码编程机器可执行的指令,每一个汇编语句几乎都对应一条机器指令
gcc  -c  hello.s  -o  hello.o或者 as  hello.s -o  hello.o
  1. 链接
  • 链接动态库和静态库
 gcc hello.s  -o  hello.o

Gem

FIND_PACKAGE( OpenMP REQUIRED)
if(OPENMP_FOUND)
message("OPENMP FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
原文链接:https://blog.csdn.net/sinat_33442459/article/details/71215120

4.2.cmake中函数

find_library(名称1 [path1 path2 …])
作用:用于查找库。
VAR 创建名为的缓存条目以存储此命令的结果。
如果找到了库,结果将存储在变量中,除非清除变量,否则将不会重复搜索。
如果什么也没找到,结果将是-NOTFOUND。
REQUIRED如果未找到任何内容,该选项将停止处理并显示一条错误消息,
否则,下次使用相同的变量调 用find_library时,将再次尝试搜索。
NAMES 为库指定一个或多个可能的名称。
HINTS, PATHS 除了默认位置,还指定要搜索的目录。该子选项读取系统环境变量的路径。
DOC 指定缓存条目的文档字符串。
REQUIRED 如果未找到任何内容,则停止处理并显示错误消息。
# 用法示例
    寻找系统库
find_library( log-lib log )
    寻找指定路径库
find_library(my_lib libmylib.so ./)
    
  • 从源码安装cmake

    从源码安装cmake

  • add_library:将指定的文件生成链接文件,添加到项目工程中去

add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2] [...])

表示库文件的名字,STATIC\SHARED\MODULE指定生成库的类型

STATIC:目标文件的归档文件,在链接其他目标的时候使用

SHARED:动态链接库,在运行时被加载

MODULE:不会被链接到其他目标中的插件,但是可能在运行时使用dlopen-函数

  1. add_library也可以用OBJECT对象的方式来进行添加库,即不会将目标文件归档到动态库也不会归档到静态库,在调用的时候再选择是动态库调用还是静态库调用

    CMake菜谱(CMake Cookbook中文版)

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-03 LANGUAGES CXX)
add_library(message-objs
OBJECT
Message.hpp
Message.cpp
)
# this is only needed for older compilers
# but doesn't hurt either to have it
set_target_properties(message-objs
PROPERTIES
POSITION_INDEPENDENT_CODE 1
)
add_library(message-shared
SHARED
$<TARGET_OBJECTS:message-objs>
)
add_library(message-static
STATIC
$<TARGET_OBJECTS:message-objs>
)
add_executable(hello-world hello-world.cpp)
target_link_libraries(hello-world message-static)

# 通过add_library来导入静态库
add_library(MYLIB STATIC IMPORTED)
set_target_properties(MYLIB PROPERTIES IMPORTED_LOCATION path/to/mylib.a)
  • link_directories:该指令的作用主要是指定要链接的库文件的路径,该指令有时候不一定需要。因为find_package和find_library指令可以得到库文件的绝对路径。不过你自己写的动态库文件放在自己新建的目录下时,可以用该指令指定该目录的路径以便工程能够找到(相当于export LD_LIBRARY_PATH)
  • link_libraries:添加需要链接的库文件路径,注意这里是全路径(可以是静态文件也可以是动态文件)
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
  • target_link_libraries:将目标文件和库文件进行链接
target_link_libraries(<target> [item1] [item2] [...]
[[debug|optimized|general] <item>] ...)

上述指令中的是指通过add_executable()和add_library()指令生成已经创建的目标文件。而[item]表示库文件没有后缀的名字。默认情况下,库依赖项是传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的连接线上。这个传递的接口存储在interface_link_libraries的目标属性中,可以通过设置该属性直接重写传递接口。

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
include_directories("/opt/MATLAB/R2012a/extern/include")
LINK_DIRECTORIES("/opt/MATLAB/R2012a/bin/glnxa64")
add_executable(myProject main.cpp)
target_link_libraries(myProject eng mx)
#equals to below
#target_link_libraries(myProject -leng -lmx)
#target_link_libraries(myProject libeng.so libmx.so)

\# 示例二:
project(CGALhellworld) //项目名称,如果目标是vs,会是sln的名称
cmake_minimum_required(VERSION 3.1) //版本申明
 
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_compile_options(-fPIC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fpic") //cmake.exe的环境变量。具体百度cmake配置
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpic")
 
include_directories(./3rdParty/windows/include)//include路径
aux_source_directory(./AreaCaculate  Allcpp )//添加AreaCaculate这个文件夹下的所有cpp,并命名为allcpp
link_directories(./3rdParty/android/lib/32) //添加静态库路径
 
//add_library是生成库的意思,生成exe是add_execute
add_library(
AreaCaculate //库名/程序名
SHARED //共享库
${Allcpp})//放入cpp,可以使用绝对路径一个一个写
set_property(TARGET AreaCaculate PROPERTY POSITION_INDEPENDENT_CODE ON)
 
target_link_libraries(AreaCaculate liblog.so)//添加所需静态库(目标名  库名),这个是log库。可以在c++里直接写loge打印日志
  • Link_libraries和target_link_libraries的区别:target_link_libraries是在目的文件已经编译后才进行的,如果目的文件没有编译,则不进行。link_libraries则是在他下面的所有add_executable,不管是否需要链接库,都会进行,除非将其他不需要链接库的add_executable放在其前面,那就没有影响了,再者,由于某些你想要编译的文件根本不需要链接库,link_libraries这一操作算是多此一举了,另外一个区别就是,如果是多文件在CMakeLists下,target_link_libraries可以指定给哪个exe文件连接库,其实link_libraries和target_link_libraries是不同的连接方式,link_directory是设置环境变量,使用target_link_libraries时也可以不使用它,直接按照link_libararies的方式一样,给出路径就行。二者唯一区别就是一个放在add_executable前面,一个要放在后面

  • include_directories:将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径

    Cmake命令之include_directories介绍

# CMakeList.txt
cmake_minimum_required(VERSION 3.18.2)
project(include_directories_test)
include\_directories(sub) #与上个场景不同的地方在于此处,sub下存放test.h
add_executable(test main.cpp)

//main.cpp
#include "test.h"
#include <stdio.h>
int main(int argc, char **argv)
{
    printf("hello, world!\n");
    return 0;
}
  • cmake中使用条件判断(CMake菜谱)
if(USE_LIBRARY)
# add_library will create a static library
# since BUILD_SHARED_LIBS is OFF
add_library(message ${_sources})
add_executable(hello-world hello-world.cpp)
target_link_libraries(hello-world message)
else()
add_executable(hello-world hello-world.cpp ${_sources})
endif()
  • option参数:可以使用-D选项来传递参数,有点像将-DANDROID连在一起表示传参
# CMakeList.txt
option(USE_LIBRARY "Compile sources into a library" OFF)
# shell
Cd build
cmake -D USE_LIBRARY=ON ..
  • find_package:引入依赖包,分为两种模式,module模式和config模式,module默认需要Find.cmake,此文件负责查找库所在的路径,此文件在share/cmake-/Modules目录下,或指定的CMAKE_MODULE_PATH,如果失败,则转入config模式

    Cmake之深入理解find_package()的用法

4.3.编译项目完成流程

  • 此流程参考CMake菜谱,计算不同几何图像的面积
# computer_area.cpp 这里相当于main.cpp来对其他文件进行调用
#include "geometry_circle.hpp"
#include "geometry_polygon.hpp"
#include "geometry_rhombus.hpp"
#include "geometry_square.hpp"
#include <cstdlib>
#include <iostream>
int main() {
using namespace geometry;
double radius = 2.5293;
double A_circle = area::circle(radius);
std::cout << "A circle of radius " << radius << " has an area of " << A_circle
<< std::endl;
int nSides = 19;
double side = 1.29312;
double A_polygon = area::polygon(nSides, side);
std::cout << "A regular polygon of " << nSides << " sides of length " << side
<< " has an area of " << A_polygon << std::endl;
double d1 = 5.0;
double d2 = 7.8912;
double A_rhombus = area::rhombus(d1, d2);
std::cout << "A rhombus of major diagonal " << d1 << " and minor diagonal " << d2
<< " has an area of " << A_rhombus << std::endl;
double l = 10.0;
double A_square = area::square(l);
std::cout << "A square of side " << l << " has an area of " << A_square
<< std::endl;
return EXIT_SUCCESS;
}
  • 文件目录结构如下:
.
├─ CMakeLists.txt
├─ compute-areas.cpp
├─ geometry_circle.cpp
├─ geometry_circle.hpp
├─ geometry_polygon.cpp
├─ geometry_polygon.hpp
├─ geometry_rhombus.cpp
├─ geometry_rhombus.hpp
├─ geometry_square.cpp
└─ geometry_square.hpp
  • CMakeList.txt的编写
# 1.设置cmake的最低版本
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# 2.声明项目和语言CXX表示设定的编译器,如cmake -D CMAKE_CXX_COMPILER=clang++ ..或env CXX=clang++ cmake .. 命令
project(recipe-08 LANGUAGES CXX)
# 3.打印当前编译器标志
message("C++ compiler flags: ${CMAKE_CXX_FLAGS}")
# 4.为目标文件准备标志列表,其中一些无法在windows上使用
list(APPEND flags "-fPIC" "-Wall")
if(NOT WIN32)
list(APPEND flags "-Wextra" "-Wpedantic")
endif()
# 5.将要调用的文件打包成一个静态库
add_library(geometry
STATIC
geometry_circle.cpp
geometry_circle.hpp
geometry_polygon.cpp
geometry_polygon.hpp
geometry_rhombus.cpp
geometry_rhombus.hpp
geometry_square.cpp
geometry_square.hpp
)
# 6.为这个库设置编译选项
target_compile_options(geometry
PRIVATE
${flags}
)
# 7.将main函数添加成一个可执行目标文件
add_executable(compute-areas compute-areas.cpp)
# 8.为可执行目标文件设置编译选项
target_compile_options(compute-areas
PRIVATE
"-fPIC"
)
# 9.将可执行文件链接到库文件
target_link_libraries(compute-areas geometry)

4.4.使用cmake和NDK进行交叉编译

  • 示例使用cmake交叉编译paddle lite的关键点检测模型

1.编写完cpp程序和CMakeLists.txt

# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(pose-demo)
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread" )
set(PaddleLite_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../PaddleLite")
set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../OpenCV/sdk/native/jni")
find_package(OpenCV REQUIRED)
message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(${PaddleLite_DIR}/cxx/include)
find_package( OpenMP REQUIRED)
if(OPENMP_FOUND)
message("OPENMP FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
set(SRCS postprocess.cc postprocess.h Detector.cc Detector.h Detector_Kpts.cc Detector_Kpts.h Pipeline.cc Pipeline.h Utils.cc Utils.h pose_action.cc pose_action.h)
add_executable(pose-demo main.cpp ${SRCS})
target_link_libraries(
  # Specifies the target library.
  pose-demo
  ${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libpaddle_light_api_shared.so
  ${OpenCV_LIBS}
  GLESv2
  EGL
  ${log-lib}
  )
  1. 使用make.sh加编译选项进行编译
export BASE_PATH=/data/liyy/PoseDemoProject
export NDK_PATH=${BASE_PATH}/android-ndk-r15c
#export NDK_PATH=${SDK_PATH}
export cmake=${NDK_PATH}/build/cmake/cmake-3.22.1/bin/cmake
export LD_LIBRARY_PATH=${NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=/data/liyy/PoseDemoProject/pose_demo_android/app/src/main/cpp/build:${LD_LIBRARY_PATH}
mkdir build
cd build

${cmake} -DANDROID_NDK=${NDK_PATH}/build \
         -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
         -DANDROID_PLATFORM=android-21 \
     -DANDROID_TOOLCHAIN=clang \
     -DANDROID_STL=c++_shared \
     -DANDROID_ABI="arm64-v8a" ..       
#-DANDROID_ABI="armeabi-v8a with NEON" ..
make -j4

5.NDK交叉编译

4.Questions

# 查看CPU类型
adb shell getprop ro.product.cpu.abi
# 查看CPU架构
cat /proc/cpuinfo
  • 静态库和共享库的区别?

    静态库和共享库之间的区别

  • HelloWorld在android上交叉编译不通过?

    1. 删除build文件和缓存
    2. 动态库.so改为静态库,可能没有添加动态库路径到LD_LIBRARY_PATH中
pwd为当前文件夹,添加当前文件夹到LD_LIBRARY_PATH中
export LD_LIBRARY_PATH=${PWD}:${LD_LIBRARY_PATH}
  1. 使用find_package或者知道include目录可以直接使用link_libraries指令

    Ubuntu中使用cmake链接opencv库的两种方法(opencv3中 base+module动态库的名字也在这里写好了)

  2. 可以在CMakeList.txt中设置OpenCV_DIR来找到OpenCV

set(OpenCV_DIR /home/User/opencv/build/)
find_package( OpenCV REQUIRED )
include_directories(${OpenCV_INCLUDE_DIRS})
  • 找不到.so文件怎么办?cmake查找的是哪个目录?查找的是LD_LIBRARY_PATH

    cmake TARGET_LINK_LIBRARIES后找不到so的问题

  • 为什么要再add_library中使用IMPORTED?有什么作用?看不懂文档描述?

    可以增加so或者静态文件路径

  • ndk中gdb.exe调试工具被移动?找不到gdb.exe?

D:\app\android-studio\sdk\ndk\21.4.7075529\prebuilt\windows-x86_64\bin>
  • 编译完之后出现了运行错误:/usr/local/google/buildbot/src/android/ndk-r15-release/external/libcxx/../../external/libcxxabi/src/abort_message.cpp:74: void abort_message(const char *, ...): assertion "terminating with uncaught exception of type std::bad_cast: std::bad_cast" failed

    1. 在编写make.sh中,传入的ANDROID_STL参数为libc++_static或者是libc++_shared要与使用的paddle_lite的static版本或者是shared版本一致
export BASE_PATH=/data/liyy/PoseDemoProject
export NDK_PATH=${BASE_PATH}/android-ndk-r15c
#export NDK_PATH=${SDK_PATH}
export cmake=${NDK_PATH}/build/cmake/cmake-3.22.1/bin/cmake
export LD_LIBRARY_PATH=${NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=/data/liyy/PoseDemoProject/pose_demo_android/app/src/main/cpp/build:${LD_LIBRARY_PATH}

mkdir build
cd build
 
${cmake} -DANDROID_NDK=${NDK_PATH}/build \
         -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
         -DANDROID_PLATFORM=android-21 \
     -DANDROID_TOOLCHAIN=clang \
  // 这里的ANDROID\_STL要与paddle\_lite版本一致


     -DANDROID_STL=c++_shared \
         -DANDROID_ABI="armeabi-v7a with NEON" ..

make -j4
  • 出现make: *** No targets specified and no makefile found. Stop.错误

    可能是缓存问题,清除缓存

  • 增加模型推理并发能力的方法有哪些?

    1. 每隔一段时间如50ms做成batch来进行推理
    2. 使用多进程或者多线程来实例化对象,进行推理
  • 在使用四个线程进行推理时,为什么内存使用量没有增加且推理耗时增加从100ms增加到了400ms?

    请问paddle-lite android armv7的编译库 set_threads 没有速度提升?(多核cpu的 使用率一直是100%)

    1. 打印查看实例化的对象的内存地址,是否真的有实例化
    2. 打印查看每个CPU的使用情况,查看是否使用了多核CPU
    3. 查看调用的CPU是否是线程安全
    4. 查看是否限制了多核使用
  • 手机CPU中什么是大核什么是小核?

    [问答] 手机CPU的大核和小核怎么区分?

  • 怎么通过关键点检测模型得到第二种类别的关键点?

    需要重新训练模型(是在人的类别训练好的情况下再进行训练吗?还是说同时训练?),经过github提问,得到的答复是需要另外训练一个模型才行

  • shell脚本运行发生错误:/bin/bash^M: bad interpreter: No such file or directory

    shell脚本执行报错:/bin/bash^M: bad interpreter: No such file or directory

 # 因为window上编辑的为dos格式,linux上为unix格式,通过查看文件是什么格式
Vim filemain
:set ff
# 将dos格式转为unix格式
:set ff=unix
  • Paddle lite怎么进行量化?

    参考文档:

    PaddleSlim+Paddle Lite 模型量化全流程解决方案

  • 为什么在应用中访问/proc/stat会显示没有权限?cannot open /proc/stat: java.io.FileNotFoundException: /proc/stat: open failed: EACCES (Permission denied)

    android O全称为android Oreo,一般指android 8.0,即8.0之后不再被允许APP访问/proc/stat,所以需要单独写c++程序来检测每个核的使用,然后使用cmake和NDK交叉编译到android运行即可

    Android O: Permission denied on /proc entries for htop and top

    c++监控CPU核的使用代码如下:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include "stdio.h"
#include <cstring>
#include <unistd.h>

#define CPU_NUMBER 8
long mLastIdle[CPU_NUMBER + 1];
long mUsage[CPU_NUMBER + 1];
long mLastTotal[CPU_NUMBER + 1];

int splitStringToVect(const std::string & srcStr, std::vector<std::string> & destVect, const std::string & strFlag)
{
    // 从s下标为0开始查找字符串strFlag,返回起始位置,找不到返回-1
    int pos = srcStr.find(strFlag, 0);
    int startPos = 0;
    int splitN = pos;
    std::string lineText(strFlag);
 
    while (pos > -1)
    {
        lineText = srcStr.substr(startPos, splitN);
        startPos = pos + 1;
        pos = srcStr.find(strFlag, pos + 1);
        splitN = pos - startPos;
        destVect.push_back(lineText);
    }
 
    lineText = srcStr.substr(startPos, srcStr.length() - startPos);
    destVect.push_back(lineText); 
 
    return destVect.size();
}

void printCpuState()
{
    // is为文件流
    std::ifstream is("/proc/stat");
    // 使用stringstream来进行ifstream和string的转化
    std::stringstream buffer;
    buffer << is.rdbuf();
    std::string strbuff(buffer.str());
    //std::cout << "start /proc/stat" << strbuff.c_str() << std::endl;
    printf("********** start /proc/stat log\n* %s\n********** end /proc/stat log\n",strbuff.c_str());
    //LOGV("********** start /proc/stat log\n* %s\n********** end /proc/stat log",strbuff.c_str());
    
    std::vector<std::string> vec;
    std::string flag = " ";
    //vec = Tokenize(strbuff);
    int size = splitStringToVect(strbuff,vec,flag);
    //@ref http://stackoverflow.com/questions/11739444/how-to-get-usage-of-each-cpu-core-on-android
    int cntcpu = 0;
    
    for(int i = 0 ; i < vec.capacity(); i++){
        std::string str = vec[i];
        // string::npos表示不存在的位置
        if( (str.find("cpu") != std::string::npos ) )  {//if contains str

            // the columns are:
            //
            //      0 "cpu": the string "cpu" that identifies the line
            //      1 user: normal processes executing in user mode
            //      2 nice: niced processes executing in user mode
            //      3 system: processes executing in kernel mode
            //      4 idle: twiddling thumbs
            //      5 iowait: waiting for I/O to complete
            //      6 irq: servicing interrupts
            //      7 softirq: servicing softirqs
            //
            // 这里由于我的/proc/stat文件中cpu行有两个空格的情况,而vector都是按一个空格存的,所以cpu行会多出一个空格来,导致idle行后移了一位,需要单独处理
            if(str == "cpu"){
                // cpu行有两个空格导致错误
                //std::cout << "CPU SP:" << idle << std::endl;
                long idle = atol(vec[i+5].c_str());
                long total = 0;
                bool head = true;
                for(int j = 0 ; j < 8; j++){
                    total += atol(vec[i+j+1].c_str());
                }
                long diffIdle   =   idle - mLastIdle[cntcpu];
                long diffTotal  =   total - mLastTotal[cntcpu];
                int usage = (int)((float)(diffTotal - diffIdle) / diffTotal * 100);
                mUsage[cntcpu] = usage;
                mLastTotal[cntcpu] = total;
                mLastIdle[cntcpu] = idle;
                printf("CPU difTot[%d] difIdle[%d]\n",diffTotal,diffIdle);
                //LOGV("CPU difTot[%d] difIdle[%d]",diffTotal,diffIdle);

                cntcpu++;

            }else{
                long idle = atol(vec[i+4].c_str());//Long.parseLong(parts[4], 10);
                long total = 0;
                bool head = true;
                for(int j = 0 ; j < 7; j++){
                    total += atol(vec[i+j+1].c_str());
                }
                long diffIdle   =   idle - mLastIdle[cntcpu];
                long diffTotal  =   total - mLastTotal[cntcpu];
                int usage = (int)((float)(diffTotal - diffIdle) / diffTotal * 100);
                mUsage[cntcpu] = usage;
                mLastTotal[cntcpu] = total;
                mLastIdle[cntcpu] = idle;
                printf("CPU difTot[%d] difIdle[%d]\n",diffTotal,diffIdle);
                //LOGV("CPU difTot[%d] difIdle[%d]",diffTotal,diffIdle);

                cntcpu++;
            }
            
        }

    }
    for(int i = 0 ; i < CPU_NUMBER + 1; i++){
        printf("CORE[%d] tot[%d] idl[%d] use[%d]\n",i,mLastTotal[i],mLastIdle[i],mUsage[i]);
        //LOGV("CORE[%d] tot[%d] idl[%d] use[%d]",i,mLastTotal[i],mLastIdle[i],mUsage[i]);
    }
    printf("CPU Usage :Tot[%d\%] Core0[%d\%] Core1[%d\%] Core2[%d\%] Core3[%d\%] Core4[%d\%] Core5[%d\%] Core6[%d\%] Core7[%d\%]\n",
            mUsage[0],
            mUsage[1],
            mUsage[2],
            mUsage[3],
            mUsage[4],
            mUsage[5],
            mUsage[6],
            mUsage[7],
            mUsage[8]
            );

    // LOGV("CPU Usage :Tot[%d\%] Core0[%d\%] Core1[%d\%] Core2[%d\%] Core3[%d\%]",
    //         mUsage[0],
    //         mUsage[1],
    //         mUsage[2],
    //         mUsage[3],
    //         mUsage[4]
    // );
    is.close();
}

int main(){
    // 初始化时间为0
    std::cout << "run main" << std::endl;
    for(int i = 0 ; i < CPU_NUMBER + 1; i++){
        mLastIdle[i] = 0;
        mUsage[i] = 0;
        mLastTotal[i] = 0;
    }
    int loop = 100;
    while(loop){
        printCpuState();
        sleep(1);
        std::cout << "delay 1" << std::endl;
        loop--;  
    }
}

关键点检测怎么来做动作识别?

  1. 在关键点检测后可以套动作识别模型
  2. 由于动作类型比较少,可以直接通过逻辑来判断

关键点检测怎么得到运动信息?

  1. 可以使用左右脚交替来判断,得到跑步频率
  2. 得到速度需要通过场地距离,来进行判断

为什么在app中打印出来的预测耗时在100ms左右,但是自己写的c++程序跑出来的接近300ms,两者为什么会有这么大的差距?

  1. 图片大小要一致
  2. Paddle lite中有config文件,里面有优先级和线程数,调节优先级和线程数
  3. 可能有其他因素影响,在监控CPU之后,刚开始使用多线程依然会发现耗时增加问题,使用top查看CPU使用情况时,发现Logd占用CPU很高,以为是android studio问题,所以重新启动android studio,重新连接平板后,偶然测试发现耗时恢复了正常,暂时不清楚是什么情况

调节paddle lite线程数没有效果,原因是什么?

github上见回复:issue

1)这个建议先在shell下验证多线程是否生效,已排除APP壳的影响shell-demo:可以参考readme.md 文档完成模型和输入预/后处理更新进行验证

2)如果shell下多线程生效,然后可以参考paddle-lite-demo APP壳,完成你的模型和输入/输出处理更新,以验证是否有问题

3)如果在这个APP 工程没有问题,说明你参考的APP-demo 应该在多线程上处理有问题,线程数可能没有生效

7.未解决问题

  • 为什么g++ -E参数会报错?

  • 使用cv::imread出现段错误sagment fault

    单独使用imread函数时在android中可以运行,但是加入到demo的main函数时不能运行

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
//using namespace cv;

int main(int argc,char **argv){
    cv::Mat img;
    std::cout << argv[1] << std::endl;
    std::string imgPath = argv[1];
    img = cv::imread(imgPath);
    if(img.empty()){
        std::cout << "read failed!" << std::endl;
        return 1;
    }
    std::cout << "read success!" << std::endl;
    return 0;
}        

markdown笔记-c++

1.基本概念

2.基本函数

  • reinterpret_cast:顾名思义,就是把内存里的值重新解释。本质上内存里存的都是01位串,至于这些位串是什么意思全看怎么解释。举个例子,32位系统,int是32位,指针也是32位,我既可以把一个32位的值解释成一个整数,也可以解释成一个指针。至于究竟能不能这样解释,由程序员负责。而reinterpret_cast就是干这个事的

    如何理解C++中的 reinterpret_cast?

  • std::shared_ptr

  • 作用:
    a. 自动释放没有指针引用的资源
    b. 使用引用计数记录指向改资源的指针数
    c. 线程安全

  • Set::reset用法:智能指针指向新的对象

    shared_ptr的 reset用法

\# std::reset用法
#include <iostream>
#include <future>
#include <thread>

using namespace std;
class Person
{
public:
    Person(int v) {
        value = v;
        std::cout << "Cons" <<value<< std::endl;
    }
    ~Person() {
        std::cout << "Des" <<value<< std::endl;
    }

int value;

};

int main()
{
    std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用计数为1

std::shared_ptr<Person> p2 = std::make_shared<Person>(2);

p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
                            // 最后将新对象的指针交给智能指针

std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2

p1.reset();//Person(3)的引用计数为1
    p3.reset();//Person(3)的引用计数为0,析构Person(3)
    p3.reset();//再reset
    return 0;
}
  • c++读写文件
#include <fstream> // ifstream, ifstream::in
using namespace std;
int main(){
    // 1. 打开图片文件
    // 评论区的 @霍鑫网络 帮忙发现一个隐藏的bug,在此表示感谢,已经修正
    // ifstream相关资料:https://blog.csdn.net/sinat_36219858/article/details/80369255
    ifstream is("test.jpg", ifstream::in | ios::binary);
    // 2. 计算图片长度
    is.seekg(0, is.end);
    int length = is.tellg();
    is.seekg(0, is.beg);
    // 3. 创建内存缓存区
    char * buffer = new char[length];
    // 4. 读取图片
    is.read(buffer, length);
    // 到此,图片已经成功的被读取到内存(buffer)中
    delete [] buffer;
    // 感谢评论区 @geocat 帮忙发现的bug,已经修正
    is.close();
    return 0;
}

  • string和char之间的转换

c++中char 和string之间的转换方法

//这一段在cppreference里面在线编译运行的,
//用的VS2017的编译器不支持strcpy,
//而且不支持strcpy_s里面的参数类型为(char *,const char*)
//说没有匹配的重载函数
    string str = "hello";
    char *p;
    p = (char *)malloc((str.length()+1)*sizeof(char));
    strcpy(p, str.c_str());
#include "stdafx.h"
#include <string>
#include <sstream>
using namespace std;
void main()
{
    // int 转 string
    stringstream ss;
    int n = 123;
    string str;
    ss<<n;
    ss>>str;
    // string 转 int
    str = "456";
    n = atoi(str.c_str());
}

#include <iostream>
#include <vector>
#include <algorithm>
#include <thread>
using namespace std;
 
//线程要做的事情就写在这个线程函数中
void GetSumT(vector<int>::iterator first,vector<int>::iterator last,int &result)
{
    result = accumulate(first,last,0); //调用C++标准库算法
}
 
int main() //主线程
{
    int result1,result2,result3,result4,result5;
    vector<int> largeArrays;
    for(int i=0;i<100000000;i++)
    {
        if(i%2==0)
            largeArrays.push_back(i);
        else
            largeArrays.push_back(-1*i);
    }
    thread first(GetSumT,largeArrays.begin(),
        largeArrays.begin()+20000000,std::ref(result1)); //子线程1
    thread second(GetSumT,largeArrays.begin()+20000000,
        largeArrays.begin()+40000000,std::ref(result2)); //子线程2
    thread third(GetSumT,largeArrays.begin()+40000000,
        largeArrays.begin()+60000000,std::ref(result3)); //子线程3
    thread fouth(GetSumT,largeArrays.begin()+60000000,
        largeArrays.begin()+80000000,std::ref(result4)); //子线程4
    thread fifth(GetSumT,largeArrays.begin()+80000000,
        largeArrays.end(),std::ref(result5)); //子线程5
 
    first.join(); //主线程要等待子线程执行完毕
    second.join();
    third.join();
    fouth.join();
    fifth.join();
 
    int resultSum = result1+result2+result3+result4+result5; //汇总各个子线程的结果
 
    return 0;
}
#include<iostream>
#include <cstring>

void delay_msec(int msec)  
{   
    clock_t now = clock();  
    while(clock()-now < msec);  
}  

Void main(){
  std::cout << "sleep:100" << std::endl;

  delay_msec(200);        

  }


# nclude <iostream\>
# nclude <sstream\>
# nclude <string\>
using namespace std;

int main()  
{
stringstream ostr("ccc");
ostr.put('d');
ostr.put('e');
ostr<<"fg";
string gstr = ostr.str();
cout<<gstr<<endl;

char a;
ostr>>a;
cout<<a

system("pause");
}
  • Tokenize分割字符串到vector容器
 https://blog.csdn.net/weixin_33713503/article/details/93781262
#include <string>
#include <vector>
using std::string;
using std::vector;
 
int splitStringToVect(const string & srcStr, vector<string> & destVect, const string & strFlag);
 
 
int main()
{
    string str = "asdasdas \n, sadasd\n, ssdddsrr\n \n \n ss\n";
    vector<string>   destVect;
    splitStringToVect(str, destVect, "\n");     //以"\n"为标记,分割字符串到vector中
    return 1;
}
 
 
int splitStringToVect(const string & srcStr, vector<string> & destVect, const string & strFlag)
{
    int pos = srcStr.find(strFlag, 0);
    int startPos = 0;
    int splitN = pos;
    string lineText(strFlag);
 
    while (pos > -1)
    {
        lineText = srcStr.substr(startPos, splitN);
        startPos = pos + 1;
        pos = srcStr.find(strFlag, pos + 1);
        splitN = pos - startPos;
        destVect.push_back(lineText);
    }
 
    lineText = srcStr.substr(startPos, srcStr.length() - startPos);
    destVect.push_back(lineText); 
 
    return destVect.size();
}

http://www.blogjava.net/fjzag/articles/317773.html
1、  采样两个足够短的时间间隔的Cpu快照,分别记作t1,t2,其中t1、t2的结构均为:
(user、nice、system、idle、iowait、irq、softirq、stealstolen、guest)的9元组;
2、  计算总的Cpu时间片totalCpuTime
a)         把第一次的所有cpu使用情况求和,得到s1;
b)         把第二次的所有cpu使用情况求和,得到s2;
c)         s2 - s1得到这个时间间隔内的所有时间片,即totalCpuTime = j2 - j1 ;
3、计算空闲时间idle
    idle对应第四列的数据,用第二次的第四列 - 第一次的第四列即可
    idle=第二次的第四列 - 第一次的第四列
6、计算cpu使用率
    pcpu =100* (total-idle)/total
  • c++中string::find的用法
string s;
    string s1;
    cin>>s>>s1;
s.find(s1);//返回s1在s中的位置,没找到返回\-1
s.find\_first\_of(s1);//返回任意字符s1在s中第一次出现的位置,s1是字符不可以为字符串
s.find(s1, a);//从s下标为a开始查找字符串s1,返回起始位置,找不到返回\-1

原文链接:https://blog.csdn.net/qq_41444888/article/details/79601846
  • c++中string::substr用法
 1 int main()
 2 {
 3     string a;
 4     string s("123456789");
 5     
 6     a = s.substr(0,5);//拷贝字符串s中从第0位开始的长度为5的字符串
 7     cout << a << endl;//输出12345
 8     
 9     a=s.substr(); //不加参数即默认拷贝整个字符串s
10     cout<<a<<endl;//输出123456789
11     
12     a=s.substr(4);//输出56789
13     cout<<a<<endl;//单个参数则默认拷贝从第4位开始至末尾的字符串
14 }
原文链接:https://www.cnblogs.com/HOLLAY/p/11324452.html

3.Questions

如果不使用using std::string,就在程序中使用string 类型变量,程序不能识别是标准库中的string 变量。因为程序自定义头文件中也可能含有string变量。所以一定要声明using std::string。这样程序里面的string类型变量就都是std标准库中的string变量了

markdown笔记-linux

1.基本命令

  • top命令
# 查看某个进程的PID
pidof php-fpm
# top查看某个进程信息
top -p 29618
# 将top的数据每隔5秒输出到文件dir中,-b表示bash模式,-d加上数字表示输出间隔
top -b -d 5 > dir
PID:进程
PR:在android N之前代表运行在哪个核上,在android N上代表优先级,当然可能设备厂商会进行自定义
CPU%:cpu占用
S:运行状态
#THR:线程数
VSS:Virtual Set Size  虚拟耗用内存(包含共享库占用的内存)
RSS:Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PCY:调度策略优先级,SP_BACKGROUND/SP_FOREGROUND
VIRT: virtual memory usage 虚拟内存总量
RES:resident memory usage 常驻内存
SHR:shared memory 共享内存
UID:进程所有者的用户id
Thread:线程名称
Name:进程名
  • 查看CPU占用
/proc/stat

2.基本概念

3.问题

  • 为什么使用top命令查看CPU占用率时会出现超出100%的情况?

CPU为多核时会出现占用率超出100%的情况,例如8核,则最大占用率可以达到800%。使用cat /proc/cpuinfo可以查看CPU信息

cat /proc/cpuinfo 查看cpu信息

# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 
# 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数

# 查看物理CPU个数
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

# 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq

# 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l
  • 为什么使用多线程或是单线程时最大占用都是单核的100%或者是在100%上下波动?是只使用了一个单核处理器吗?那为什么不使用多核处理器?

    因为paddle lite使用的是单线程,导致只使用一个CPU进行运算

markdown笔记-paddle lite关键点检测模型量化

1.相关资源

# train数据集
http://images.cocodataset.org/zips/train2017.zip 
http://images.cocodataset.org/annotations/annotations_trainval2017.zip
# val数据集
http://images.cocodataset.org/zips/val2017.zip 
http://images.cocodataset.org/annotations/stuff_annotations_trainval2017.zip
# test数据集
http://images.cocodataset.org/zips/test2017.zip 
http://images.cocodataset.org/annotations/image_info_test2017.zip 

2.基本概念

  • mAP基本含义:mean averge precision

    目标检测中的mAP是什么含义?

    PR曲线:precision-recall曲线

    Precision: TP / (TP + FP)

    Recall:

    TP: IoU>0.5的检测框数量(同一Ground Truth只计算一次)

    FP: IoU<=0.5的检测框,或者是检测到同一个GT的多余检测框的数量

    FN: 没有检测到的GT的数量

    Ground Truth:理想的结果或是期望的结果,详解

3.量化步骤

  • 在conda环境下安装paddlelism
# 安装最新版本
pip install paddleslim -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装指定版本
pip install paddleslim==2.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 安装paddle
官网地址:https://github.com/PaddlePaddle/Paddle
# CPU
pip install paddlepaddle
pip install paddlepaddle -i https://pypi.tuna.tsinghua.edu.cn/simple
# GPU
pip install paddlepaddle-gpu
  • 下载coco数据集,使用cocoapi从coco数据集中分出100张训练图片和数据
# 1.下载coco数据集,数据集见相关资源章节
# 2.安装依赖和coco api
pip install numpy Cython matplotlab
Git clone https://github.com/cocodataset/cocoapi.git
~$ cd coco/PythonAPI
~/cocoapi$ make

未完待续~

posted @   热风丶1921  阅读(673)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
点击右上角即可分享
微信分享提示