1. 程式人生 > 其它 >android關鍵點檢測演算法效能測試

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
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>
usingnamespacestd;
voidmain()
{
//int轉string
stringstreamss;
intn=123;
stringstr;
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 -ihttps://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

未完待續~