Repository: imistyrain/OpenDL
Branch: master
Commit: 2f4d0ace52b5
Files: 31
Total size: 155.4 KB
Directory structure:
gitextract__lt7gl0y/
├── .gitignore
├── CMakeLists.txt
├── README.md
├── classification.bat
├── cpp/
│ ├── LenetClassifier.cpp
│ ├── LenetClassifier.h
│ ├── evaluation.cpp
│ └── evaluationcpp.vcxproj
├── cpp4caffe/
│ ├── cnnpredictor.h
│ ├── cpp4caffe.vcxproj
│ └── evaluation.cpp
├── models/
│ ├── deploy.prototxt
│ ├── labels.txt
│ ├── mean.binaryproto
│ ├── mean.npy
│ ├── plate_fromimg.prototxt
│ ├── plate_lenet.prototxt
│ └── solver.prototxt
├── train.bat
├── train.py
├── train.sh
└── util/
├── deploy.prototxt
├── evaluation.py
├── meanfilebinartynpy.py
├── plotaccuracy.py
├── preprocess.py
├── solver.prototxt
├── test.prototxt
├── train.prototxt
├── train.txt
└── val.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
build
data
output
error
error.txt
*.solverstate
*.caffemodel
*lmdb
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8)
set(ProjName caffe-onclick)
project(${ProjName})
set(CMAKE_CXX_STANDARD 11)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
find_package(Caffe REQUIRED)
include_directories(${Caffe_INCLUDE_DIRS})
add_executable(evaluation cpp/evaluation.cpp cpp/LenetClassifier.cpp)
target_link_libraries(evaluation ${OpenCV_LIBS} ${Caffe_LIBRARIES})
add_executable(cpp4caffe cpp4caffe/evaluation.cpp)
target_link_libraries(cpp4caffe ${OpenCV_LIBS} ${Caffe_LIBRARIES})
================================================
FILE: README.md
================================================
caffe一键式训练评估集成开发环境
====================================
**Last Update 2020.07.08**
## 概述
本项目提供一个集成式开发环境,在配好caffe环境的前提下,只需将准备好的图片放入data目录下,便可以一键生成lmdb数据文件、均值文件、标注文件和测试评估模型、找出错误样本、部署模型等所有的操作,更难能可贵的是它是跨平台的,可以无缝的在Windos和Linux间切换。
使用深度学习完成一个特定的任务比如说字符识别、人脸识别等大致可以分为数据准备、定义模型、训练模型、评估模型和部署模型等几个步骤。
### 配置caffe
现在配置caffe十分方便,仅需几行命令即可搞定,确保安装了所需的依赖,这里仅摘录最关键的部分,其余的详细内容可参见参考链接.
**Windows**
::为了减少日后不必要的麻烦,建议VS2015,Cuda8.0,cudnn5.1及以上,python2.7
git clone https://github.com/BVLC/caffe
cd caffe
git checkout windows
scripts\build_win.cmd
**Linux**
git clone https://github.com/BVLC/caffe
cd caffe
mkdir build
cd build
cmake ..
make -j8
### 1.数据准备
首先收集要任务相关的数据,这里准备了一个车牌字符数据(仅包含0-9共10个数字),直接解压[data.zip](https://github.com/imistyrain/caffe-oneclick/releases/download/1.0/data.zip)到当前文件夹即可,格式如下图所示,每类图片对应一个文件夹,放到一个data文件夹下,注意格式一致型(都为.jpg或.png文件),仔细筛查,不要含有其他的非图片文件在里面,你也可以用自己的数据替换这些车牌字符数据。

caffe使用了lmdb内存数据库等来加快训练时读取数据的速度,为此,caffe自带的tools里提供了一个工具(可由convert_imageset.cpp编译生成),它的输入是图片路径和标签对组成的文件,每次都手动生成这个文件不胜其烦。
我们希望是自动化的从文件夹读取的功能,因此,本项目通过preprocess/preprocess.py来获取如下图所示的文件夹下所有的文件路径以及对应的文件标签的功能,它输出训练和验证集preprocess/train.txt和preprocess/val.txt以及标签映射文件modef/labels.txt
你也可以直接下载已经转换好的[lmdb.tar.gz](https://github.com/imistyrain/caffe-oneclick/releases/download/1.0/lmdb.tar.gz)文件直接使用
### 2.定义模型
训练定义文件位于models下的plate_train_test.prototxt,部署文件在deploy.prototxt,你可以通过[网络结构可视化](http://ethereon.github.io/netscope/#/editor)对这些网络进行可视化,以便更清晰的理解他们的含义
### 3.训练模型
```
./train.sh
```
### 4.评估模型
[evaluation.py](util/evaluation.py)用来对data文件下下的数据进行评估,它会得出迭代次数为10000时模型的错误率,并且打印出误识别图片对应的真值和预测值,并把相应数据保存在error文件夹下,命名格式为字符文件夹/图片在文件夹内的序号_真值类别_预测类别(以0/190_0_4.jpg为例,代表0/190.jpg被误识为4),这些错误识别的样本需要仔细分析,不断调试参数,以获得期望的结果。
本项目提供了一个训练好的[模型文件](https://github.com/imistyrain/caffe-oneclick/releases/download/1.0/plate999.caffemodel),其错误率低于0.1%,这就意味着其达到了99.9%以上的准确率。
### 5.部署模型
由于速度原因,实际中多使用C++而不是python进行部署,因此本项目在cpp文件夹下提供了evaluationcpp工程,它使用单例模式来防止每次预测都加载模型,只需使用如下代码即可在你的项目中一行代码使用CNN,此外,该项目也提供了对模型进行评估的功能。
```
cv::Mat img=cv::imread("imagepath.jpg");
string result=CnnPredictor::getInstance()->predict(img);
```
当然,你也可以运行calssification.bat来调用caffe自身进行分类识别
```
"../build/examples/cpp_classification/classification" "modeldef/deploy.prototxt" "trainedmodels/platerecognition_iter_1000.caffemodel" "modeldef/mean.binaryproto" "modeldef/labels.txt" "data/0/4-3.jpg"
```
其返回了最高的5个类别的相似度,不难看出训练的网络对于data/0/0.jpg有高达93%的概率认为其属于0这个字符,结果还是非常理想的
## 参考
* [Caffe 配置与示例运行](http://blog.csdn.net/guoyk1990/article/details/52909864)
* [图文并解caffe源码](http://blog.csdn.net/mounty_fsc/article/category/6136645)
* [caffe源码解析](http://blog.csdn.net/qq_16055159)
* [从零开始山寨Caffe caffe为什么这么设计?](http://www.cnblogs.com/neopenx/)
* [Caffe代码导读 21天实战caffe作者博客](http://blog.csdn.net/kkk584520/article/category/2620891/2)
* [CNN卷积神经网络推导和实现](http://blog.csdn.net/zouxy09/article/details/9993371)
* [caffe卷积层代码阅读笔记](http://blog.csdn.net/tangwei2014/article/details/47730797)
* [caffe添加新层教程](http://blog.csdn.net/shuzfan/article/details/51322976)
* [caffe中各语言预处理对应方式](http://blog.csdn.net/minstyrain/article/details/78373914)
* [mxnet 训练自己的数据](https://github.com/imistyrain/mxnet-mr)
* [MatconvNet 训练自己的数据](https://github.com/imistyrain/MatConvNet-mr)
================================================
FILE: classification.bat
================================================
@echo off
set CAFFE_DIR=..
set eval_iter=10000
set imagepath=data/0/0.jpg
set trainedmodel=snapshot/plate_iter_%eval_iter%.caffemodel
::set trainedmodel=platere996.caffemodel
echo %imagepath% %eval_iter%
"%CAFFE_DIR%/build/examples/cpp_classification/classification" "models/deploy.prototxt" "%trainedmodel%" "models/mean.binaryproto" "models/labels.txt" "%imagepath%"
pause
================================================
FILE: cpp/LenetClassifier.cpp
================================================
#include "LenetClassifier.h"
std::pairCLenetClassifier::predict(const cv::Mat &img)
{
std::pairp;
if (!bloaded)
{
load();
}
else
{
cv::Mat input;
cv::resize(img, input, cv::Size(20, 20));
cv::Mat inputBlob = blobFromImage(input);// , 255.0f, cv::Size(20, 20), _mean, false);
cv::Mat prob;
_net.setInput(inputBlob, "data");
prob = _net.forward("prob");
cv::Mat probMat = prob.reshape(1, 1);
cv::Point classNumber;
cv::minMaxLoc(probMat, NULL, &p.second, NULL, &classNumber);
p.first = classNumber.x;
}
return p;
}
bool CLenetClassifier::load(cv::String modelTxt, cv::String modelBin)
{
_net = cv::dnn::readNetFromCaffe(modelTxt, modelBin);
// _mean = cv::Scalar(66, 66, 66);
bloaded = !_net.empty();
return bloaded;
}
================================================
FILE: cpp/LenetClassifier.h
================================================
#pragma once
#include "string"
#include "opencv2/opencv.hpp"
#include
using namespace cv::dnn;
using namespace std;
const string caffeplatedir = "../";
const string model_file = caffeplatedir + "/models/deploy.prototxt";
const string trained_file = caffeplatedir + "/models/plate999.caffemodel";
const string mean_file = caffeplatedir + "/models/mean.binaryproto";
class CLenetClassifier
{
public:
static CLenetClassifier*getInstance()
{
static CLenetClassifier instance;
return &instance;
}
std::pairpredict(const cv::Mat &img);
bool load(cv::String modelTxt = model_file, cv::String modelBin = trained_file);
private:
bool bloaded = false;
Net _net;
cv::Scalar _mean;
CLenetClassifier() {
}
};
================================================
FILE: cpp/evaluation.cpp
================================================
#include "mrdir.h"
#include "mrutil.h"
#include "mropencv.h"
#include "LenetClassifier.h"
using namespace std;
const string errordir = caffeplatedir + "/error";
const string platedatadir = caffeplatedir + "data";
void cleardir(const string dir)
{
vectorfiles=getAllFilesinDir(dir);
for (int i = 0; i < files.size(); i++)
{
string filepath = dir + "/" + files[i];
remove(filepath.c_str());
}
}
void clearerror(const string dir)
{
cout << "clearing" << dir << endl;
vectorsubdirs=getAllSubdirs(dir);
for (int i = 0; i < subdirs.size(); i++)
{
string subdir = dir + "/" + subdirs[i];
cout << subdirs[i]<subdirs=getAllSubdirs(platedatadir);
for (auto sub : subdirs)
{
string subdir = platedatadir + "/" + sub;
vectorfiles=getAllFilesinDir(subdir);
for (auto file : files)
{
string fileapth = subdir + "/" + file;
cv::Mat img = cv::imread(fileapth);
auto ret=CLenetClassifier::getInstance()->predict(img).first;
if (ret == string2int(sub))
rightcount++;
else
{
cout << sub + "/" + file.substr(0, file.size() - 4) + ":" + int2string(ret) << endl;
errorcount++;
string errorlabeldir = errordir;
errorlabeldir = errorlabeldir + "/" + sub;
if (!EXISTS(errorlabeldir.c_str()))
{
MKDIR(errorlabeldir.c_str());
}
string errorfilepath = errorlabeldir + "/" + file.substr(0,file.size()-4) + "_" + sub + "_" + int2string(ret) + ".png";
//imshow("error", img);
imwrite(errorfilepath, img);
//cv::waitKey(1);
}
total++;
}
}
cout << "acc:" << rightcount << "/" << total << endl;
cout << rightcount*1.0 / total << endl;
return 0;
}
int testimg(const std::string imgpath = "data/0/0.jpg")
{
cv::Mat img = imread(imgpath);
TickMeter tm;
tm.start();
auto p = CLenetClassifier::getInstance()->predict(img);
tm.stop();
std::cout << p.first << std::endl;// " " << p.second << endl;
std::cout << tm.getTimeMilli() << "ms" << std::endl;
return 0;
}
int testdir(const std::string dir = "img")
{
auto files = getAllFilesinDir(dir);
for (int i = 0; i < files.size(); i++)
{
std::string imgpath = dir + "/" + files[i];
std::cout << files[i] << ":";
testimg(imgpath);
}
return 0;
}
int main(int argc,char*argv[])
{
if (argc==1)
evaluation();
else
{
testimg();
testdir();
}
return 0;
}
================================================
FILE: cpp/evaluationcpp.vcxproj
================================================
Debug
Win32
Debug
x64
Release
Win32
Release
x64
{0A2F0DB8-57C5-4380-93D9-E45110DE3719}
Win32Proj
charsrecog
8.1
Application
true
v140
Unicode
Application
true
v140
Unicode
Application
false
v140
true
Unicode
Application
false
v140
true
Unicode
true
true
D:\opencv33\build\include;$(IncludePath)
D:\opencv33\build\x64\vc14\lib;$(LibraryPath)
false
false
D:\opencv33\build\include;$(IncludePath)
D:\opencv33\build\x64\vc14\lib;$(LibraryPath)
Level3
Disabled
WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
Level3
Disabled
WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
$(SolutionDir)include;%(AdditionalIncludeDirectories)
/D_CRT_SECURE_NO_WARNINGS %(AdditionalOptions)
Console
true
kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;cudart.lib;cublas.lib;curand.lib;libprotobufd.lib;hdf5_tools.lib;hdf5_hl_fortran.lib;hdf5_fortran.lib;hdf5_hl_f90cstub.lib;hdf5_f90cstub.lib;hdf5_cpp.lib;hdf5_hl_cpp.lib;hdf5_hl.lib;hdf5.lib;zlib.lib;szip.lib;caffelibd.lib;opencv_world300d.lib;shlwapi.lib;leveldbd.lib;cuda.lib;libglog.lib;lmdb.lib;cudnn.lib;libopenblas.dll.a;libgflags.lib;cublas_device.lib;%(AdditionalDependencies)
compute_20,sm_20;compute_30,sm_30;compute_35,sm_35;compute_50,sm_50;
Level3
MaxSpeed
true
true
WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
Console
true
true
true
Level3
Disabled
true
true
WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
true
$(SolutionDir)include;%(AdditionalIncludeDirectories)
true
/D_CRT_SECURE_NO_WARNINGS %(AdditionalOptions)
Console
true
true
true
kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies)
Shared
64
compute_20,sm_20;compute_30,sm_30;compute_35,sm_35;compute_50,sm_50;
================================================
FILE: cpp4caffe/cnnpredictor.h
================================================
#pragma once
#include
#include "mrdir.h"
#include "opencv2/opencv.hpp"
#include
#include
#include
#include
#include
#include
using namespace std;
const string caffeplatedir = "../";
const string model_file = caffeplatedir + "models/deploy.prototxt";
const string trained_file = caffeplatedir + "models/plate999.caffemodel";
const string mean_file = caffeplatedir + "models/mean.binaryproto";
const string label_file = caffeplatedir + "models/labels.txt";
using namespace caffe; // NOLINT(build/namespaces)
using std::string;
/* Pair (label, confidence) representing a prediction. */
typedef std::pair Prediction;
class Classifier {
public:
Classifier(const string& model_file,
const string& trained_file,
const string& mean_file,
const string& label_file);
std::vector Classify(const cv::Mat& img, int N = 5);
private:
void SetMean(const string& mean_file);
std::vector Predict(const cv::Mat& img);
void WrapInputLayer(std::vector* input_channels);
void Preprocess(const cv::Mat& img,
std::vector* input_channels);
private:
std::shared_ptr > net_;
cv::Size input_geometry_;
int num_channels_;
cv::Mat mean_;
std::vector labels_;
};
Classifier::Classifier(const string& model_file,
const string& trained_file,
const string& mean_file,
const string& label_file) {
#ifdef CPU_ONLY
Caffe::set_mode(Caffe::CPU);
#else
Caffe::set_mode(Caffe::GPU);
#endif
/* Load the network. */
net_.reset(new Net(model_file, TEST));
net_->CopyTrainedLayersFrom(trained_file);
CHECK_EQ(net_->num_inputs(), 1) << "Network should have exactly one input.";
CHECK_EQ(net_->num_outputs(), 1) << "Network should have exactly one output.";
Blob* input_layer = net_->input_blobs()[0];
num_channels_ = input_layer->channels();
CHECK(num_channels_ == 3 || num_channels_ == 1)
<< "Input layer should have 1 or 3 channels.";
input_geometry_ = cv::Size(input_layer->width(), input_layer->height());
/* Load the binaryproto mean file. */
SetMean(mean_file);
/* Load labels. */
std::ifstream labels(label_file.c_str());
CHECK(labels) << "Unable to open labels file " << label_file;
string line;
while (std::getline(labels, line))
labels_.push_back(string(line));
Blob* output_layer = net_->output_blobs()[0];
CHECK_EQ(labels_.size(), output_layer->channels())
<< "Number of labels is different from the output layer dimension.";
}
static bool PairCompare(const std::pair& lhs,
const std::pair& rhs) {
return lhs.first > rhs.first;
}
/* Return the indices of the top N values of vector v. */
static std::vector Argmax(const std::vector& v, int N) {
std::vector > pairs;
for (size_t i = 0; i < v.size(); ++i)
pairs.push_back(std::make_pair(v[i], i));
std::partial_sort(pairs.begin(), pairs.begin() + N, pairs.end(), PairCompare);
std::vector result;
for (int i = 0; i < N; ++i)
result.push_back(pairs[i].second);
return result;
}
/* Return the top N predictions. */
std::vector Classifier::Classify(const cv::Mat& img, int N) {
std::vector output = Predict(img);
N = std::min(labels_.size(), N);
std::vector maxN = Argmax(output, N);
std::vector predictions;
for (int i = 0; i < N; ++i) {
int idx = maxN[i];
predictions.push_back(std::make_pair(labels_[idx], output[idx]));
}
return predictions;
}
/* Load the mean file in binaryproto format. */
void Classifier::SetMean(const string& mean_file) {
BlobProto blob_proto;
ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto);
/* Convert from BlobProto to Blob */
Blob mean_blob;
mean_blob.FromProto(blob_proto);
CHECK_EQ(mean_blob.channels(), num_channels_)
<< "Number of channels of mean file doesn't match input layer.";
/* The format of the mean file is planar 32-bit float BGR or grayscale. */
std::vector channels;
float* data = mean_blob.mutable_cpu_data();
for (int i = 0; i < num_channels_; ++i) {
/* Extract an individual channel. */
cv::Mat channel(mean_blob.height(), mean_blob.width(), CV_32FC1, data);
channels.push_back(channel);
data += mean_blob.height() * mean_blob.width();
}
/* Merge the separate channels into a single image. */
cv::Mat mean;
cv::merge(channels, mean);
/* Compute the global mean pixel value and create a mean image
* filled with this value. */
cv::Scalar channel_mean = cv::mean(mean);
mean_ = cv::Mat(input_geometry_, mean.type(), channel_mean);
}
std::vector Classifier::Predict(const cv::Mat& img) {
Blob* input_layer = net_->input_blobs()[0];
input_layer->Reshape(1, num_channels_,
input_geometry_.height, input_geometry_.width);
/* Forward dimension change to all layers. */
net_->Reshape();
std::vector input_channels;
WrapInputLayer(&input_channels);
Preprocess(img, &input_channels);
net_->Forward();
/* Copy the output layer to a std::vector */
Blob* output_layer = net_->output_blobs()[0];
const float* begin = output_layer->cpu_data();
const float* end = begin + output_layer->channels();
return std::vector(begin, end);
}
/* Wrap the input layer of the network in separate cv::Mat objects
* (one per channel). This way we save one memcpy operation and we
* don't need to rely on cudaMemcpy2D. The last preprocessing
* operation will write the separate channels directly to the input
* layer. */
void Classifier::WrapInputLayer(std::vector* input_channels) {
Blob* input_layer = net_->input_blobs()[0];
int width = input_layer->width();
int height = input_layer->height();
float* input_data = input_layer->mutable_cpu_data();
for (int i = 0; i < input_layer->channels(); ++i) {
cv::Mat channel(height, width, CV_32FC1, input_data);
input_channels->push_back(channel);
input_data += width * height;
}
}
void Classifier::Preprocess(const cv::Mat& img,
std::vector* input_channels) {
/* Convert the input image to the input image format of the network. */
cv::Mat sample;
if (img.channels() == 3 && num_channels_ == 1)
cv::cvtColor(img, sample, CV_BGR2GRAY);
else if (img.channels() == 4 && num_channels_ == 1)
cv::cvtColor(img, sample, CV_BGRA2GRAY);
else if (img.channels() == 4 && num_channels_ == 3)
cv::cvtColor(img, sample, CV_BGRA2BGR);
else if (img.channels() == 1 && num_channels_ == 3)
cv::cvtColor(img, sample, CV_GRAY2BGR);
else
sample = img;
cv::Mat sample_resized;
if (sample.size() != input_geometry_)
cv::resize(sample, sample_resized, input_geometry_);
else
sample_resized = sample;
cv::Mat sample_float;
if (num_channels_ == 3)
sample_resized.convertTo(sample_float, CV_32FC3);
else
sample_resized.convertTo(sample_float, CV_32FC1);
cv::Mat sample_normalized;
// cv::subtract(sample_float, mean_, sample_normalized);
sample_normalized = sample_float;
/* This operation will write the separate BGR planes directly to the
* input layer of the network because it is wrapped by the cv::Mat
* objects in input_channels. */
cv::split(sample_normalized, *input_channels);
CHECK(reinterpret_cast(input_channels->at(0).data)
== net_->input_blobs()[0]->cpu_data())
<< "Input channels are not wrapping the input layer of the network.";
}
class CnnPredictor
{
public:
static CnnPredictor*getInstance()
{
static CnnPredictor instance;
return &instance;
}
const string predict(cv::Mat &img)
{
std::vector predictions = pclassifier->Classify(img);
return predictions[0].first;
}
private:
CnnPredictor()
{
pclassifier = new Classifier(model_file, trained_file, mean_file, label_file);
};
~CnnPredictor()
{
if (pclassifier)
delete pclassifier;
};
Classifier *pclassifier;
};
================================================
FILE: cpp4caffe/cpp4caffe.vcxproj
================================================
Debug
x64
Release
x64
{D9C1D98F-F412-44F6-A819-D1AFB27D31E7}
Win32Proj
x64
cpp4caffe
NoUpgrade
8.1
Application
false
MultiByte
v140
Application
false
MultiByte
v140
<_ProjectFileVersion>10.0.20506.1
Debug\
Debug\
$(ProjectName)-d
.exe
true
true
..\bin\
Release\
$(ProjectName)
.exe
false
true
Debug/
EnableFastChecks
CompileAsCpp
ProgramDatabase
Sync
Disabled
MaxSpeed
NotUsing
MultiThreadedDebugDLL
true
Level3
WIN32;_WINDOWS;CAFFE_VERSION=1.0.0;BOOST_ALL_NO_LIB;USE_LMDB;USE_LEVELDB;USE_CUDNN;USE_OPENCV;CMAKE_WINDOWS_BUILD;GLOG_NO_ABBREVIATED_SEVERITIES;GOOGLE_GLOG_DLL_DECL=__declspec(dllimport);GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS=__declspec(dllimport);H5_BUILT_AS_DYNAMIC_LIB=1;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)
$(IntDir)
WIN32;_DEBUG;_WINDOWS;CAFFE_VERSION=1.0.0;BOOST_ALL_NO_LIB;USE_LMDB;USE_LEVELDB;USE_CUDNN;USE_OPENCV;CMAKE_WINDOWS_BUILD;GLOG_NO_ABBREVIATED_SEVERITIES;GOOGLE_GLOG_DLL_DECL=__declspec(dllimport);GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS=__declspec(dllimport);H5_BUILT_AS_DYNAMIC_LIB=1;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)
$(ProjectDir)/$(IntDir)
%(Filename).h
%(Filename).tlb
%(Filename)_i.c
%(Filename)_p.c
%(AdditionalOptions) /machine:x64
kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;caffe-d.lib;caffeproto-d.lib;boost_system-vc140-mt-gd-1_61.lib;boost_thread-vc140-mt-gd-1_61.lib;boost_filesystem-vc140-mt-gd-1_61.lib;boost_chrono-vc140-mt-gd-1_61.lib;boost_date_time-vc140-mt-gd-1_61.lib;boost_atomic-vc140-mt-gd-1_61.lib;glogd.lib;gflagsd.lib;shlwapi.lib;libprotobufd.lib;caffehdf5_hl_D.lib;caffehdf5_D.lib;caffezlibd.lib;lmdbd.lib;ntdll.lib;leveldbd.lib;snappy_staticd.lib;caffezlibd.lib;cudart.lib;curand.lib;cublas.lib;cublas_device.lib;cudnn.lib;opencv_highgui310d.lib;opencv_videoio310d.lib;opencv_imgcodecs310d.lib;opencv_imgproc310d.lib;opencv_core310d.lib;libopenblas.dll.a;python27.lib;boost_python-vc140-mt-gd-1_61.lib
%(AdditionalLibraryDirectories)
Debug
%(IgnoreSpecificDefaultLibraries)
MTCNN-d.pdb
Console
false
%(AdditionalIncludeDirectories)
Release/
CompileAsCpp
Sync
AnySuitable
MaxSpeed
NotUsing
MultiThreadedDLL
true
Level3
WIN32;_WINDOWS;NDEBUG;CAFFE_VERSION=1.0.0;BOOST_ALL_NO_LIB;USE_LMDB;USE_LEVELDB;USE_CUDNN;USE_OPENCV;CMAKE_WINDOWS_BUILD;GLOG_NO_ABBREVIATED_SEVERITIES;GOOGLE_GLOG_DLL_DECL=__declspec(dllimport);GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS=__declspec(dllimport);H5_BUILT_AS_DYNAMIC_LIB=1;CMAKE_INTDIR="Release";%(PreprocessorDefinitions)
$(IntDir)
ProgramDatabase
WIN32;_WINDOWS;NDEBUG;CAFFE_VERSION=1.0.0;BOOST_ALL_NO_LIB;USE_LMDB;USE_LEVELDB;USE_CUDNN;USE_OPENCV;CMAKE_WINDOWS_BUILD;GLOG_NO_ABBREVIATED_SEVERITIES;GOOGLE_GLOG_DLL_DECL=__declspec(dllimport);GOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS=__declspec(dllimport);H5_BUILT_AS_DYNAMIC_LIB=1;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions)
$(ProjectDir)/$(IntDir)
%(Filename).h
%(Filename).tlb
%(Filename)_i.c
%(Filename)_p.c
%(AdditionalOptions) /machine:x64
kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;boost_system-vc140-mt-1_61.lib;boost_thread-vc140-mt-1_61.lib;boost_filesystem-vc140-mt-1_61.lib;boost_chrono-vc140-mt-1_61.lib;boost_date_time-vc140-mt-1_61.lib;boost_atomic-vc140-mt-1_61.lib;glog.lib;gflags.lib;shlwapi.lib;libprotobuf.lib;caffehdf5_hl.lib;caffehdf5.lib;caffezlib.lib;lmdb.lib;ntdll.lib;leveldb.lib;snappy_static.lib;caffe.lib;caffeproto.lib;cudart.lib;curand.lib;cublas.lib;cublas_device.lib;cudnn.lib;opencv_highgui310.lib;opencv_imgcodecs310.lib;opencv_imgproc310.lib;opencv_core310.lib;libopenblas.dll.a;python27.lib;boost_python-vc140-mt-1_61.lib
%(AdditionalLibraryDirectories)
true
%(IgnoreSpecificDefaultLibraries)
MTCNN.pdb
Console
false
================================================
FILE: cpp4caffe/evaluation.cpp
================================================
#define REG_USE_CNN 1
#pragma warning(disable:4996)
#include "mrdir.h"
#include "mrutil.h"
#include "cnnpredictor.h"
const string errordir = caffeplatedir + "error";
const string platedatadir = caffeplatedir+"data";
void cleardir(const string dir)
{
vectorfiles=getAllFilesinDir(dir);
for (int i = 0; i < files.size(); i++)
{
string filepath = dir + "/" + files[i];
remove(filepath.c_str());
}
}
void clearerror(const string dir)
{
vectorsubdirs=getAllSubdirs(dir);
for (int i = 0; i < subdirs.size(); i++)
{
string subdir = dir + "/" + subdirs[i];
cleardir(subdir);
}
}
int evaluation()
{
string line;
string label;
int rightcount = 0, errorcount = 0, total = 0;
if (!EXISTS(errordir.c_str()))
{
cout << "Error dir not exist" << endl;
MKDIR(errordir.c_str());
}
clearerror(errordir);
vectorsubdirs=getAllSubdirs(platedatadir);
for (auto sub : subdirs)
{
string subdir = platedatadir + "/" + sub;
vectorfiles=getAllFilesinDir(subdir);
for (auto file : files)
{
string fileapth = subdir + "/" + file;
cv::Mat img = cv::imread(fileapth);
auto ret = split(CnnPredictor::getInstance()->predict(img), " ")[1];
if (ret == sub)
rightcount++;
else
{
errorcount++;
string errorlabeldir = errordir;
errorlabeldir = errorlabeldir + "/" + sub;
if (!EXISTS(errorlabeldir.c_str()))
{
MKDIR(errorlabeldir.c_str());
}
string errorfilepath = errorlabeldir + "/" + file.substr(0,file.size()-4) + "_" + sub + "_" + ret + ".png";
cout << sub + "/" + file.substr(0, file.size() - 4) + ":" + ret << endl;
//imshow("error", img);
imwrite(errorfilepath, img);
//cv::waitKey(1);
}
total++;
}
}
cout << "acc:" << rightcount << "/" << total << endl;
cout << rightcount*1.0 / total << endl;
return 0;
}
int main(int argc,char*argv[])
{
if (argc==1)
evaluation();
else
{
cv::Mat img = cv::imread(argv[1]);
cout << CnnPredictor::getInstance()->predict(img) << endl;
}
return 0;
}
================================================
FILE: models/deploy.prototxt
================================================
name: "LeNet"
layer{
name:"data"
type:"Input"
top: "data"
input_param { shape: {dim:1 dim:3 dim:20 dim:20 } }
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "fc1"
type: "InnerProduct"
bottom: "pool2"
top: "fc1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "fc1"
top: "fc1"
}
layer {
name: "fc2"
type: "InnerProduct"
bottom: "fc1"
top: "fc2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "fc2"
top: "prob"
}
================================================
FILE: models/labels.txt
================================================
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
================================================
FILE: models/plate_fromimg.prototxt
================================================
name: "LeNet"
layer {
name: "mnist"
type: "ImageData"
top: "data"
top: "label"
image_data_param {
source: "util/train.txt"
root_folder: "data/"
new_height: 20
new_width: 20
batch_size: 64
shuffle: true
}
include: { phase: TRAIN }
}
layer {
name: "mnist"
type: "ImageData"
top: "data"
top: "label"
image_data_param {
source: "util/val.txt"
root_folder: "data/"
new_height: 20
new_width: 20
batch_size: 64
}
include: { phase: TEST }
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "fc1"
type: "InnerProduct"
bottom: "pool2"
top: "fc1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "fc1"
top: "fc1"
}
layer {
name: "fc2"
type: "InnerProduct"
bottom: "fc1"
top: "fc2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc2"
bottom: "label"
top: "loss"
}
================================================
FILE: models/plate_lenet.prototxt
================================================
name: "Lenet"
layer {
name: "Lenet"
type: "Data"
top: "data"
top: "label"
transform_param {
scale: 0.00390625
}
data_param {
source: "lmdb/train_lmdb"
backend: LMDB
batch_size: 64
}
include: { phase: TRAIN }
}
layer {
name: "Lenet"
type: "Data"
top: "data"
top: "label"
transform_param {
scale: 0.00390625
}
data_param {
source: "lmdb/val_lmdb"
backend: LMDB
batch_size: 64
}
include: { phase: TEST }
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "fc1"
type: "InnerProduct"
bottom: "pool2"
top: "fc1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "fc1"
top: "fc1"
}
layer {
name: "fc2"
type: "InnerProduct"
bottom: "fc1"
top: "fc2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc2"
bottom: "label"
top: "loss"
}
================================================
FILE: models/solver.prototxt
================================================
# The train/test net protocol buffer definition
net: "models/plate_lenet.prototxt"
#net: "models/plate_fromimg.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 24
# Carry out testing every 500 training iterations.
test_interval: 1000
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
#solver_type: ADAGRAD
weight_decay: 0.0005
#weight_decay: 0.0005
# The learning rate policy
lr_policy: "inv"
gamma: 0.0001
power: 0.75
# Display every 100 iterations
display: 1000
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "output/plate"
# solver mode: CPU or GPU
solver_mode: GPU
================================================
FILE: train.bat
================================================
@echo off
set CAFFE_DIR=..
set DATA=data
set REISZE_DIM=20
set converttool=%CAFFE_DIR%/build/tools/convert_imageset
set finetunemodel=models/plate999.caffemodel
if exist "lmdb/train_lmdb" (set regenlmdb=0) else (set regenlmdb=1)
::set regenlmdb=1
if %regenlmdb% equ 1 goto regeneratelmdb
goto train
:regeneratelmdb
echo "Creating train lmdb..."
del "lmdb/train_lmdb\*.*" /f /s /q
del "lmdb/val_lmdb\*.*" /f /s /q
rd /s /q "lmdb/train_lmdb"
rd /s /q "lmdb/val_lmdb"
rd /s /q lmdb
mkdir lmdb
"%converttool%" --resize_height=%REISZE_DIM% --resize_width=%REISZE_DIM% --shuffle "%DATA%/" "util/train.txt" "lmdb/train_lmdb"
echo "Creating val lmdb..."
"%converttool%" --resize_height=%REISZE_DIM% --resize_width=%REISZE_DIM% --shuffle "%DATA%/" "util/val.txt" "lmdb/val_lmdb"
echo "Computing mean:"
"%CAFFE_DIR%/build/tools/compute_image_mean" "lmdb/train_lmdb" "models/mean.binaryproto"
:train
rem if exist %finetunemodel% (set extra_cmd="--weights=%finetunemodel%")
rem "%CAFFE_DIR%/build/tools/caffe" train --solver=models/solver.prototxt %extra_cmd%
python3 train.py
:evaluation
python util/evaluation.py
echo "Done"
pause
================================================
FILE: train.py
================================================
import os
import sys
sys.path.insert(0, '../python')
import caffe
from caffe import layers as L, params as P
from caffe.proto import caffe_pb2
import lmdb
from tqdm import tqdm
import logging
import cv2
import numpy as np
root_folder="data/"
batch_size = 64
test_batch_size = 100
input_size = [20,20]
G4 = 4*1024*1024*1024
def remove_if_exists(db):
if os.path.exists(db):
logger.info('remove %s'%db)
shutil.rmtree(db)
def get_test_num(valpath = "util/val.txt"):
with open(valpath) as f:
lines = f.readlines()
return len(lines)
def make_datum(img,label):
return caffe_pb2.Datum(channels=3,width=input_size[0],height=input_size[1],label=label,
data=np.rollaxis(img,2).tobytes())
def gen_data_layer(phase="train",uselmdb=True):
if uselmdb:
source = "lmdb/"+phase+"_lmdb"
if not os.path.exists(source):
print("creating "+source)
os.makedirs(source)
db = lmdb.open(source, map_size=G4)
txn = db.begin(write=True)
txtfile="util/"+phase+".txt"
with open(txtfile) as f:
lines = f.readlines()
for i,line in tqdm(enumerate(lines)):
items = line.split()
imgpath = root_folder+"/"+items[0]
img = cv2.imread(imgpath)
if img is None:
logging.info("cannot read"+imgpath)
key = "%08d_data"%(i)
label=int(items[1])
txn.put(key,make_datum(img,label).SerializeToString())
if i %1000 == 0:
txn.commit()
txn = db.begin(write=True)
db.close()
data, label = L.Data(batch_size=batch_size, backend=P.Data.LMDB,source=source,transform_param=dict(scale=1./255), ntop=2)
else:
txtfile="util/"+phase+".txt"
data, label = L.ImageData(image_data_param=dict(source=txtfile,root_folder=root_folder,batch_size=batch_size,shuffle=phase=="train",new_width=20,new_height=20),ntop=2,transform_param=dict(scale=1./255))
return data,label
def lenet(phase="train",batch_size=64):
n = caffe.NetSpec()
n.data, n.label = gen_data_layer(phase)
n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier'))
n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier'))
n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.fc1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
n.relu1 = L.ReLU(n.fc1, in_place=True)
n.fc2 = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
n.acc = L.Accuracy(n.fc2, n.label)
n.loss = L.SoftmaxWithLoss(n.fc2, n.label)
return n
def lenet_deploy(net,deploy_net_file="util/deploy.prototxt"):
deploy_net = net
with open(deploy_net_file, 'w') as f:
net_param = deploy_net.to_proto()
del net_param.layer[0]
del net_param.layer[-1]
del net_param.layer[-1]
net_param.name = 'lenet'
net_param.input.extend(['data'])
net_param.input_shape.extend([
caffe_pb2.BlobShape(dim=[1, 3, input_size[0], input_size[1]])])
f.write(str(net_param))
def gen_solver_txt(train_net_path, test_net_path):
s = caffe_pb2.SolverParameter()
s.train_net = train_net_path
s.test_net.append(test_net_path)
s.test_interval = 500
s.test_iter.append(int(get_test_num()/test_batch_size))
s.max_iter = 10000
s.base_lr = 0.01
s.lr_policy = 'step'
s.gamma = 0.1
s.power = 0.75
s.stepsize = 5000
s.momentum = 0.9
s.weight_decay = 5e-4
s.display = 1000
s.snapshot = 5000
s.snapshot_prefix = 'output/plate'
s.solver_mode = caffe_pb2.SolverParameter.GPU
return s
def main():
train_net_path = 'util/train.prototxt'
net = lenet('train',batch_size)
with open(train_net_path, 'w') as f:
f.write(str(net.to_proto()))
test_net_path = 'util/test.prototxt'
net = lenet('val',test_batch_size)
with open(test_net_path, 'w') as f:
f.write(str(net.to_proto()))
lenet_deploy(net)
solver_path = 'util/solver.prototxt'
with open(solver_path, 'w') as f:
f.write(str(gen_solver_txt(train_net_path, test_net_path)))
caffe.set_mode_gpu()
solver = caffe.get_solver(solver_path)
solver.solve()
if __name__=="__main__":
main()
================================================
FILE: train.sh
================================================
#!/usr/bin/env sh
set -e
DATA=data
TOOLS=../build/tools
regeneratelmdb=0
convert_lmdb(){
RESIZE_HEIGHT=20
RESIZE_WIDTH=20
echo "Creating train lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle $DATA/ \
util/train.txt \
lmdb/train_lmdb
echo "Creating val lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle $DATA/ \
util/val.txt \
lmdb/val_lmdb
}
create_lmdb(){
if [ -d lmdb ] ; then
if [ $regeneratelmdb -eq 1 ] ; then
rm lmdb -rf
mkdir lmdb
convert_lmdb
fi
else
mkdir lmdb
convert_lmdb
fi
}
train(){
if [ ! -d output ]; then
mkdir output
fi
latest=$(ls -t models/*.caffemodel | head -n 1)
if [ -f ${latest} ]; then
echo "Resume training from ${latest}"
$TOOLS/caffe train --solver=models/solver.prototxt --weights=$latest
else
echo "Start Training"
$TOOLS/caffe train --solver=models/solver.prototxt
fi
echo "Done"
}
# python evaluate
evaluate(){
latest=$(ls -t output/*.caffemodel | head -n 1)
echo "Evaluating "${latest}
python util/evaluation.py --weights=${latest}
cd ..
}
# c++ evaluate
run(){
if [ not -d build ]; then
mkdir build
fi
cd build
cmake ..
make -j8
./evaluation
#./cpp4caffe
}
# generate util/train.txt and val.txt for training
# python3 util/preprocess.py
#create_lmdb
# train
python train.py
evaluate
#run
================================================
FILE: util/deploy.prototxt
================================================
name: "lenet"
input: "data"
input_shape {
dim: 1
dim: 3
dim: 20
dim: 20
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
convolution_param {
num_output: 20
kernel_size: 5
weight_filler {
type: "xavier"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
convolution_param {
num_output: 50
kernel_size: 5
weight_filler {
type: "xavier"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "fc1"
type: "InnerProduct"
bottom: "pool2"
top: "fc1"
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "fc1"
top: "fc1"
}
layer {
name: "fc2"
type: "InnerProduct"
bottom: "fc1"
top: "fc2"
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
}
}
================================================
FILE: util/evaluation.py
================================================
import os,argparse,sys,time,shutil
import numpy as np
import sys
sys.path.append('../python')
import caffe
import logging
from tqdm import tqdm
def create_logger(logdir="output"):
time_str = time.strftime('%Y%m%d-%H%M%S')
log_file = '{}/{}.log'.format(logdir, time_str)
head = '%(asctime)-15s %(message)s'
logging.basicConfig(filename=str(log_file),format=head)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
console = logging.StreamHandler()
logging.getLogger('').addHandler(console)
def clearlasterrors(args):
if os.path.exists("error"):
subdirs=os.listdir(args.errordir)
for subdir in subdirs:
files=os.listdir(args.errordir+"/"+subdir)
for file in files:
os.remove(args.errordir+"/"+subdir+"/"+file)
os.rmdir(args.errordir+"/"+subdir)
def loadmean(meanprotopath):
blob = caffe.proto.caffe_pb2.BlobProto()
blob.ParseFromString(open(meanprotopath, 'rb').read())
return np.array(caffe.io.blobproto_to_array(blob))[0]
def getclassifier(args):
classifier = caffe.Classifier(args.modeldef, args.weights,image_dims=args.image_dims
)
##mean=loadmean(args.meanfile).mean(1).mean(1),#raw_scale=255,channel_swap=[2,1,0]
caffe.set_mode_gpu()
return classifier
class EvalStatic:
total = 0
error = 0
def __str__(self):
return str(self.error)+","+str(self.total)+","+str(self.error*1.0/self.total)
def evaluationonebyone(args):
labels=[w.split()[1] for w in open(args.labelfile).readlines()]
classifier = getclassifier(args)
start = time.time()
if not os.path.exists(args.errordir):
os.mkdir(args.errordir)
subdirs=os.listdir(args.datadir)
evalstatics=[]
for subdir in subdirs:
print(subdir+":")
evalstatic=EvalStatic()
files=os.listdir(args.datadir+'/'+subdir)
evalstatic.total=len(files)
for file in tqdm(files):
imgpath=args.datadir+'/'+subdir+'/'+file
inputs = [caffe.io.load_image(imgpath)]
try:
predictions = classifier.predict(inputs,oversample=False)
except Exception as e:
print(e)
p=predictions[0,:].argmax()
label=labels[p]
if subdir!=label:
logging.info(subdir+" "+file+":"+str(label))
evalstatic.error=evalstatic.error+1
if not os.path.exists(args.errordir+'/'+subdir):
os.mkdir(args.errordir+'/'+subdir)
errorfilepath=args.errordir+'/'+subdir+'/'+file[:-4]+"_"+subdir+'_'+label+'.jpg'
shutil.copy(imgpath,errorfilepath)
evalstatics.append(evalstatic)
logging.info("Done in %.2f s." % (time.time() - start))
totalcount=0
error=0
for i,evalstatic in enumerate(evalstatics):
error=error+evalstatic.error
totalcount=totalcount+evalstatic.total
logging.info(subdirs[i]+":"+str(evalstatic))
logging.info("Toal error")
logging.info(str(error)+" "+str(totalcount)+" "+str(error*1.0/totalcount))
def evaluation_batch(args):
labels=[w.split()[1] for w in open(args.labelfile).readlines()]
classifier=getclassifier(args)
start = time.time()
if not os.path.exists(args.errordir):
os.mkdir(args.errordir)
subdirs=os.listdir(args.datadir)
evalstatics=[]
for subdir in subdirs:
print(subdir)
evalstatic=EvalStatic()
files=os.listdir(args.datadir+'/'+subdir)
evalstatic.total=len(files)
inputs=[caffe.io.load_image(args.datadir+'/'+subdir+'/'+file) for file in files]
try:
predictions = classifier.predict(inputs,oversample=False)
except Exception as e:
print(e)
for i in tqdm(range(len(files))):
p=predictions[i,:].argmax()
label=labels[p]
if subdir!=label:
logging.info(subdir+" "+files[i]+":"+str(label))
evalstatic.error=evalstatic.error+1
if not os.path.exists(args.errordir+'/'+subdir):
os.mkdir(args.errordir+'/'+subdir)
imgpath=args.datadir+"/"+subdir+"/"+files[i]
errorfilepath=args.errordir+'/'+subdir+'/'+files[i][:-4]+"_"+subdir+'_'+label+'.jpg'
shutil.copy(imgpath,errorfilepath)
evalstatics.append(evalstatic)
logging.info("Done in %.2f s." % (time.time() - start))
totalcount=0
error=0
for i,evalstatic in enumerate(evalstatics):
error=error+evalstatic.error
totalcount=totalcount+evalstatic.total
logging.info(subdirs[i]+":"+str(evalstatic))
logging.info("Toal error")
logging.info(str(error)+" "+str(totalcount)+" "+str(error*1.0/totalcount))
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument("--iter",default=10000,help="caffemodel iter to evaluation")
parser.add_argument("--datadir",default="data",help="datadir")
parser.add_argument("--image_dims",default=[20,20],help="image_dims")
parser.add_argument("--modeldef",default="util/deploy.prototxt",help="deploy file")
parser.add_argument("--weights",default="models/plate999.caffemodel",help="caffemodel")
parser.add_argument("--labelfile",default="models/labels.txt",help="label file")
parser.add_argument("--meanfile",default="models/mean.binaryproto",help="meanfile")
parser.add_argument("--errordir",default="error",help="errordir")
parser.add_argument("--logfile",default="error.txt",help="log txt")
parser.add_argument("--evaluationonebyone",default=True,help="log txt")
parser.add_argument("--imgpath",default="data/0/0.jpg",help="image path")
args = parser.parse_args()
return args
def classification():
args = get_args()
args = parser.parse_args()
labels=[w.split()[1] for w in open(args.labelfile).readlines()]
classifier=getclassifier(args)
inputs=[caffe.io.load_image(args.imgpath)]
predictions = classifier.predict(inputs,oversample=False)
p=predictions[0,:].argmax()
label=labels[p]
print(label,predictions[0,p])
top_inds = predictions[0,:].argsort()[::-1][:5]
def evaluation():
args = get_args()
clearlasterrors(args)
create_logger()
if args.evaluationonebyone:
evaluationonebyone(args)
else:
evaluation_batch(args)
if __name__=='__main__':
evaluation()
#classification()
================================================
FILE: util/meanfilebinartynpy.py
================================================
import os
import sys
sys.path.append('../../python')
MEAN_BIN = "../models/mean.binaryproto"
MEAN_NPY = "../models/mean.npy"
from caffe.proto import caffe_pb2
from caffe.io import blobproto_to_array
print('generating mean file...')
mean_blob = caffe_pb2.BlobProto()
mean_blob.ParseFromString(open(MEAN_BIN, 'rb').read())
import numpy as np
mean_npy = blobproto_to_array(mean_blob)
mean_npy_shape = mean_npy.shape
mean_npy = mean_npy.reshape(mean_npy_shape[1], mean_npy_shape[2], mean_npy_shape[3])
np.save(open(MEAN_NPY, 'wb'), mean_npy)
print('done...')
================================================
FILE: util/plotaccuracy.py
================================================
#coding =utf-8
#迷若烟雨@Baidu
import re
import matplotlib.pyplot as plt
import os
import sys
import datetime
def getlastesttraininfofilefromdir(logdir):
logfiles=os.listdir(logdir)
infologfiles=filter(lambda s:s.startswith('INFO'),logfiles)
infologfiles=filter(lambda s:s.endswith('.txt'),infologfiles)
if infologfiles:
lastestfile=infologfiles[0]
maxtm=0
for logf in infologfiles:
path = os.path.join(logdir, logf)
timestamp = os.path.getmtime(path)
date = datetime.datetime.fromtimestamp(timestamp)
if timestamp>maxtm:
lastestfile=path
return lastestfile
else:
return None
def plotaccuarcy():
logdir='log'
infologfile=getlastesttraininfofilefromdir(logdir)
print(infologfile)
if infologfile:
# infologfile='../log/INFO2015-11-19T19-45-15.txt'
f=open(infologfile)
lines=f.read()
#print lines
iterations=re.findall('Iteration \d*',lines)
accuracysstrings=re.findall('accuracy = \d*.\d*',lines)
trainlosstrings=re.findall('Train net output #0: loss = \d*.\d*',lines)
testlossstrings=re.findall('Test net output #1: loss = \d*.\d*',lines)
f.close()
accuracys=[ac[11:] for ac in accuracysstrings]
trainlosses=[loss[27:-1]for loss in trainlosstrings]
testlosses=[loss[27:-1]for loss in testlossstrings]
#for ac in accuracys:
# print ac
plt.plot(range(len(accuracys)),accuracys)
#plt.plot(range(len(trainlosses)),trainlosses)
#plt.plot(range(len(testlosses)),testlosses)
plt.show()
if __name__=="__main__":
plotaccuarcy()
================================================
FILE: util/preprocess.py
================================================
import os,argparse,random,shutil
def main(args):
datadir=args.rootdir+"/"+args.dataname
print("loading data from "+datadir+":")
trainfile=open("util/train.txt","w")
valfile=open("util/val.txt","w")
categoryfile=open("models/labels.txt",'w')
paths=os.listdir(datadir)
classindex=0
trainpaths=[]
valpaths=[]
categorys=[]
for subdir in paths:
if(os.path.isdir(datadir+"/"+subdir)):
categorys.append(str(classindex)+" "+subdir+"\n")
files=os.listdir(datadir+"/"+subdir)
files=[file for file in files]
random.shuffle(files)
print(subdir,len(files))
num2train=len(files)*args.trainrtaio
for fileindex,file in enumerate(files):
if fileindex