2. 数据预处理

第二部分——数据预处理

2.1 数据预处理概述

数据预处理提供两个接口AIPP和DVPP。AIPP前文已经介绍过,这部分主要介绍DVPP(数字视觉与处理)

下文**“数字视觉与处理”**专指DVPP。

DVPP提供了五个功能模块,视频编码(VENC)、视频解码(VDEC)、JPEG图像编码(JPEGE)、JPEG图像解码(JPEGD)、视觉与处理模块(VPC)。VPC模块还提供了如格式转换,图像缩放,裁剪等其他功能

  • VPC:抠图、缩放、叠加、拼接、格式转换
  • JPEGD:解码为YUV格式
  • JPEGE:将YUV编码为.jpg
  • VDEC:视频解码
  • VENC:视频编码

与数据预处理相关的内存必须通过acldvppMalloc接口申请,通过acldvppFree接口释放。除了acldvppMalloc和acldvppFree,数字视觉预处理(DVPP)其他接口只能再HOST上调用,不能在Device上调用。

2.2 数字视觉预处理共用接口

  • 整体调用流程:创建通道描述——创建通道——创建图片描述——执行操作——销毁图片描述——创建图片描述——执行操作——销毁图片描述——销毁通道——销毁通道描述

1. dvpp内存的申请与释放

acldvppMalloc接口负责分配内存给Device侧媒体数据预处理时使用,申请后必须用acldvppFree释放(不能delete),内存地址32对齐。频繁调用acldvppMalloc与acldvppFree对,对造成性能损耗。

aclError acldvppMalloc(void **devPtr, size_t_size)
// devPtr, 输出:Device上已分配内存的指针的指针
// size, 输入: 申请内存的大小aclError acldvppFree(void *devPtr)
// devPtr, 输入:待释放内存的指针

若用户使用acldvppMalloc接口申请大块内存并自行划分、管理内存时,用户在管理内存时,需按每张图片的实际数据大小向上对齐成32整数倍+32字节(ALIGN_UP[len]+32字节)来管理内存。如管理n张图片内存,每张大小为X个字节,则实际应按 n × ( ALIGN_UP(X) + 32字节)大小管理内存。每张图片地址按 ALIGN_UP(X) + 32字节偏移。

  • ALIGN_UP = ((len - 1)/32 + 1) × 32

2. 通道&通道描述创建与销毁

创建通道描述 -> 创建通道 -> 销毁通道 -> 销毁通道描述信息

// 通道描述创建与销毁
acldvppChannelDesc *acldvppCreateChannelDesc()
// 函数功能:创建acldvppChannelDesc类型数据,表示创建图片数据处理通道时的通道描述信息,为同步接口
// 返回acldvppChannelDesc类型表示成功;失败返回 nullaclError acldvppDestroyChannelDesc(acldvppChannelDesc *channelDesc)
// 必须调用acldvppDestroyChannel(销毁通道)后,再调用acldvppDestroyChannelDesc(销毁通道信息),否则报错
// channelDesc输入// ”通道“创建与销毁
aclError acldvppCreateChannel(acldvppChannelDesc* channelDesc)
// 函数功能:创建通道,同一通道可以反复使用,销毁后不可再使用
//  约束:通道为非线程安全,即不同线程要求创建不同通道
// channelDesc,输入与输出:指定通道描述信息aclError acldvppDestroyChannel(acldvppChannel *channel)

3. 创建与销毁描述图片描述信息

acldvppPicDesc* acldvppCreatePicDesc()
// 创建图片描述信息,同步接口
// 返回acldvppPicDesc类型数据表示成功,返回null则失败aclError acldvppDestroyPicDesc(acldvppPicDesc* picDesc)
// 销毁由acldvppCreatePicDesc创建的图片描述信息

4. acldvppSetPicDesc系列接口——设置图片描述


2.3 JPEG解码

  • 像素对齐
    针对不同的编码格式,解码后输出不同格式的图片,如YUV444SP,YUV422SP,YUV420SP。解码对输出图片有对齐要求:宽128,高16.即,假如原始输出宽高为(129,17)应对齐为(256,32)

