MACE(3)-----工程化
作者:十岁的小男孩
QQ:929994365
能下者,上。
前言
本文是MACE的第三步即MACE环境编译出来的库在Android工程中的使用。在第一篇博文中通过mace官方提供的安卓工程进行调试,本文将其精简,只关心其数据流的逻辑过程。该工程功能是mace的demo物体识别,即传入一张图片,模型识别预测将结果在桌面显示。本人萌新一枚,学习安卓有一月多了,工程漏洞百出,望相互学习。下一篇博文会对mace做一个全面的总结。本部分的学习需要掌握JNI/NDK技术,若有问题浏览前面文章。
MACE(1)-----环境搭建:https://www.cnblogs.com/missidiot/p/9480033.html
MACE(2)-----模型编译:https://www.cnblogs.com/missidiot/p/9509831.html
JNI/NDK:https://www.cnblogs.com/missidiot/p/9716902.html
经过mace环境编译出文件如下:
此处应该有图!!!
给Android studio配置NDK版本,其版本与编译版本应一致,本文是r-16b。
新建工程,勾选c++支持选项框。
其c++标准库选择c++11。
以上工程构建完毕。
Android studio2.2版本以上采用的是CMake编译,加载库在CMakeLists.txt文件中配置。在MainActivity中加载库,如下:
库的名称自己更改:比如本文改为"mace_jni",相对应的在CMakeLists.txt文件中修改名称和创建对应的cpp文件,即mace_jni.cpp,如下图:
在MainActivity中加载库;
static {
System.loadLibrary("mace_jni");
}
在CMakeLists.txt文件中修改;(刚开始文件如下)
cmake_minimum_required(VERSION 3.4.1) add_library(
mace_jni SHARED src/main/cpp/mace_jni.cpp ) find_library(
log-lib log ) target_link_libraries(
mace_jni ${log-lib} )
CMakeLists最终文件如下:(加载编译出的a库)
cmake_minimum_required(VERSION 3.4.1) #set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../app/libs/${ANDROID_ABI}) # 这块路径有点问题
include_directories(${CMAKE_SOURCE_DIR}/)
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/public) # libmace.a是mace编译生成的,这块以后要改的在这块
set(mace_lib ${CMAKE_SOURCE_DIR}/src/main/cpp/lib/arm64-v8a/libmace.a)
# set(mace_lib ${CMAKE_SOURCE_DIR}/src/main/cpp/lib/armeabi-v7a/libmace.a) # mobilneet.a也是mace环境编译生成的,后期如果生成的话要改在这里
set(mobilenet_lib ${CMAKE_SOURCE_DIR}/src/main/cpp/model/arm64-v8a/mobilenet.a)
# set(mobilenet_lib ${CMAKE_SOURCE_DIR}/src/main/cpp/model/armeabi-v7a/mobilenet.a) add_library (mace_lib STATIC IMPORTED)
set_target_properties(mace_lib PROPERTIES IMPORTED_LOCATION ${mace_lib}) add_library (mobilenet_lib STATIC IMPORTED)
set_target_properties(mobilenet_lib PROPERTIES IMPORTED_LOCATION ${mobilenet_lib}) add_library( # Sets the name of the library.
mace_jni SHARED src/main/cpp/mace_jni.cpp )
find_library(
log-lib
log ) target_link_libraries( # Specifies the target library.
mace_jni
mace_lib
mobilenet_lib
${log-lib} )
创建mace_jni.cpp文件,实现native方法。
在java包下新建JniUtils.java,将native方法的声明放在其中,当然也将MainActivity中的加载库代码剪切放在JniUtils文件中。并声明三个方法,会自动在mace_jni.cpp下自动被声明。新建方法会显示红色报错,在AS中快捷键是alt+enter创建方法在mace_jni.cpp文件中。
package com.tcl.weilong.mace; public class JniUtils {
static {
System.loadLibrary("mace_jni");
} /**
* 给模型设置参数
* @param ompNumThreads
* @param cpuAffinityPolicy
* @param gpuPerfHint
* @param gpuPriorityHint
* @param kernelPath
* @return
*/ public native int maceMobilenetSetAttrs(int ompNumThreads, int cpuAffinityPolicy, int gpuPerfHint, int gpuPriorityHint, String kernelPath);
/**
* 给模型创建运行环境
* @param model
* @param device
* @return
*/
public native int maceMobilenetCreateEngine(String model, String device); /**
* 模型具体核心功能,识别图片
* @param input
* @return
*/
public native float[] maceMobilenetClassify(float[] input);
}
在mace_jni.cpp文件中自动生成对声明方法的实现声明:
在build.gradle文件中更改为如下代码:
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -fopenmp"
abiFilters "arm64-v8a"
}
}
将mace附带的example工程中的lib,model,public三个文件拷贝到cpp文件夹下面并在加载时候修改路径,按如下目录结构。
最为该工程核心的是在java声明的native方法在底层c/c++中具体实现,以下代码是以上三个方法在mace_jni.cpp文件中的具体实现:
#include <jni.h>
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <numeric> #include "public/mace.h"
#include "public/mace_runtime.h"
#include "public/mace_engine_factory.h" namespace { struct ModelInfo {
std::string input_name;
std::string output_name;
std::vector<int64_t> input_shape;
std::vector<int64_t> output_shape;
};
//有的地方叫.cc也可以叫.cpp其实一个意思,实质为区别c文件的
struct MaceContext {
std::shared_ptr<mace::MaceEngine> engine;
std::shared_ptr<mace::KVStorageFactory> storage_factory;
std::string model_name;
mace::DeviceType device_type = mace::DeviceType::CPU;
//模型的输入输出在这里改
std::map<std::string, ModelInfo> model_infos = {
{"mobilenet_v1", {"input", "MobilenetV1/Predictions/Reshape_1",
{1, 224, 224, 3}, {1, 1001}}},
{"mobilenet_v2", {"input", "MobilenetV2/Predictions/Reshape_1",
{1, 224, 224, 3}, {1, 1001}}}
};
}; mace::DeviceType ParseDeviceType(const std::string &device) {
if (device.compare("CPU") == 0) {
return mace::DeviceType::CPU;
} else if (device.compare("GPU") == 0) {
return mace::DeviceType::GPU;
} else if (device.compare("HEXAGON") == 0) {
return mace::DeviceType::HEXAGON;
} else {
return mace::DeviceType::CPU; //默认是返回CPU
}
} MaceContext& GetMaceContext() {
static auto *mace_context = new MaceContext; return *mace_context;
} } //namcspace
/**
* java+包名+类名+函数名
* Java+com.tcl.weilong.mace+JniUtils+maceMobilenetSetAttrs
*/
extern "C"
jint Java_com_tcl_weilong_mace_JniUtils_maceMobilenetSetAttrs(JNIEnv *env, jobject instance,
jint ompNumThreads, jint cpuAffinityPolicy,
jint gpuPerfHint, jint gpuPriorityHint,
jstring kernelPath_) {
MaceContext &mace_context = GetMaceContext();
mace::MaceStatus status;
// openmp ??
status = mace::SetOpenMPThreadPolicy(
ompNumThreads,
static_cast<mace::CPUAffinityPolicy>(cpuAffinityPolicy));
// gpu
mace::SetGPUHints(
static_cast<mace::GPUPerfHint>(gpuPerfHint),
static_cast<mace::GPUPriorityHint>(gpuPriorityHint));
// opencl cache
const char *kernel_path_ptr = env->GetStringUTFChars(kernelPath_, nullptr);
if (kernel_path_ptr == nullptr) return JNI_ERR;
const std::string kernel_file_path(kernel_path_ptr);
mace_context.storage_factory.reset(
new mace::FileStorageFactory(kernel_file_path));
mace::SetKVStorageFactory(mace_context.storage_factory);
env->ReleaseStringUTFChars(kernelPath_, kernel_path_ptr);
return JNI_OK;
}
extern "C"
jint Java_com_tcl_weilong_mace_JniUtils_maceMobilenetCreateEngine(JNIEnv *env, jobject instance,
jstring model_, jstring device_) {
MaceContext &mace_context = GetMaceContext();
// parse model name
const char *model_name_ptr = env->GetStringUTFChars(model_, nullptr);
if (model_name_ptr == nullptr) return JNI_ERR;
mace_context.model_name.assign(model_name_ptr);
env->ReleaseStringUTFChars(model_, model_name_ptr); // load model input and output name
// auto model_info_iter = mace_context.model_infos.find(mace_context.model_name);
auto model_info_iter = mace_context.model_infos.find(mace_context.model_name);
if (model_info_iter == mace_context.model_infos.end()) { return JNI_ERR;
}
std::vector<std::string> input_names = {model_info_iter->second.input_name}; std::vector<std::string> output_names = {model_info_iter->second.output_name}; // get device
const char *device_ptr = env->GetStringUTFChars(device_, nullptr);
if (device_ptr == nullptr) return JNI_ERR;
mace_context.device_type = ParseDeviceType(device_ptr);
env->ReleaseStringUTFChars(device_, device_ptr); mace::MaceStatus create_engine_status =
CreateMaceEngineFromCode(mace_context.model_name,
std::string(),
input_names,
output_names,
mace_context.device_type,
&mace_context.engine); return create_engine_status == mace::MaceStatus::MACE_SUCCESS ?
JNI_OK : JNI_ERR;
}extern "C"
jfloatArray Java_com_tcl_weilong_mace_JniUtils_maceMobilenetClassify(JNIEnv *env, jobject instance,
jfloatArray input_) {
MaceContext &mace_context = GetMaceContext();
// prepare input and output
auto model_info_iter =
mace_context.model_infos.find(mace_context.model_name);
if (model_info_iter == mace_context.model_infos.end()) { return nullptr;
}
const ModelInfo &model_info = model_info_iter->second;
const std::string &input_name = model_info.input_name;
const std::string &output_name = model_info.output_name;
const std::vector<int64_t> &input_shape = model_info.input_shape;
const std::vector<int64_t> &output_shape = model_info.output_shape;
const int64_t input_size =
std::accumulate(input_shape.begin(), input_shape.end(), 1,
std::multiplies<int64_t>());
const int64_t output_size =
std::accumulate(output_shape.begin(), output_shape.end(), 1,
std::multiplies<int64_t>()); // load input
jfloat *input_data_ptr = env->GetFloatArrayElements(input_, nullptr);
if (input_data_ptr == nullptr) return nullptr;
jsize length = env->GetArrayLength(input_);
if (length != input_size) return nullptr; std::map<std::string, mace::MaceTensor> inputs;
std::map<std::string, mace::MaceTensor> outputs;
// construct input
auto buffer_in = std::shared_ptr<float>(new float[input_size],
std::default_delete<float[]>());
std::copy_n(input_data_ptr, input_size, buffer_in.get());
env->ReleaseFloatArrayElements(input_, input_data_ptr, 0);
inputs[input_name] = mace::MaceTensor(input_shape, buffer_in); // construct output
auto buffer_out = std::shared_ptr<float>(new float[output_size],
std::default_delete<float[]>());
outputs[output_name] = mace::MaceTensor(output_shape, buffer_out); // run model
mace_context.engine->Run(inputs, &outputs); // transform output
jfloatArray jOutputData = env->NewFloatArray(output_size); // allocate
if (jOutputData == nullptr) return nullptr;
env->SetFloatArrayRegion(jOutputData, 0, output_size,
outputs[output_name].data().get()); // copy return jOutputData;
}
以上步骤将模型底层运行环境准备好了,接下来继续封装为模型准备数据及其数据结果展示表层工作。
建立AppModel.java为模型准备输入数据和接受模型的预测结果数据作为MainActivity和底层native方法的桥梁。
说明:这个文件有四个方法,首先是对模型的设置参数,第二个是对模型创建一个运行环境实在cpu还是在gpu下运行,第三个是对识别功能准备数据将其转换为rgb,第四个是核心功能即物体识别。
package com.tcl.weilong.mace; import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.nio.FloatBuffer; public class AppModel {
JniUtils jniUtils = new JniUtils();
//这些参数的值具体代表什么尚未搞清楚
int ompNumThreads = 2; //线程个数
int cpuAffinityPolicy = 0; //
int gpuPerfHint = 3; //
int gpuPriorityHint = 3; //
String kernelPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mace"; //内核路径 public static final String[] MODELS = new String[]{"mobilenet_v1", "mobilenet_v2"}; //模型名称,这块不要de
public static final String[] DEVICES = new String[]{"CPU", "GPU"}; //设备 String model = MODELS[1]; //mobilenet_v2
String device = DEVICES[0]; //CPU private int[] colorValues; //存储RGB颜色数据
private FloatBuffer floatBuffer; //输入缓存 /**
* 给模型设置属性
*/
public void maceMobilenetSetAttrs(){
int attrs;
attrs = jniUtils.maceMobilenetSetAttrs(ompNumThreads,cpuAffinityPolicy,gpuPerfHint,gpuPriorityHint,kernelPath);
Log.d("idiot","attrs="+attrs);
} /**
* 给模型创建运行引擎,cpu或者gpu
*/
public void maceMobilenetCreateEngine(){
int engine;
engine = jniUtils.maceMobilenetCreateEngine(model,device);
Log.d("idiot","engin="+engine);
} /**
* 输入数据处理,将照片转换成数组 float[],bitmap是224*224的大小
* @param bitmap 要处理的原始图片
* @return 将图片处理后转换成像素点进行返回
*/
public FloatBuffer dealPic(Bitmap bitmap){
//这块要创建一个224*224的图片是针对输入原始图片创建的,每一行有224个像素点,共有224行
colorValues = new int[224 * 224]; //存储像素
float[] floatValues = new float[224 * 224 * 3]; //RGB像素*3
floatBuffer = FloatBuffer.wrap(floatValues, 0, 224 * 224 * 3);
//这个函数要深究,从(0,0)点开始平移,平移尺度是单元尺寸的一个宽度。
bitmap.getPixels(colorValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
floatBuffer.rewind(); //??
//最核心的图像像素点的变化,根本没懂
for (int i = 0; i < colorValues.length; i++) {
int value = colorValues[i];
floatBuffer.put((((value >> 16) & 0xFF) - 128f) / 128f);
floatBuffer.put((((value >> 8) & 0xFF) - 128f) / 128f);
floatBuffer.put(((value & 0xFF) - 128f) / 128f);
}
return floatBuffer;
}
/**
* 模型的分类函数
* @param input 缓存的像素点
* @return 识别结果数据,在类标中匹配识别
*/
public float[] maceMobilenetClassify(FloatBuffer input){
float[] result;
result = jniUtils.maceMobilenetClassify(input.array());
return result;
}
}
以上文件即可实现对模型输入图片的功能,下面的文件是对识别结果进行匹配。创建LableCache.java文件实现:
package com.tcl.weilong.mace; import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; public class LabelCache { private List<Float> floatList = new ArrayList<>(); //每张识别结果的概率
private List<String> resultLabel = new ArrayList<>(); //识别结果缓存列表
private ResultData mResultData; // /**
* 加载类标的资源文件
* @param context 上下文
*/
public void readCacheLabelFromLocalFile(Context context) {
try {
AssetManager assetManager = context.getAssets(); //获取资源管理器
//从资源管理器中加载标签文件,所有的结果在文本文件中,识别的结果在label中去匹配
BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open("cacheLabel.txt")));
String readLine = null;
while ((readLine = reader.readLine()) != null) {
Log.d("labelCache", "readLine = " + readLine);
resultLabel.add(readLine); //原文是移动识别,这样一张大的图片会识别很多个,将所有识别的都放在该列表里,该最简工程只识别了最小识别单元
}
reader.close();
} catch (Exception e) {
Log.e("labelCache", "error " + e);
}
} /**
* 获取所有识别结果中概率最高的一个值进行返回
* @param floats 识别结果值: result = appModel.maceMobilenetClassify(input);
* @return 将识别的结果值存储到data中返回
*/
public ResultData getResultFirst(float[] floats) {
floatList.clear();
for (float f : floats) {
floatList.add(f);
}
float maxResult = Collections.max(floatList); //在所有识别结果中获取最大的概率即为最终识别结果 int indexResult = floatList.indexOf(maxResult);
if (indexResult < resultLabel.size()) {
String result = resultLabel.get(indexResult);
if (result != null) {
if (mResultData == null) {
mResultData = new ResultData(result, maxResult);
} else {
mResultData.updateData(result, maxResult);
}
return mResultData;
}
}
return null;
}
}
所需的资源文件为cacheLabel.txt文件在assets资源文件夹下面。
以上工作将识别的结果已经拿到,由于结果包括识别名称,识别的时间和相对应的概率,我们需要data包来封装这些数据,创建ResultData.java文件封装。
package com.tcl.weilong.mace; public class ResultData {
public String name; //识别结果内容
public float probability; //识别的可能性,概率值
public long costTime; // 运行时间 /**
* 构造函数,只有名称和识别概率值
* @param name
* @param probability
*/
public ResultData(String name, float probability) {
this.name = name;
this.probability = probability;
} /**
* 构造函数
* @param name
*/
public ResultData(String name) {
this.name = name;
} /**
* 更新名称和概率值,这个函数原工程中使用用于不断的识别,更新名称显示
* @param name
* @param probability
*/
public void updateData(String name, float probability) {
this.name = name;
this.probability = probability;
} }
接下来对拿到的数据进行展示,展示比较简单。其activity.xml文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_gravity="center_horizontal" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv"/>
</LinearLayout>
最后MainActivity.java文件进行调度:
package com.tcl.weilong.mace; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.nio.FloatBuffer; public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = findViewById(R.id.iv);
TextView tv = findViewById(R.id.tv);
float[] result;
//获取图片,图片的大小是224 * 224,由于模型的识别输入是该大小,如果是一张大图片就会顺次平移类似于CNN一样
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.mouse,options);
Log.d("idiot","width:"+bitmap.getWidth()+";height:"+bitmap.getHeight());
iv.setImageBitmap(bitmap); //只显示了图片,这块没有意义,原意该处为图片的获取及其展示 LabelCache labelCache = new LabelCache(); //模型结果label
Context context = getApplicationContext();
labelCache.readCacheLabelFromLocalFile(context); //加载label资源文件 AppModel appModel = new AppModel();
appModel.maceMobilenetSetAttrs(); //为模型设置环境
appModel.maceMobilenetCreateEngine(); FloatBuffer input = appModel.dealPic(bitmap); //加载输入识别照片,返回缓存的像素点单元
long start = System.currentTimeMillis();
result = appModel.maceMobilenetClassify(input); //该工程的核心功能块,识别图片
long end = System.currentTimeMillis();
long costTime = end - start; //识别一张图片的时间
Toast.makeText(this,"CostTime: "+costTime+" ms",Toast.LENGTH_LONG).show(); ResultData data;
data = labelCache.getResultFirst(result); //将返回的结果对照label处理
data.costTime = costTime;
String resultt = data.name + "\n" + data.probability + "\ncost time(ms): " + data.costTime;
tv.setText(resultt); //展示结果
} }
注意:
1.传入的图片是224*224大小,这个问题以后会更改。
2.运行的cpu架构是armv-8的,这个问题没有解决。
模拟器运行结果如下:采用的模拟器时间很慢。
真机测试运行时间和内存消耗结果:
CPU:
GPU:
MACE采用GPU加速,时间和硬件消耗缩减一半左右。
MACE(3)-----工程化的更多相关文章
- mace
作者:十岁的小男孩 QQ:929994365 心之安处即是吾乡. 本文主要的方向是终端移植.其主要又分两个小方向,理论和实践,即模型优化和模型移植.下文为前期写的,较为潦草,现在基本框架思路已经搭起来 ...
- MACE(2)-----模型编译
作者:十岁的小男孩 QQ:929994365 无用 本文仅用于学习研究,非商业用途,欢迎大家指出错误一起学习,文章内容翻译自 MACE 官方手册,记录本人阅读与开发过程,力求不失原意,但推荐阅读原文. ...
- MACE(1)-----环境搭建
作者:十岁的小男孩 QQ:929994365 无为 本文仅用于学习研究,非商业用途,欢迎大家指出错误一起学习,文章内容翻译自 MACE 官方手册,记录本人阅读与开发过程,力求不失原意,但推荐阅读原文. ...
- F.I.S初探(前端工程化)
云笔记:http://note.youdao.com/share/?id=7c4a2dcf118f0ad7bb52a36aaee46a7a&type=note 一.初识FIS 在做项目中遇 ...
- 前端工程化开发之yeoman、bower、grunt
上两遍文章介绍了前端模块化开发(以seaJs为例)和前端自动化开发(以grunt为例)的流程,参见: http://www.cnblogs.com/luozhihao/p/4818782.html ( ...
- [转]基于gulp和webpack的前端工程化
本文样例代码 :https://github.com/demohi/learning-gulp 本文主要简单介绍一下基于gulp和webpack的前端工程化. 技术栈 React.js reFlux ...
- 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML(转)
1.什么是工程化开发 软件工程的工程化开发概念由来已久,但对于前端开发来说,我们没有像VS或者eclipse这样量身打造的IDE,因为在大多数人眼中,前端代码无需编译,因此只要一个浏览器来运行调试就行 ...
- Completely change MACE timestamps?
Hi, One of my friends Sandy asked me about the possibility of completely change MACE timestamps. As ...
- 阿里云无线&前端团队是如何基于webpack实现前端工程化的
背景 前端经历了初期的野蛮生长(切图,写简单的特效)——为了兼容浏览器兼容性而出现的各种类库(JQUERY,YUI等——mv*(饱暖思淫欲,代码多了,也就想到怎样组织代码结构,backbone,ang ...
随机推荐
- JavaEE学习总结(十六)— Servlet
一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...
- C#多线程和异步(一)——基本概念和使用方法
一.多线程相关的基本概念 进程(Process):是系统中的一个基本概念. 一个正在运行的应用程序在操作系统中被视为一个进程,包含着一个运行程序所需要的资源,进程可以包括一个或多个线程 .进程之间是相 ...
- python 小爬虫爬取博客文章初体验
最近学习 python 走火入魔,趁着热情继续初级体验一下下爬虫,以前用 java也写过,这里还是最初级的爬取html,都没有用html解析器,正则等...而且一直在循环效率肯定### 很低下 imp ...
- 解决从本地文件系统上传到HDFS时的权限问题
当使用 hadoop fs -put localfile /user/xxx 时提示: put: Permission denied: user=root, access=WRITE, inode=& ...
- stm32启动文件ld md hd cl vl xl分析及选择
startup_stm32f10x_cl.s互联型的STM32F105xx,STM32F107xxstartup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx, ...
- jsp/servlet页面跳转丢失样式问题
问题:使用servlet,如何处理在多路径页面跳转中servlet转发页面样式丢失问题?(例如访问 http://localhost/project/listUser.action后转到http:// ...
- Linux 下安装 storm
一:准备工作 (机器部署情况详见)这篇博客 3台安装supervisor,2台安装nimbus (1)安装jdk1.8 (2)安装zookeeper3.4.5 以上两部分安装可查看这篇博客 (3)下载 ...
- 把ui界面加入到工程中
第一步 from untitled import Ui_Form untitled是ui转化成py的文件名:Ui_Form是转换后的类名 第二步 把Ui_Form做为工程的父类 class ...
- jzoj4313 电话线铺设(最小生成树+最近公共祖先)
题面 \(solution:\) 这道题很奇妙,需要对kruskal重构树有足够的了解!我们先对王牌电缆实行kruskal重构树,然后我们再来枚举每一条李牌电缆,我们将某一条李牌电缆加进这棵树中必然构 ...
- 恶意代码分析实战-x86反汇编速成班
x86反汇编速成 x86体系结构 3种硬件构成: 中央处理器:负责执行代码 内存(RAM):负责存储所有的数据和代码 输入/输出系统(I/O):为硬盘.键盘.显示器等设备提供接口 内存 一个程序的内存 ...