forked from MaybeShewill-CV/lanenet-lane-detection
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
63d8dfc
commit 9dba8a9
Showing
6 changed files
with
483 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[LaneNet] | ||
# 模型文件路径 | ||
model_file_path=~/MNN-0.2.1.0/beec_task/lane_detection/model/lanenet_model.mnn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/************************************************ | ||
* Author: MaybeShewill-CV | ||
* File: configParser.cpp | ||
* Date: 2019/10/10 上午10:39 | ||
************************************************/ | ||
|
||
#include "config_parser.h" | ||
|
||
#include <algorithm> | ||
#include <cctype> | ||
#include <fstream> | ||
#include <iostream> | ||
#include <sstream> | ||
#include <string> | ||
|
||
namespace beec { | ||
namespace config_parse_utils { | ||
|
||
ConfigParser::ConfigParser(const std::string &filename) { | ||
|
||
std::ifstream fin(filename); | ||
|
||
if (fin.good()) { | ||
std::string line; | ||
std::string current_header = ""; | ||
while (std::getline(fin, line)) { | ||
trim(line); | ||
|
||
// Skip empty lines | ||
if (line.size() == 0) | ||
continue; | ||
|
||
switch (line[0]) { | ||
case '#': | ||
case ';': | ||
// Ignore comments | ||
break; | ||
case '[': | ||
// Section header | ||
current_header = read_header(line); | ||
break; | ||
default: | ||
// Everything else will be configurations | ||
read_configuration(line, current_header); | ||
} | ||
} | ||
fin.close(); | ||
} else { | ||
throw std::runtime_error("File `" + filename + "` does not exist"); | ||
} | ||
} | ||
|
||
std::map<std::string, std::string> ConfigParser::get_section(const std::string §ion_name) const { | ||
|
||
if (_m_sections.count(section_name) == 0) { | ||
std::string error = "No such key: `" + section_name + "`"; | ||
throw std::out_of_range(error); | ||
} | ||
return _m_sections.at(section_name); | ||
} | ||
|
||
std::map<std::string, std::string> ConfigParser::operator[](const std::string §ion_name) const { | ||
|
||
if (_m_sections.count(section_name) == 0) { | ||
std::string error = "No such key: `" + section_name + "`"; | ||
throw std::out_of_range(error); | ||
} | ||
return _m_sections.at(section_name); | ||
} | ||
|
||
void ConfigParser::dump(FILE *log_file) { | ||
|
||
// Set up iterators | ||
std::map<std::string, std::string>::iterator itr1; | ||
std::map<std::string, std::map<std::string, std::string> >::iterator itr2; | ||
for (itr2 = _m_sections.begin(); itr2 != _m_sections.end(); itr2++) { | ||
fprintf(log_file, "[%s]\n", itr2->first.c_str()); | ||
for (itr1 = itr2->second.begin(); itr1 != itr2->second.end(); itr1++) { | ||
fprintf(log_file, "%s=%s\n", itr1->first.c_str(), itr1->second.c_str()); | ||
} | ||
} | ||
} | ||
|
||
std::string ConfigParser::read_header(const std::string &line) { | ||
|
||
if (line[line.size() - 1] != ']') | ||
throw std::runtime_error("Invalid section header: `" + line + "`"); | ||
return trim_copy(line.substr(1, line.size() - 2)); | ||
} | ||
|
||
void ConfigParser::read_configuration(const std::string &line, const std::string &header) { | ||
if (header == "") { | ||
std::string error = "No section provided for: `" + line + "`"; | ||
throw std::runtime_error(error); | ||
} | ||
|
||
if (line.find('=') == std::string::npos) { | ||
std::string error = "Invalid configuration: `" + line + "`"; | ||
throw std::runtime_error(error); | ||
} | ||
|
||
std::istringstream iss(line); | ||
std::string key; | ||
std::string val; | ||
std::getline(iss, key, '='); | ||
|
||
if (key.size() == 0) { | ||
std::string error = "No key found in configuration: `" + line + "`"; | ||
throw std::runtime_error(error); | ||
} | ||
|
||
std::getline(iss, val); | ||
|
||
_m_sections[header][trim_copy(key)] = trim_copy(val); | ||
} | ||
|
||
// trim from start (in place) | ||
void ConfigParser::ltrim(std::string &s) { | ||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { | ||
return !std::isspace(ch); | ||
})); | ||
} | ||
|
||
// trim from end (in place) | ||
void ConfigParser::rtrim(std::string &s) { | ||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { | ||
return !std::isspace(ch); | ||
}).base(), s.end()); | ||
} | ||
|
||
// trim from both ends (in place) | ||
void ConfigParser::trim(std::string &s) { | ||
ltrim(s); | ||
rtrim(s); | ||
} | ||
|
||
// trim from start (copying) | ||
std::string ConfigParser::ltrim_copy(std::string s) { | ||
ltrim(s); | ||
return s; | ||
} | ||
|
||
// trim from end (copying) | ||
std::string ConfigParser::rtrim_copy(std::string s) { | ||
rtrim(s); | ||
return s; | ||
} | ||
|
||
// trim from both ends (copying) | ||
std::string ConfigParser::trim_copy(std::string s) { | ||
trim(s); | ||
return s; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/************************************************ | ||
* Author: MaybeShewill-CV | ||
* File: configParser.h | ||
* Date: 2019/10/10 上午10:39 | ||
************************************************/ | ||
|
||
#ifndef MNN_CONFIGPARSER_H | ||
#define MNN_CONFIGPARSER_H | ||
|
||
// Config parser | ||
|
||
#include <exception> | ||
#include <stdio.h> | ||
#include <string> | ||
#include <map> | ||
|
||
const extern int __CONFIG_BUFFER_SIZE; | ||
|
||
namespace beec { | ||
namespace config_parse_utils { | ||
|
||
class ConfigParser { | ||
|
||
using config_values = std::map<std::string, std::string>; | ||
public: | ||
explicit ConfigParser(const std::string& filename); | ||
|
||
~ConfigParser() = default; | ||
|
||
config_values get_section(const std::string& section_name) const; | ||
|
||
config_values operator[](const std::string& section_name) const; | ||
|
||
void dump(FILE* log_file); | ||
|
||
private: | ||
std::map<std::string, std::map<std::string, std::string> > _m_sections; | ||
|
||
std::string read_header(const std::string& line); | ||
|
||
void read_configuration(const std::string& line, const std::string& header); | ||
|
||
// trim from start (in place) | ||
void ltrim(std::string &s); | ||
|
||
// trim from end (in place) | ||
void rtrim(std::string &s); | ||
|
||
// trim from both ends (in place) | ||
void trim(std::string &s); | ||
|
||
// trim from start (copying) | ||
std::string ltrim_copy(std::string s); | ||
|
||
// trim from end (copying) | ||
std::string rtrim_copy(std::string s); | ||
|
||
// trim from both ends (copying) | ||
std::string trim_copy(std::string s); | ||
}; | ||
} | ||
} | ||
|
||
#endif //MNN_CONFIGPARSER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/************************************************ | ||
* Author: MaybeShewill-CV | ||
* File: lanenetModel.cpp | ||
* Date: 2019/11/5 下午5:19 | ||
************************************************/ | ||
|
||
#include "lanenet_model.h" | ||
|
||
#include <glog/logging.h> | ||
|
||
#include <AutoTime.hpp> | ||
|
||
namespace lane_detection { | ||
/******************Public Function Sets***************/ | ||
|
||
/*** | ||
* | ||
* @param config | ||
*/ | ||
LaneNet::LaneNet(const beec::config_parse_utils::ConfigParser &config) { | ||
using config_content = std::map<std::string, std::string>; | ||
|
||
config_content config_section; | ||
try { | ||
config_section = config["LaneNet"]; | ||
} catch (const std::out_of_range& e) { | ||
LOG(ERROR) << e.what(); | ||
LOG(ERROR) << "Can not get LaneNet section content in config file, please check again"; | ||
_m_successfully_initialized = false; | ||
return; | ||
} | ||
|
||
if (config_section.find("model_file_path") == config_section.end()) { | ||
LOG(ERROR) << "Can not find \"model_file_path\" field in config section"; | ||
_m_successfully_initialized = false; | ||
return; | ||
} else { | ||
_m_lanenet_model_file_path = config_section["model_file_path"]; | ||
} | ||
|
||
_m_lanenet_model = std::unique_ptr<MNN::Interpreter>(MNN::Interpreter::createFromFile( | ||
_m_lanenet_model_file_path.c_str())); | ||
if (nullptr == _m_lanenet_model) { | ||
LOG(ERROR) << "Construct lanenet mnn interpreter failed"; | ||
_m_successfully_initialized = false; | ||
return; | ||
} | ||
|
||
MNN::ScheduleConfig mnn_config; | ||
mnn_config.type = MNN_FORWARD_CPU; | ||
mnn_config.numThread = 4; | ||
|
||
MNN::BackendConfig backend_config; | ||
backend_config.precision = MNN::BackendConfig::Precision_High; | ||
backend_config.power = MNN::BackendConfig::Power_High; | ||
mnn_config.backendConfig = &backend_config; | ||
|
||
_m_lanenet_session = _m_lanenet_model->createSession(mnn_config); | ||
if (nullptr == _m_lanenet_session) { | ||
LOG(ERROR) << "Construct laneNet mnn session failed"; | ||
_m_successfully_initialized = false; | ||
return; | ||
} | ||
|
||
std::string input_node_name = "lanenet/input_tensor"; | ||
std::string pix_embedding_output_name = "lanenet/final_pixel_embedding_output"; | ||
std::string binary_output_name = "lanenet/final_binary_output"; | ||
_m_input_tensor_host = _m_lanenet_model->getSessionInput( | ||
_m_lanenet_session, input_node_name.c_str()); | ||
_m_binary_output_tensor_host = _m_lanenet_model->getSessionOutput( | ||
_m_lanenet_session, binary_output_name.c_str()); | ||
_m_pix_embedding_output_tensor_host = _m_lanenet_model->getSessionOutput( | ||
_m_lanenet_session, pix_embedding_output_name.c_str()); | ||
_m_input_node_size_host.width = _m_input_tensor_host->width(); | ||
_m_input_node_size_host.height = _m_input_tensor_host->height(); | ||
|
||
for (auto i : _m_binary_output_tensor_host->shape()) { | ||
LOG(INFO) << i; | ||
} | ||
|
||
_m_successfully_initialized = true; | ||
return; | ||
} | ||
|
||
/*** | ||
* Destructor | ||
*/ | ||
LaneNet::~LaneNet() { | ||
_m_lanenet_model->releaseModel(); | ||
_m_lanenet_model->releaseSession(_m_lanenet_session); | ||
} | ||
|
||
/*** | ||
* Detect lanes on image using lanenet model | ||
* @param input_image | ||
* @param binary_seg_result | ||
* @param pix_embedding_result | ||
*/ | ||
void LaneNet::detect(const cv::Mat &input_image, cv::Mat &binary_seg_result, cv::Mat &pix_embedding_result) { | ||
|
||
// preprocess | ||
cv::Mat input_image_copy; | ||
input_image.copyTo(input_image_copy); | ||
{ | ||
AUTOTIME | ||
preprocess(input_image, input_image_copy); | ||
} | ||
|
||
// run session | ||
MNN::Tensor input_tensor_user(_m_input_tensor_host, MNN::Tensor::DimensionType::TENSORFLOW); | ||
{ | ||
AUTOTIME | ||
auto input_tensor_user_data = input_tensor_user.host<float>(); | ||
auto input_tensor_user_size = input_tensor_user.size(); | ||
::mempcpy(input_tensor_user_data, input_image_copy.data, input_tensor_user_size); | ||
|
||
_m_input_tensor_host->copyFromHostTensor(&input_tensor_user); | ||
_m_lanenet_model->runSession(_m_lanenet_session); | ||
} | ||
|
||
// output graph node | ||
MNN::Tensor binary_output_tensor_user( | ||
_m_binary_output_tensor_host, MNN::Tensor::DimensionType::TENSORFLOW); | ||
MNN::Tensor pix_embedding_output_tensor_user( | ||
_m_pix_embedding_output_tensor_host, MNN::Tensor::DimensionType::TENSORFLOW); | ||
_m_binary_output_tensor_host->copyToHostTensor(&binary_output_tensor_user); | ||
_m_pix_embedding_output_tensor_host->copyToHostTensor(&pix_embedding_output_tensor_user); | ||
|
||
auto binary_output_data = binary_output_tensor_user.host<float>(); | ||
cv::Mat binary_output_mat(_m_input_node_size_host, CV_32FC1, binary_output_data); | ||
binary_output_mat *= 255; | ||
binary_output_mat.convertTo(binary_seg_result, CV_8UC1); | ||
|
||
auto pix_embedding_output_data = pix_embedding_output_tensor_user.host<float>(); | ||
cv::Mat pix_embedding_output_mat( | ||
_m_input_node_size_host, CV_32FC4, pix_embedding_output_data); | ||
pix_embedding_output_mat.convertTo(pix_embedding_result, CV_8UC4); | ||
} | ||
|
||
/***************Private Function Sets*******************/ | ||
|
||
/*** | ||
* Resize image and scale image into [-1.0, 1.0] | ||
* @param input_image | ||
* @param output_image | ||
*/ | ||
void LaneNet::preprocess(const cv::Mat &input_image, cv::Mat& output_image) { | ||
|
||
if (input_image.type() != CV_32FC3) { | ||
input_image.convertTo(output_image, CV_32FC3); | ||
} | ||
|
||
if (output_image.size() != _m_input_node_size_host) { | ||
cv::resize(output_image, output_image, _m_input_node_size_host); | ||
} | ||
|
||
cv::divide(output_image, cv::Scalar(127.5, 127.5, 127.5), output_image); | ||
cv::subtract(output_image, cv::Scalar(1.0, 1.0, 1.0), output_image); | ||
|
||
return; | ||
} | ||
} |
Oops, something went wrong.