2.3.1 解码约束

  • 输入约束:

    • 最大分辨率:8192×8192, 最小分辨率:32×32
    • 只支持哈夫曼编码,码流色域为YUV,下采样码流为444,422,420,400
    • 不支持渐进JPEG
    • 不支持JPEG2000
  • 输出要求:
    • 宽对齐128,高对齐16(像素对齐)
  • 内存大小:内存大小与输出图片数据格式相关
    • YUV420SP: widthStride × heightStride × 3 / 2
    • YUV422SP: widthStride × heightStride × 2
    • YUV444SP: widthStride × heightStride × 3

DVPP对输出的图片宽高有对齐要求:宽128,高16(指像素对齐而不是内存对齐)

2.3.2 解码流程图

2.3.3 图片解码接口

1. 解码接口

aclError acldvppJpegDecodeAsync(acldvppChannelDesc *channelDesc, // 输入、通道描述,与调用acldvppCreateChannel时传入的channelDesc一致const void *data,  // 输入图片的内存地址uint32_t size, // 输入图片的实际数据大小,单位ByteacldvppPicDesc* outputDesc, // 输出图片信息。aclrtStream stream // 指定此接口绑定的stream
)// 关于outputDesc
// 1. 调用acldvppCreatePicDesc接口,创建图片描述信息
// 2. JPEG原图的高宽,可以通过acldvppJpegGetImageInfo获取
// 3. 输出图片的内存大小可提前调用acldvppJpegPredictDecSize接口获取
// 4. 输出图片格式支持设置成jpeg原图编码格式或NV12或NV21格式

2. 读取图像宽高及通道数

aclError acldvppJpegGetImageInfo(const void* data, // 图像内存地址uint32_t size,  // 实际数据大小,单位Byteuint32_t *width, uint32_t *height,int32_t *components // 通道个数
)

3. 预测输出内存

自动计算输出占用内存

aclError acldvppJpegPredictDecSize(const void* data, // 图像内存地址uint32_t size,  // 实际数据大小,单位ByteacldvppPixelFormat outputPixelFormat,  // 解码后输出图片的格式uint32_t *decsize // 返回JPEG图片解码后所需输出内存的大小
)

2.3.4 图像解码Demo

头文件 #include "acl/ops/acl_dvpp.h"

1. jpegd.h

# pragma once
# include "acl/acl.h"
# include <iostream>
# include <cstdint>
// DVPP头文件
# include "acl/ops/acl_dvpp.h"# define INFO_LOG(fmt, args...) fprintf(stdout, "[INFO]" fmt "\n", ##args)
# define WARN_LOG(fmt, args...) fprintf(stdout, "[WARN] " fmt "\n", ##args)
# define ERROR_LOG(fmt, args...) fprintf(stdout, "[ERROR] " fmt "\n", ##args)typedef enum Result {SUCCESS = 0 ,FAILED = 1
}Result;typedef struct PicDesc {std::string picName;int width;int height;
}PicDesc;// 根据图片描述信息获取Device内存
static void* GetDeviceBufferOfPicture(const PicDesc &picDesc, uint32_t &devPicBufferSize);
// 从硬盘取文件
static char* ReadBinFile(std:: string fileName, uint32_t &fileSize);
// 保存dvpp输出结果至文件
static Result SaveDvppOutputData(const char *fileName,const void *devPtr, uint32_t datasize);
// 设置输入
void SetInput(void *inDevBuffer, int inDevBufferSize, int inputWidth, int inputHeight);
// 获取输出
void GetOutput(void **outputBuffer, int &outputSize);void DestroyResource();
void DestroyDecodeResource();int32_t deviceId_ = 0;
aclrtContext context_= nullptr;
aclrtStream stream_=nullptr;
acldvppChannelDesc *dvppChannelDesc_; // 频道描述信息void* decodeOutDevBuffer_; // 解码输出内存地址
acldvppPicDesc *decodeOutputDesc_; // 解码输出描述信息
uint32_t decodeDataSize_; // 解码输出内存大小void *inDevBuffer_; // 解码输入内存地址
uint32_t inDevBuffersize_; // 解码输入内存大小
uint32_t inputwidth_;
uint32_t inputHeight_;

2. jpegd.cpp

# include "acl/acl.h"
# include <iostream>
# include <jpegd.h>
# include <fstream>
# include <cstring>
# include <sys/stat.h>
# include "acl/ops/acl_dvpp.h"
using namespace std;void CreateResource(); // 申请运行管理资源
void CreateDecodeResource(); // 创建图像数据处理通道int main(){// ACL初始化const char* aclConfigPath = "../src/acl.json";aclInit(aclConfigPath);INFO_LOG("acl init success");// 申请运行管理资源CreateResource();// 创建图像数据处理通道CreateDecodeResource();// 预置的图片数据std::string dvppOutputfileName = "./dvpp_output_";PicDesc testPic[]= {{"../data/dog1_1024_683.jpg", 1024, 683} ,{"../data/dog2_1024_683.jpg", 1024, 683} ,};// 开始循环处理图片for (size_t index = 0; index < sizeof(testPic) / sizeof(testPic[0]);++index) {INFO_LOG ("start to process picture:%s",testPic[index], picName.c_str());// 将图片读入内存并复制到dvppMalloc申请的内存中uint32_t devPicBufferSize;void *picDevBuffer = GetDeviceBufferOfPicture(testPic[index], devPicBufferSize);if (picDevBuffer == nullptr){ERROR_LOG ("get pic device buffer failed , index is 8zu", index);return FAILED ;}SetInput(picDevBuffer, devPicBufferSize, testPic[index].width, testPic[index].height);// 计算解码输出图片对齐后的宽高uint32_t decodeOutWidthStride = (inputWidth_ + 127) / 128 * 128;uint32_t decodeOutHeightStride = (inputHeight_ + 15) / 16 * 16;// 申请解码输出内存aclError ret = acldvppMalloc(&decodeOutDevBuffer_, decodeDataSize_);  // 按计算的内存大小申请空间if (ret != ACL_ERROR_NONE) {ERROR_LOG("acldvppMalloc jpegOutBufferDev failed, ret = %d", ret);return FAILED;}// 创建解码输出图片描述信息decodeOutputDesc_ = acldvppCreatePicDesc();if (decodeOutputDesc_ != ACL_ERROR_NONE) {ERROR_LOG("acldvppCreatePicDesc decodeOutputDesc failed");return FAILED;}// 添加详细的图片描述信息acldvppSetPicDescData(decodeOutputDesc_, decodeOutDevBuffer_); // 解码图片内存地址acldvppSetPicDescFormat(decodeOutputDesc_,PIXEL_FORMAT_YUV_SEMIPLANAR_420); // 色域acldvppSetPicDescWidth(decodeOutputDesc_, inputWidth_); // 输入图片宽acldvppSetPicDescHeight(decodeOutputDesc_, inputHeight_);acldvppSetPicDeseWidthStride(decodeOutputDesc_, decodeOutWidthStride); // 输出宽对齐acldvppSetPicDescHeightStride(decodeOutputDesc_, decodeOutHeightStride); // 输出高对齐acldvppSetPicDescSize(decodeOutputDese_, decodeDataSize_); // 数据大小/* ========================= 数据准备完毕 ================================== */// 执行解码动作ret = acldvppJpegDecodeAsync(dvppChannelDesc_, inDevBuffer_, inDevBufferSize_, decodeOutputDesc_, stream_);if (ret != ACL_ERROR_NONE){ERROR_LOG("acldvppJpegDecodeAsync failed, ret=%d", ret);return FAILED;}// 同步等待//       使得程序阻塞在此,直到上一步执行解码动作完成ret = aclrtSynchronizeStream(stream_);if (ret != ACL_ERROR_NONE){ERROR_LOG("aclrtSynchronizeStream failed, ret=%d", ret);return FAILED;}// 解码结束后  释放输入数据内存(void)acldvppFree(picDevBuffer);picDevBuffer = nullptr;// 解码数据写入磁盘void *dvppOutputBuffer = nullptr;int dvppOutputSize;GetOutput(&dvppOutputBuffer, dvppOutputSize);std::string dvppOutputfileNameCur = dvppOutputfileName + std::to_string(index);ret = SaveDvppOutputData(dvppOutputfileNameCur.c_str(), dvppOutputBuffer, dvppOutputSize);if (ret != SUCCESS){ERROR_LOG("save dvpp output data failed");}// 销毁本张图片描述信息acldvppDestroyPicDesc(decodeOutputDesc_);}// 销毁解码channelDestroyDecodeResource();// 销毁运行资源DestroyResource();INFO_LOG("execute sample sucecss");return SUCCESS;
}
// 函数定义void CreatResource(){aclrtSetDevice(deviceId_);INFO_LOG("open device %d success", deviceId_);aclrtCreateContext(&context_, deviceId_);INFO_LOG("open context success");aclrtCreateStream(&stream_);INFO_LOG("open stream success");
}void CreateDecodeResource(){dvppChannelDesc_ = acldvppCreateChannelDesc();acldvppCreateChannel(dvppChannelDesc_); // Desc里就有了一个创建好的Channel了INFO_LOG("dvpp init resource success");
}char* ReadBinFile(std::string fileName, uint32_t &fileSize){struct stat sBuf;int fileStatus = stat(fileName.data(), &sBuf);if (fileStatus == -1) {ERROR_LOG("failed to get file");return nullptr;}if (S_ISREG(sBuf.st_mode) == 0) {ERROR_LOG("%s is not a file, please enter a file", fileName.c_str());return nullptr;}std::ifstream binFile(fileName, std::ifstream::binary);if (binFile.is_open() == false) {ERROR_LOG("open file %s failed", fileName.c_str());return nullptr;}binFile.seekg(0, binFile.end);uint32_t binFileBufferLen = binFile.tellg();if (binFileBufferLen == 0) {ERROR_LOG("binfile is empty, filename is %s", fileName.c_str());binFile.close();return nullptr;}binFile.seekg(0, binFile.beg);void* binFileBufferData = nullptr;aclError ret = ACL_ERROR_NONE;if (!g_isDevice) {ret = aclrtMallocHost(&binFileBufferData, binFileBufferLen);if (binFileBufferData == nullptr) {ERROR_LOG("malloc binFileBufferData failed");binFile.close();return nullptr;}} else {ret = aclrtMalloc(&binFileBufferData, binFileBufferLen, ACL_MEM_MALLOC_NORMAL_ONLY);if (ret != ACL_ERROR_NONE) {ERROR_LOG("malloc device buffer failed. size is %u", binFileBufferLen);binFile.close();return nullptr;}}binFile.read(static_cast<char *>(binFileBufferData), binFileBufferLen);binFile.close();fileSize = binFileBufferLen;return binFileBufferData;
}void* GetDeviceBufferOfPicture(const PicDesc &picDesc, uint32_t &devPicBufferSize){if (picDesc.picName.empty(){ERROR_LOG ( "picture file name is empty") ;return nullptr ;}// 从硬盘中读取uint32_t inputHostBuffSize = 0 ;// 将输入图片内存返回char* inputHostBuff = ReadBinFile(picDesc.picName, inputHostBuffSize);if (inputHostBuff == nullptr){return nullptr;}void *inBufferDev = nullptr ;uint32_t inBufferSize = inputHostBuffSize;aclError ret = acldvppMalloc(&inBufferDev, inBufferSize); // 申请DVPP内存,用于存放输入JPEG码流if (ret != ACL_ERROR_NONE) {ERROR_LOG("malloc device buffer failed. size is %u", inBufferSize);delete[] inputHostBuff;return nullptr;}// 将输入数据复制到DVPP申请的内存中ret = aclrtMemcpy(inBufferDev,inBufferSize,inputHostBuff,inputHostBuffSize, ACL_MEMCPY_HOST_TO_DEVICE);if (ret!=ACL_ERROR_NONE){ERROR_LOG ("memcpy failed, device buffer size is %u, input host buffer size is u",  inBufferSize, inputHostBuffSize) ;acldvppFree (inBufferDev);delete[] inputHostBuff;return nullptr ;}// 用DVPP自带接口计算解码后数据内存大小ret = acldvppJpegPredictDecSize(inputHostBuff, inputHostBuffSize, PIXEL_FORMAT_YUV_SEMIPLANAR_420, &decodeDataSize_);if (ret != ACL_ERROR_NONE){ERROR_LOG("acldvppJpegPredictDecSize failed, ret = %d", ret);}// 计算H, W, Cuint32_t picWidth=0;uint32_t picHeight=0;int32_t picComponents=0;ret = acldvppJpegGetImageInfo(inputHostBuff, inputHostBuffSize, &picWidth, &picHeight, &picComponents);if (ret != ACL_ERROR_NONE){ERROR_LOG("acldvppJpegGetImageInfo failed, ret=%d", ret);}else{INFO_LOG("Picture: %s, widthL %d, heights: %d, channel: %d", picDesc.picName.c_str(), picWidth, picHeight, picComponents);}delete[] inputHostBuff;devPicBufferSize = inBufferSize;return inBufferDev;
}// 将准备好的输入传递给内置对象(头文件中定义的)
void SetInput(void *inDevBuffer, int inDevBufferSize, int inputWidth, int inputHeight){inDevBuffer_ = inDevBuffer;inDevBufferSize_ = inDevBufferSize;inputWidth_ = inputWidth;inputHeight_ = inputHeight;
}void GetOutput(void **outputBuffer, int &outputSize){*outputBuffer = decodeOutDevBuffer_;outputSize = decodeDataSize_;decodeOutDevBuffer_ = nullptr;decodeDataSize_ = 0;
}// 写入硬盘
Result SaveDvppOutputData(const char *fileName, const void *devPtr, uint32_t dataSize){// 将输出数据从Device拷到Hostvoid *hostPtr = nullptr;aclError aclRet = aclrtMal1ocHost(&hostPtr, dataSize);if (aclRet != ACL_ERROR_NONE){ERROR_LOG("malloc host data buffer failed,aclRet is %d", aclRet);return FAILED;}aclRet = aclrtMemcpy(hostPtr, dataSize, devPtr, dataSize, ACL_MEMCPY_DEVICE_TO_HOST);if (aclRet != ACL_ERROR_NONE){ERROR_LOG("dvpp output memcpy to host failed, aclRet is %d", aclRet);(void)aclrtFreeHost(hostPtr);return FAILED;}// 打开文件指针FILE *outFileFp = fopen(fileName, "wb+");if (nullptr == outFi1eFp){ERROR_LOG("fopen out file %s failed. ", fileName);(void) ac1rtFreeHost(hostPtr) ;return FAILED;}// 内存写入size_t writeSize = fwrite(hostPtr, sizeof(char),dataSize,outFileFp);if(writeSize != dataSize){ERROR_LOG("need write %u bytes to %s,but only write 号zu bytes. ",dataSize, fileName, writeSize);(void)aclrtFreeHost(hostPtr);return FAILED ;}(void)aclrtFreeHost(hostPtr);fflush(outFileFp);fclose(outFileFp);return SUCCESS;
}void DestroyDecodeResource()
{if (decodeOutputDesc_ != nullptr){acldvppDestroyChannel(dvppChannelDesc_);    // 销毁channelacldvppDestroyChannelDesc(dvppChannelDesc_); // 销毁descdecodeOutputDesc_ = nullptr;}
}void DestroyResource (){aclError ret;if (stream_ != nullptr) {ret =aclrtDestroyStream(stream_);if (ret != ACL_ERROR_NONE) {ERROR_LOG("destroy stream failed");}stream_ = nullptr;}INFO_LOG("end to destroy stream");if (context_ != nullptr){ret =aclrtDestroyContext(context_) ;if (ret != ACL_ERROR_NONE){ERROR_LOG("destroy context failed");}context_ = nullptr;}INFO_LOG("end to destroy context");ret = aclrtResetDevice(deviceId_);if (ret !=ACL_ERROR_NONE){ERROR_LOG("reset device failed");}INFO_LOG("end to reset device is %d", deviceId_);ret = aclFinalize();if (ret !=ACL_ERROR_NONE){ERROR_LOG("finalize acl failed");}INFO_LOG("end to finalize acl");
}

3. 编译运行

使用dvpp时CMakeList有些不一样

# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.
# CMake lowest version requirement
cmake_minimum_required(VERSION 3.5.1)# project information
project(ACL_HELLO_WORLD)# Compile options
add_compile_options(-std=c++11)add_definitions(-DENABLE_DVPP_INTERFACE)# 指定生成路径
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY  "../out")
set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall")set(INC_PATH $ENV{DDK_PATH})if (NOT DEFINED ENV{DDK_PATH})if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows")set(INC_PATH "C:/Program Files/HuaWei/Ascend")else ()set(INC_PATH "/usr/local/Ascend")endif ()message(STATUS "set default INC_PATH: ${INC_PATH}")
else ()message(STATUS "env INC_PATH: ${INC_PATH}")
endif ()set(LIB_PATH $ENV{NPU_HOST_LIB})# Dynamic libraries in the stub directory can only be used for compilation
if (NOT DEFINED ENV{NPU_HOST_LIB})if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows")set(LIB_PATH "C:/Program Files/HuaWei/Ascend/Acllib/lib64")else ()set(LIB_PATH "/usr/local/Ascend/acllib/lib64/stub/")endif ()message(STATUS "set default LIB_PATH: ${LIB_PATH}")
else ()message(STATUS "env LIB_PATH: ${LIB_PATH}")
endif ()# Header path
# 引用除acl.h 需要在此添加头文件
include_directories(${INC_PATH}/acllib/include/)if(target STREQUAL "Simulator_Function")add_compile_options(-DFUNC_SIM)
endif()# add host lib path
link_directories(${LIB_PATH}
)# 可能变化的
# 想要编译的源文件
add_executable(jpegd  # 指生成的可执行文件jpged.cpp)  # 指待编译的源文件target_link_libraries(jpegdascendcl acl_cblas acl_dvpp stdc++  )install(TARGETS main DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

cmake -> make -> touch -> main

2.4 抠图和缩放

由VPC模块实现功能。可以实现抠图、缩放、叠加和拼接功能。

  • VPC要求输入宽高对齐


YUV420SP宽高要求对齐至16。

2.4.1 缩放接口

acldvppResizeConfig *acldvppCreateResizeConfig()创建图片缩放配置数据。
acldvppDestroyResizeConfig(acldvppResizeConfig *resizeConfig):销毁缩放配置

华为CANN训练营笔记——应用开发全流程 [5](with 代码版)相关推荐

  1. 一文解锁华为云新技能-AIOT开发全流程【设备接入-ESP端侧数据收集[MQTT]-实时数据分析】(步步截图较详细)

    一文解锁华为云新技能-AIOT开发全流程[设备接入-ESP端侧数据收集[MQTT]-实时数据分析](步步截图较详细) 在这篇你将会接触到:从物联网工程师从硬件层-通信层到应用层全流程:开发华为云最基本 ...

  2. MindSpore框架TBE算子开发全流程

    本文为MindSpore框架TBE算子开发全流程的图文案例.         视频案例请移步MindsSpore框架TBE算子开发全流程 MindSpore框架TBE算子开发全流程 一.工具介绍 1. ...

  3. 【AI Studio】飞桨图像分类零基础训练营 - 0456 - 图像分类竞赛全流程实战

    前言:因为两课讲的一个比赛内容(课程里也没分页),所以我把笔记也合在一起.而且也是因为老师讲得很飘,所以我感觉我想记的东西估计不多吧.因为大部分都是新概念,所以我自己也没什么全新的理解,基本都是复制粘 ...

  4. 深度学习大厂前端项目开发全流程全流程

    用户审美的要求越来越高,也就使得越来越多的公司和企业注重线上用户的体验,都想要向外界传达出众的气质形象和重要信息,所以,Web前端人员的需求也越来越大. 深度学习大厂项目开发全流程全流程 在国外企业, ...

  5. 次世代游戏美术资源开发全流程及常用的软件

    次世代游戏美术资源开发全流程案例 外国艺术家FlyCat用Blender完成的次世代悟空(胸像)全流程 外国艺术家FlyCat用Blender完成的次世代悟空(胸像)全流程 流程图

  6. 智能门锁开发全流程分享

    本文分享自中移OneOS微信公众号<智能门锁开发全流程>,作者:小O. <<<Python是计算机中一门公认的简单并且容易学习的语言,它的语法简单友好,拥有丰富的库资源和 ...

  7. RPD快速产品开发全流程详解

    一.什么是RPD? RPD定义-Rapid Product Development(快速产品开发): 借鉴了业界主流的产品开发流程:IPD.敏捷开发等: 结合企业当前软硬件开发实践,RPD是包括了思想 ...

  8. 【微信小程序开发全流程】篇章0:基于JavaScript开发的校园综合类微信小程序的概览

    基于JavaScript开发的校园综合类微信小程序的概览 本文仅供学习,未经同意请勿转载 一些说明:上述项目来源于笔者我本科大三阶段2020年电子设计课程项目,在这个项目中,我主要是负责的部分有前端, ...

  9. 菜鸟看的Android应用开发全流程

    给菜鸟看的Android应用开发全流程--好多Android开发中,没人告诉过你的事 很多菜鸟开始学习Android开发,去网上搜集过很多"Android开发教程",但是搜索出来的 ...

  10. 国内征信行业模型开发全流程详解

    1. 前言 目前国内的金融体系主要由银行.互联网消费金融.助贷机构组成,本人参与过国内外大型银行.消金.助贷机构的征信模型开发,相对而言,对当前国内的征信模型具有一定的发言权.下面,我将从技术角度全面 ...

最新文章

  1. poj 3321 Apple Trie
  2. 【控制】李亚普诺夫稳定性分析
  3. android JNI层线程回调Java函数
  4. ?: (staticfiles.E002) The STATICFILES_DIRS setting should not contain the STATIC_ROOT setting.
  5. OAuth1.0介绍
  6. 写一个Windows上的守护进程(4)日志其余
  7. RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds
  8. 实现JNI的另一种方法:使用RegisterNatives方法传递和使用Java自定义类 (转)
  9. Hive去重最佳方法
  10. SAP与GE开展工业物联网合作
  11. C语言main函数带参数在VC6下的调试方法
  12. 人脸对齐:Procrustes analysis 普氏分析
  13. 【C++】:动态库与静态库区别
  14. 任正非:华为不会让CFO接班 干部选拔以李云龙为标杆
  15. 基于arduino的ESP32 学习笔记(一) 基于ESP32的智能花盆
  16. 网站图片怎么优化搜索排名
  17. 只需四步,让你成为朋友圈的焦点----Python制作微信好友头像墙
  18. 如何将PDF压缩突破限制大小
  19. Bean 的生命周期
  20. Android 启动速度优化

热门文章

  1. 3D次时代来临 如何玩转红蓝立体游戏
  2. Wireshark 设置中文
  3. 怎么把PDF图片转换成PPT
  4. 计算机蓝牙快捷键,电脑蓝牙在哪里开?蓝牙快捷方式介绍
  5. SSL证书有什么用? SSL证书错误怎么办?
  6. 【打卡】汽车领域多语种迁移学习挑战赛
  7. Cocos2d+protobuf仿JJ斗地主源码,win32和Android编译通过
  8. Java教学视频全集,活见久
  9. 类的加载、连接和初始化
  10. 图片裁切器Cropper.js的使用