feat: initial HSAP platform
Huaxu Sentinel Active Safety Platform with embedded algorithm code, Docker Compose setup, and vendored dataset scaffolds for clone-and-run. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from configs.semantic_segmentation.common.datasets._utils import CITYSCAPES_ROOT as base
|
||||
|
||||
|
||||
def traverse(images_dir, data_list):
|
||||
for city in sorted(os.listdir(images_dir)):
|
||||
city_path = os.path.join(images_dir, city)
|
||||
for image in sorted(os.listdir(city_path)):
|
||||
temp = city + '/' + image.split('_leftImg8bit')[0] + '\n'
|
||||
data_list.append(temp)
|
||||
|
||||
|
||||
# Traverse images
|
||||
train_list = []
|
||||
val_list = []
|
||||
test_list = []
|
||||
traverse(os.path.join(base, "leftImg8bit/train"), train_list)
|
||||
traverse(os.path.join(base, "leftImg8bit/val"), val_list)
|
||||
traverse(os.path.join(base, "leftImg8bit/test"), test_list)
|
||||
print('Whole training set size: ' + str(len(train_list)))
|
||||
print('Whole validation set size: ' + str(len(val_list)))
|
||||
print('Whole test set size: ' + str(len(test_list)))
|
||||
|
||||
# Save training list
|
||||
lists_dir = os.path.join(base, "data_lists")
|
||||
if not os.path.exists(lists_dir):
|
||||
os.makedirs(lists_dir)
|
||||
with open(os.path.join(lists_dir, "train.txt"), "w") as f:
|
||||
f.writelines(train_list)
|
||||
with open(os.path.join(lists_dir, "val.txt"), "w") as f:
|
||||
f.writelines(val_list)
|
||||
with open(os.path.join(lists_dir, "test.txt"), "w") as f:
|
||||
f.writelines(test_list)
|
||||
print("Complete.")
|
||||
@@ -0,0 +1,49 @@
|
||||
PROJECT_NAME:= evaluate
|
||||
|
||||
# config ----------------------------------
|
||||
|
||||
INCLUDE_DIRS := include
|
||||
LIBRARY_DIRS := lib
|
||||
|
||||
COMMON_FLAGS := -DCPU_ONLY
|
||||
CXXFLAGS := -std=c++11 -fopenmp
|
||||
LDFLAGS := -fopenmp -Wl,-rpath,./lib
|
||||
|
||||
BUILD_DIR := build
|
||||
|
||||
# make rules -------------------------------
|
||||
CXX ?= g++
|
||||
BUILD_DIR ?= ./build
|
||||
|
||||
LIBRARIES += opencv_core opencv_highgui opencv_imgproc
|
||||
|
||||
# Comment Line 21 if using opencv2
|
||||
LIBRARIES += opencv_imgcodecs
|
||||
|
||||
CXXFLAGS += $(COMMON_FLAGS) $(foreach includedir,$(INCLUDE_DIRS),-I$(includedir))
|
||||
LDFLAGS += $(COMMON_FLAGS) $(foreach includedir,$(LIBRARY_DIRS),-L$(includedir)) $(foreach library,$(LIBRARIES),-l$(library))
|
||||
SRC_DIRS += $(shell find * -type d -exec bash -c "find {} -maxdepth 1 \( -name '*.cpp' -o -name '*.proto' \) | grep -q ." \; -print)
|
||||
CXX_SRCS += $(shell find src/ -name "*.cpp")
|
||||
CXX_TARGETS:=$(patsubst %.cpp, $(BUILD_DIR)/%.o, $(CXX_SRCS))
|
||||
ALL_BUILD_DIRS := $(sort $(BUILD_DIR) $(addprefix $(BUILD_DIR)/, $(SRC_DIRS)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(PROJECT_NAME)
|
||||
|
||||
.PHONY: $(ALL_BUILD_DIRS)
|
||||
$(ALL_BUILD_DIRS):
|
||||
@mkdir -p $@
|
||||
|
||||
$(BUILD_DIR)/%.o: %.cpp | $(ALL_BUILD_DIRS)
|
||||
@echo "CXX" $<
|
||||
@$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
$(PROJECT_NAME): $(CXX_TARGETS)
|
||||
@echo "CXX/LD" $@
|
||||
@$(CXX) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(CXX_TARGETS)
|
||||
@rm -rf $(PROJECT_NAME)
|
||||
@rm -rf $(BUILD_DIR)
|
||||
@@ -0,0 +1,46 @@
|
||||
# Accumulate and calculate whole F1 score on CULane
|
||||
|
||||
import argparse
|
||||
import fcntl
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Settings
|
||||
parser = argparse.ArgumentParser(description='PyTorch 1.6.0')
|
||||
parser.add_argument('--exp-name', type=str, default='',
|
||||
help='Name of experiment')
|
||||
parser.add_argument('--save-dir', type=str, help='Path prefix to save full res.')
|
||||
args = parser.parse_args()
|
||||
|
||||
filename = 'output/' + args.exp_name + '_iou0.5_split.txt'
|
||||
with open(filename, 'r') as f:
|
||||
temp = f.readlines()
|
||||
|
||||
# Count
|
||||
tp = 0
|
||||
fp = 0
|
||||
fn = 0
|
||||
for i in range(9):
|
||||
line = temp[i * 6 + 1].replace('\n', '').split(' ')
|
||||
tp += int(line[1])
|
||||
fp += int(line[3])
|
||||
fn += int(line[5])
|
||||
|
||||
# Calculate
|
||||
precision = tp / (tp + fp)
|
||||
recall = tp / (tp + fn)
|
||||
f1 = 2 * precision * recall / (precision + recall) * 100
|
||||
|
||||
# Log
|
||||
res_str = '\nF1 score: {}\nPrecision: {}\nRecall: {}\n'.format(f1, precision * 100, recall * 100)
|
||||
print(res_str)
|
||||
with open('../../log.txt', 'a') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
f.write(args.exp_name + ': ' + str(f1) + '\n')
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
if args.save_dir is not None:
|
||||
import os
|
||||
save_dir = os.path.join('../../', args.save_dir, args.exp_name)
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
with open(os.path.join(save_dir, 'test_result.txt'), 'a') as f:
|
||||
f.write('Total:' + res_str)
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
root=../../
|
||||
data_dir=../../../../dataset/culane/
|
||||
exp=$1
|
||||
detect_dir=../../output/
|
||||
|
||||
# These can not be changed
|
||||
w_lane=30;
|
||||
iou=0.5; # Set iou to 0.3 or 0.5
|
||||
im_w=1640
|
||||
im_h=590
|
||||
frame=1
|
||||
list0=${data_dir}list/test_split/test0_normal.txt
|
||||
list1=${data_dir}list/test_split/test1_crowd.txt
|
||||
list2=${data_dir}list/test_split/test2_hlight.txt
|
||||
list3=${data_dir}list/test_split/test3_shadow.txt
|
||||
list4=${data_dir}list/test_split/test4_noline.txt
|
||||
list5=${data_dir}list/test_split/test5_arrow.txt
|
||||
list6=${data_dir}list/test_split/test6_curve.txt
|
||||
list7=${data_dir}list/test_split/test7_cross.txt
|
||||
list8=${data_dir}list/test_split/test8_night.txt
|
||||
out0=./output/out0_normal.txt
|
||||
out1=./output/out1_crowd.txt
|
||||
out2=./output/out2_hlight.txt
|
||||
out3=./output/out3_shadow.txt
|
||||
out4=./output/out4_noline.txt
|
||||
out5=./output/out5_arrow.txt
|
||||
out6=./output/out6_curve.txt
|
||||
out7=./output/out7_cross.txt
|
||||
out8=./output/out8_night.txt
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list0 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out0
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list1 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out1
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list2 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out2
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list3 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out3
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list4 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out4
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list5 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out5
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list6 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out6
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list7 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out7
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list8 -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out8
|
||||
cat ./output/out*.txt>./output/${exp}_iou${iou}_split.txt
|
||||
|
||||
if ! [ -z "$2" ]
|
||||
then
|
||||
mkdir -p ../../${2}/${1}
|
||||
cp ./output/${exp}_iou${iou}_split.txt ../../${2}/${1}/test_result.txt
|
||||
fi
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
root=../../
|
||||
data_dir=../../../../dataset/culane/
|
||||
exp=$1
|
||||
detect_dir=../../output/
|
||||
|
||||
# These can not be changed
|
||||
w_lane=30;
|
||||
iou=0.5; # Set iou to 0.3 or 0.5
|
||||
im_w=1640
|
||||
im_h=590
|
||||
frame=1
|
||||
list=${data_dir}list/val.txt
|
||||
out=./output/${exp}_iou${iou}_validation.txt
|
||||
./evaluate -a $data_dir -d $detect_dir -i $data_dir -l $list -w $w_lane -t $iou -c $im_w -r $im_h -f $frame -o $out
|
||||
|
||||
if ! [ -z "$2" ]
|
||||
then
|
||||
mkdir -p ../../${2}/${1}
|
||||
cp ${out} ../../${2}/${1}/val_result.txt
|
||||
fi
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
#ifndef COUNTER_HPP
|
||||
#define COUNTER_HPP
|
||||
|
||||
#include "lane_compare.hpp"
|
||||
#include "hungarianGraph.hpp"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// before coming to use functions of this class, the lanes should resize to im_width and im_height using resize_lane() in lane_compare.hpp
|
||||
class Counter
|
||||
{
|
||||
public:
|
||||
Counter(int _im_width, int _im_height, double _iou_threshold=0.4, int _lane_width=10):tp(0),fp(0),fn(0){
|
||||
im_width = _im_width;
|
||||
im_height = _im_height;
|
||||
sim_threshold = _iou_threshold;
|
||||
lane_compare = new LaneCompare(_im_width, _im_height, _lane_width, LaneCompare::IOU);
|
||||
};
|
||||
double get_precision(void);
|
||||
double get_recall(void);
|
||||
long getTP(void);
|
||||
long getFP(void);
|
||||
long getFN(void);
|
||||
void setTP(long);
|
||||
void setFP(long);
|
||||
void setFN(long);
|
||||
// direct add tp, fp, tn and fn
|
||||
// first match with hungarian
|
||||
tuple<vector<int>, long, long, long, long> count_im_pair(const vector<vector<Point2f> > &anno_lanes, const vector<vector<Point2f> > &detect_lanes);
|
||||
void makeMatch(const vector<vector<double> > &similarity, vector<int> &match1, vector<int> &match2);
|
||||
|
||||
private:
|
||||
double sim_threshold;
|
||||
int im_width;
|
||||
int im_height;
|
||||
long tp;
|
||||
long fp;
|
||||
long fn;
|
||||
LaneCompare *lane_compare;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef HUNGARIAN_GRAPH_HPP
|
||||
#define HUNGARIAN_GRAPH_HPP
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
struct pipartiteGraph {
|
||||
vector<vector<double> > mat;
|
||||
vector<bool> leftUsed, rightUsed;
|
||||
vector<double> leftWeight, rightWeight;
|
||||
vector<int>rightMatch, leftMatch;
|
||||
int leftNum, rightNum;
|
||||
bool matchDfs(int u) {
|
||||
leftUsed[u] = true;
|
||||
for (int v = 0; v < rightNum; v++) {
|
||||
if (!rightUsed[v] && fabs(leftWeight[u] + rightWeight[v] - mat[u][v]) < 1e-2) {
|
||||
rightUsed[v] = true;
|
||||
if (rightMatch[v] == -1 || matchDfs(rightMatch[v])) {
|
||||
rightMatch[v] = u;
|
||||
leftMatch[u] = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void resize(int leftNum, int rightNum) {
|
||||
this->leftNum = leftNum;
|
||||
this->rightNum = rightNum;
|
||||
leftMatch.resize(leftNum);
|
||||
rightMatch.resize(rightNum);
|
||||
leftUsed.resize(leftNum);
|
||||
rightUsed.resize(rightNum);
|
||||
leftWeight.resize(leftNum);
|
||||
rightWeight.resize(rightNum);
|
||||
mat.resize(leftNum);
|
||||
for (int i = 0; i < leftNum; i++) mat[i].resize(rightNum);
|
||||
}
|
||||
void match() {
|
||||
for (int i = 0; i < leftNum; i++) leftMatch[i] = -1;
|
||||
for (int i = 0; i < rightNum; i++) rightMatch[i] = -1;
|
||||
for (int i = 0; i < rightNum; i++) rightWeight[i] = 0;
|
||||
for (int i = 0; i < leftNum; i++) {
|
||||
leftWeight[i] = -1e5;
|
||||
for (int j = 0; j < rightNum; j++) {
|
||||
if (leftWeight[i] < mat[i][j]) leftWeight[i] = mat[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for (int u = 0; u < leftNum; u++) {
|
||||
while (1) {
|
||||
for (int i = 0; i < leftNum; i++) leftUsed[i] = false;
|
||||
for (int i = 0; i < rightNum; i++) rightUsed[i] = false;
|
||||
if (matchDfs(u)) break;
|
||||
double d = 1e10;
|
||||
for (int i = 0; i < leftNum; i++) {
|
||||
if (leftUsed[i] ) {
|
||||
for (int j = 0; j < rightNum; j++) {
|
||||
if (!rightUsed[j]) d = min(d, leftWeight[i] + rightWeight[j] - mat[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d == 1e10) return ;
|
||||
for (int i = 0; i < leftNum; i++) if (leftUsed[i]) leftWeight[i] -= d;
|
||||
for (int i = 0; i < rightNum; i++) if (rightUsed[i]) rightWeight[i] += d;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // HUNGARIAN_GRAPH_HPP
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef LANE_COMPARE_HPP
|
||||
#define LANE_COMPARE_HPP
|
||||
|
||||
#include "spline.hpp"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
class LaneCompare{
|
||||
public:
|
||||
enum CompareMode{
|
||||
IOU,
|
||||
Caltech
|
||||
};
|
||||
|
||||
LaneCompare(int _im_width, int _im_height, int _lane_width = 10, CompareMode _compare_mode = IOU){
|
||||
im_width = _im_width;
|
||||
im_height = _im_height;
|
||||
compare_mode = _compare_mode;
|
||||
lane_width = _lane_width;
|
||||
}
|
||||
|
||||
double get_lane_similarity(const vector<Point2f> &lane1, const vector<Point2f> &lane2);
|
||||
void resize_lane(vector<Point2f> &curr_lane, int curr_width, int curr_height);
|
||||
private:
|
||||
CompareMode compare_mode;
|
||||
int im_width;
|
||||
int im_height;
|
||||
int lane_width;
|
||||
Spline splineSolver;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef SPLINE_HPP
|
||||
#define SPLINE_HPP
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <math.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
struct Func {
|
||||
double a_x;
|
||||
double b_x;
|
||||
double c_x;
|
||||
double d_x;
|
||||
double a_y;
|
||||
double b_y;
|
||||
double c_y;
|
||||
double d_y;
|
||||
double h;
|
||||
};
|
||||
class Spline {
|
||||
public:
|
||||
vector<Point2f> splineInterpTimes(const vector<Point2f> &tmp_line, int times);
|
||||
vector<Point2f> splineInterpStep(vector<Point2f> tmp_line, double step);
|
||||
vector<Func> cal_fun(const vector<Point2f> &point_v);
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
/*************************************************************************
|
||||
> File Name: counter.cpp
|
||||
> Author: Xingang Pan, Jun Li
|
||||
> Mail: px117@ie.cuhk.edu.hk
|
||||
> Created Time: Thu Jul 14 20:23:08 2016
|
||||
************************************************************************/
|
||||
|
||||
#include "counter.hpp"
|
||||
|
||||
double Counter::get_precision(void)
|
||||
{
|
||||
cerr<<"tp: "<<tp<<" fp: "<<fp<<" fn: "<<fn<<endl;
|
||||
if(tp+fp == 0)
|
||||
{
|
||||
cerr<<"no positive detection"<<endl;
|
||||
return -1;
|
||||
}
|
||||
return tp/double(tp + fp);
|
||||
}
|
||||
|
||||
double Counter::get_recall(void)
|
||||
{
|
||||
if(tp+fn == 0)
|
||||
{
|
||||
cerr<<"no ground truth positive"<<endl;
|
||||
return -1;
|
||||
}
|
||||
return tp/double(tp + fn);
|
||||
}
|
||||
|
||||
long Counter::getTP(void)
|
||||
{
|
||||
return tp;
|
||||
}
|
||||
|
||||
long Counter::getFP(void)
|
||||
{
|
||||
return fp;
|
||||
}
|
||||
|
||||
long Counter::getFN(void)
|
||||
{
|
||||
return fn;
|
||||
}
|
||||
|
||||
void Counter::setTP(long value)
|
||||
{
|
||||
tp = value;
|
||||
}
|
||||
|
||||
void Counter::setFP(long value)
|
||||
{
|
||||
fp = value;
|
||||
}
|
||||
|
||||
void Counter::setFN(long value)
|
||||
{
|
||||
fn = value;
|
||||
}
|
||||
|
||||
tuple<vector<int>, long, long, long, long> Counter::count_im_pair(const vector<vector<Point2f> > &anno_lanes, const vector<vector<Point2f> > &detect_lanes)
|
||||
{
|
||||
vector<int> anno_match(anno_lanes.size(), -1);
|
||||
vector<int> detect_match;
|
||||
if(anno_lanes.empty())
|
||||
{
|
||||
return make_tuple(anno_match, 0, detect_lanes.size(), 0, 0);
|
||||
}
|
||||
|
||||
if(detect_lanes.empty())
|
||||
{
|
||||
return make_tuple(anno_match, 0, 0, 0, anno_lanes.size());
|
||||
}
|
||||
// hungarian match first
|
||||
|
||||
// first calc similarity matrix
|
||||
vector<vector<double> > similarity(anno_lanes.size(), vector<double>(detect_lanes.size(), 0));
|
||||
for(int i=0; i<anno_lanes.size(); i++)
|
||||
{
|
||||
const vector<Point2f> &curr_anno_lane = anno_lanes[i];
|
||||
for(int j=0; j<detect_lanes.size(); j++)
|
||||
{
|
||||
const vector<Point2f> &curr_detect_lane = detect_lanes[j];
|
||||
similarity[i][j] = lane_compare->get_lane_similarity(curr_anno_lane, curr_detect_lane);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
makeMatch(similarity, anno_match, detect_match);
|
||||
|
||||
|
||||
int curr_tp = 0;
|
||||
// count and add
|
||||
for(int i=0; i<anno_lanes.size(); i++)
|
||||
{
|
||||
if(anno_match[i]>=0 && similarity[i][anno_match[i]] > sim_threshold)
|
||||
{
|
||||
curr_tp++;
|
||||
}
|
||||
else
|
||||
{
|
||||
anno_match[i] = -1;
|
||||
}
|
||||
}
|
||||
int curr_fn = anno_lanes.size() - curr_tp;
|
||||
int curr_fp = detect_lanes.size() - curr_tp;
|
||||
return make_tuple(anno_match, curr_tp, curr_fp, 0, curr_fn);
|
||||
}
|
||||
|
||||
|
||||
void Counter::makeMatch(const vector<vector<double> > &similarity, vector<int> &match1, vector<int> &match2) {
|
||||
int m = similarity.size();
|
||||
int n = similarity[0].size();
|
||||
pipartiteGraph gra;
|
||||
bool have_exchange = false;
|
||||
if (m > n) {
|
||||
have_exchange = true;
|
||||
swap(m, n);
|
||||
}
|
||||
gra.resize(m, n);
|
||||
for (int i = 0; i < gra.leftNum; i++) {
|
||||
for (int j = 0; j < gra.rightNum; j++) {
|
||||
if(have_exchange)
|
||||
gra.mat[i][j] = similarity[j][i];
|
||||
else
|
||||
gra.mat[i][j] = similarity[i][j];
|
||||
}
|
||||
}
|
||||
gra.match();
|
||||
match1 = gra.leftMatch;
|
||||
match2 = gra.rightMatch;
|
||||
if (have_exchange) swap(match1, match2);
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*************************************************************************
|
||||
> File Name: evaluate.cpp
|
||||
> Author: Xingang Pan, Jun Li
|
||||
> Mail: px117@ie.cuhk.edu.hk
|
||||
> Created Time: 2016年07月14日 星期四 18时28分45秒
|
||||
************************************************************************/
|
||||
|
||||
#include "counter.hpp"
|
||||
#include "spline.hpp"
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
void help(void)
|
||||
{
|
||||
cout<<"./evaluate [OPTIONS]"<<endl;
|
||||
cout<<"-h : print usage help"<<endl;
|
||||
cout<<"-a : directory for annotation files (default: /data/driving/eval_data/anno_label/)"<<endl;
|
||||
cout<<"-d : directory for detection files (default: /data/driving/eval_data/predict_label/)"<<endl;
|
||||
cout<<"-i : directory for image files (default: /data/driving/eval_data/img/)"<<endl;
|
||||
cout<<"-l : list of images used for evaluation (default: /data/driving/eval_data/img/all.txt)"<<endl;
|
||||
cout<<"-w : width of the lanes (default: 10)"<<endl;
|
||||
cout<<"-t : threshold of iou (default: 0.4)"<<endl;
|
||||
cout<<"-c : cols (max image width) (default: 1920)"<<endl;
|
||||
cout<<"-r : rows (max image height) (default: 1080)"<<endl;
|
||||
cout<<"-s : show visualization"<<endl;
|
||||
cout<<"-f : start frame in the test set (default: 1)"<<endl;
|
||||
}
|
||||
|
||||
|
||||
void read_lane_file(const string &file_name, vector<vector<Point2f> > &lanes);
|
||||
void visualize(string &full_im_name, vector<vector<Point2f> > &anno_lanes, vector<vector<Point2f> > &detect_lanes, vector<int> anno_match, int width_lane);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// process params
|
||||
string anno_dir = "/data/driving/eval_data/anno_label/";
|
||||
string detect_dir = "/data/driving/eval_data/predict_label/";
|
||||
string im_dir = "/data/driving/eval_data/img/";
|
||||
string list_im_file = "/data/driving/eval_data/img/all.txt";
|
||||
string output_file = "./output.txt";
|
||||
int width_lane = 10;
|
||||
double iou_threshold = 0.4;
|
||||
int im_width = 1920;
|
||||
int im_height = 1080;
|
||||
int oc;
|
||||
bool show = false;
|
||||
int frame = 1;
|
||||
while((oc = getopt(argc, argv, "ha:d:i:l:w:t:c:r:sf:o:")) != -1)
|
||||
{
|
||||
switch(oc)
|
||||
{
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
case 'a':
|
||||
anno_dir = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
detect_dir = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
im_dir = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
list_im_file = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
width_lane = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
iou_threshold = atof(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
im_width = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
im_height = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
show = true;
|
||||
break;
|
||||
case 'f':
|
||||
frame = atoi(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
output_file = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cout<<"------------Configuration---------"<<endl;
|
||||
cout<<"anno_dir: "<<anno_dir<<endl;
|
||||
cout<<"detect_dir: "<<detect_dir<<endl;
|
||||
cout<<"im_dir: "<<im_dir<<endl;
|
||||
cout<<"list_im_file: "<<list_im_file<<endl;
|
||||
cout<<"width_lane: "<<width_lane<<endl;
|
||||
cout<<"iou_threshold: "<<iou_threshold<<endl;
|
||||
cout<<"im_width: "<<im_width<<endl;
|
||||
cout<<"im_height: "<<im_height<<endl;
|
||||
cout<<"-----------------------------------"<<endl;
|
||||
cout<<"Evaluating the results..."<<endl;
|
||||
// this is the max_width and max_height
|
||||
|
||||
if(width_lane<1)
|
||||
{
|
||||
cerr<<"width_lane must be positive"<<endl;
|
||||
help();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ifstream ifs_im_list(list_im_file, ios::in);
|
||||
if(ifs_im_list.fail())
|
||||
{
|
||||
cerr<<"Error: file "<<list_im_file<<" not exist!"<<endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Counter counter(im_width, im_height, iou_threshold, width_lane);
|
||||
|
||||
vector<int> anno_match;
|
||||
string sub_im_name;
|
||||
// pre-load filelist
|
||||
vector<string> filelists;
|
||||
while (getline(ifs_im_list, sub_im_name)) {
|
||||
filelists.push_back(sub_im_name);
|
||||
}
|
||||
ifs_im_list.close();
|
||||
|
||||
vector<tuple<vector<int>, long, long, long, long>> tuple_lists;
|
||||
tuple_lists.resize(filelists.size());
|
||||
|
||||
#pragma omp parallel for
|
||||
for (size_t i = 0; i < filelists.size(); i++)
|
||||
{
|
||||
auto sub_im_name = filelists[i];
|
||||
string full_im_name = im_dir + sub_im_name;
|
||||
string sub_txt_name = sub_im_name.substr(0, sub_im_name.find_last_of(".")) + ".lines.txt";
|
||||
string anno_file_name = anno_dir + sub_txt_name;
|
||||
string detect_file_name = detect_dir + sub_txt_name;
|
||||
vector<vector<Point2f> > anno_lanes;
|
||||
vector<vector<Point2f> > detect_lanes;
|
||||
read_lane_file(anno_file_name, anno_lanes);
|
||||
read_lane_file(detect_file_name, detect_lanes);
|
||||
//cerr<<count<<": "<<full_im_name<<endl;
|
||||
tuple_lists[i] = counter.count_im_pair(anno_lanes, detect_lanes);
|
||||
if (show)
|
||||
{
|
||||
auto anno_match = get<0>(tuple_lists[i]);
|
||||
visualize(full_im_name, anno_lanes, detect_lanes, anno_match, width_lane);
|
||||
waitKey(0);
|
||||
}
|
||||
}
|
||||
|
||||
long tp = 0, fp = 0, tn = 0, fn = 0;
|
||||
for (auto result: tuple_lists) {
|
||||
tp += get<1>(result);
|
||||
fp += get<2>(result);
|
||||
// tn = get<3>(result);
|
||||
fn += get<4>(result);
|
||||
}
|
||||
counter.setTP(tp);
|
||||
counter.setFP(fp);
|
||||
counter.setFN(fn);
|
||||
|
||||
double precision = counter.get_precision();
|
||||
double recall = counter.get_recall();
|
||||
double F = 2 * precision * recall / (precision + recall);
|
||||
cerr<<"finished process file"<<endl;
|
||||
cout<<"precision: "<<precision<<endl;
|
||||
cout<<"recall: "<<recall<<endl;
|
||||
cout<<"Fmeasure: "<<F<<endl;
|
||||
cout<<"----------------------------------"<<endl;
|
||||
|
||||
ofstream ofs_out_file;
|
||||
ofs_out_file.open(output_file, ios::out);
|
||||
ofs_out_file<<"file: "<<output_file<<endl;
|
||||
ofs_out_file<<"tp: "<<counter.getTP()<<" fp: "<<counter.getFP()<<" fn: "<<counter.getFN()<<endl;
|
||||
ofs_out_file<<"precision: "<<precision<<endl;
|
||||
ofs_out_file<<"recall: "<<recall<<endl;
|
||||
ofs_out_file<<"Fmeasure: "<<F<<endl<<endl;
|
||||
ofs_out_file.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void read_lane_file(const string &file_name, vector<vector<Point2f> > &lanes)
|
||||
{
|
||||
lanes.clear();
|
||||
ifstream ifs_lane(file_name, ios::in);
|
||||
if(ifs_lane.fail())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string str_line;
|
||||
while(getline(ifs_lane, str_line))
|
||||
{
|
||||
vector<Point2f> curr_lane;
|
||||
stringstream ss;
|
||||
ss<<str_line;
|
||||
double x,y;
|
||||
while(ss>>x>>y)
|
||||
{
|
||||
curr_lane.push_back(Point2f(x, y));
|
||||
}
|
||||
lanes.push_back(curr_lane);
|
||||
}
|
||||
|
||||
ifs_lane.close();
|
||||
}
|
||||
|
||||
void visualize(string &full_im_name, vector<vector<Point2f> > &anno_lanes, vector<vector<Point2f> > &detect_lanes, vector<int> anno_match, int width_lane)
|
||||
{
|
||||
Mat img = imread(full_im_name, 1);
|
||||
Mat img2 = imread(full_im_name, 1);
|
||||
vector<Point2f> curr_lane;
|
||||
vector<Point2f> p_interp;
|
||||
Spline splineSolver;
|
||||
Scalar color_B = Scalar(255, 0, 0);
|
||||
Scalar color_G = Scalar(0, 255, 0);
|
||||
Scalar color_R = Scalar(0, 0, 255);
|
||||
Scalar color_P = Scalar(255, 0, 255);
|
||||
Scalar color;
|
||||
for (int i=0; i<anno_lanes.size(); i++)
|
||||
{
|
||||
curr_lane = anno_lanes[i];
|
||||
if(curr_lane.size() == 2)
|
||||
{
|
||||
p_interp = curr_lane;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_interp = splineSolver.splineInterpTimes(curr_lane, 50);
|
||||
}
|
||||
if (anno_match[i] >= 0)
|
||||
{
|
||||
color = color_G;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = color_G;
|
||||
}
|
||||
for (int n=0; n<p_interp.size()-1; n++)
|
||||
{
|
||||
line(img, p_interp[n], p_interp[n+1], color, width_lane);
|
||||
line(img2, p_interp[n], p_interp[n+1], color, 2);
|
||||
}
|
||||
}
|
||||
bool detected;
|
||||
for (int i=0; i<detect_lanes.size(); i++)
|
||||
{
|
||||
detected = false;
|
||||
curr_lane = detect_lanes[i];
|
||||
if(curr_lane.size() == 2)
|
||||
{
|
||||
p_interp = curr_lane;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_interp = splineSolver.splineInterpTimes(curr_lane, 50);
|
||||
}
|
||||
for (int n=0; n<anno_lanes.size(); n++)
|
||||
{
|
||||
if (anno_match[n] == i)
|
||||
{
|
||||
detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (detected == true)
|
||||
{
|
||||
color = color_B;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = color_R;
|
||||
}
|
||||
for (int n=0; n<p_interp.size()-1; n++)
|
||||
{
|
||||
line(img, p_interp[n], p_interp[n+1], color, width_lane);
|
||||
line(img2, p_interp[n], p_interp[n+1], color, 2);
|
||||
}
|
||||
}
|
||||
namedWindow("visualize", 1);
|
||||
imshow("visualize", img);
|
||||
namedWindow("visualize2", 1);
|
||||
imshow("visualize2", img2);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************
|
||||
> File Name: lane_compare.cpp
|
||||
> Author: Xingang Pan, Jun Li
|
||||
> Mail: px117@ie.cuhk.edu.hk
|
||||
> Created Time: Fri Jul 15 10:26:32 2016
|
||||
************************************************************************/
|
||||
|
||||
#include "lane_compare.hpp"
|
||||
|
||||
double LaneCompare::get_lane_similarity(const vector<Point2f> &lane1, const vector<Point2f> &lane2)
|
||||
{
|
||||
if(lane1.size()<2 || lane2.size()<2)
|
||||
{
|
||||
cerr<<"lane size must be greater or equal to 2"<<endl;
|
||||
return 0;
|
||||
}
|
||||
Mat im1 = Mat::zeros(im_height, im_width, CV_8UC1);
|
||||
Mat im2 = Mat::zeros(im_height, im_width, CV_8UC1);
|
||||
// draw lines on im1 and im2
|
||||
vector<Point2f> p_interp1;
|
||||
vector<Point2f> p_interp2;
|
||||
if(lane1.size() == 2)
|
||||
{
|
||||
p_interp1 = lane1;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_interp1 = splineSolver.splineInterpTimes(lane1, 50);
|
||||
}
|
||||
|
||||
if(lane2.size() == 2)
|
||||
{
|
||||
p_interp2 = lane2;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_interp2 = splineSolver.splineInterpTimes(lane2, 50);
|
||||
}
|
||||
|
||||
Scalar color_white = Scalar(1);
|
||||
for(int n=0; n<p_interp1.size()-1; n++)
|
||||
{
|
||||
line(im1, p_interp1[n], p_interp1[n+1], color_white, lane_width);
|
||||
}
|
||||
for(int n=0; n<p_interp2.size()-1; n++)
|
||||
{
|
||||
line(im2, p_interp2[n], p_interp2[n+1], color_white, lane_width);
|
||||
}
|
||||
|
||||
double sum_1 = cv::sum(im1).val[0];
|
||||
double sum_2 = cv::sum(im2).val[0];
|
||||
double inter_sum = cv::sum(im1.mul(im2)).val[0];
|
||||
double union_sum = sum_1 + sum_2 - inter_sum;
|
||||
double iou = inter_sum / union_sum;
|
||||
return iou;
|
||||
}
|
||||
|
||||
|
||||
// resize the lane from Size(curr_width, curr_height) to Size(im_width, im_height)
|
||||
void LaneCompare::resize_lane(vector<Point2f> &curr_lane, int curr_width, int curr_height)
|
||||
{
|
||||
if(curr_width == im_width && curr_height == im_height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
double x_scale = im_width/(double)curr_width;
|
||||
double y_scale = im_height/(double)curr_height;
|
||||
for(int n=0; n<curr_lane.size(); n++)
|
||||
{
|
||||
curr_lane[n] = Point2f(curr_lane[n].x*x_scale, curr_lane[n].y*y_scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "spline.hpp"
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
vector<Point2f> Spline::splineInterpTimes(const vector<Point2f>& tmp_line, int times) {
|
||||
vector<Point2f> res;
|
||||
|
||||
if(tmp_line.size() == 2) {
|
||||
double x1 = tmp_line[0].x;
|
||||
double y1 = tmp_line[0].y;
|
||||
double x2 = tmp_line[1].x;
|
||||
double y2 = tmp_line[1].y;
|
||||
|
||||
for (int k = 0; k <= times; k++) {
|
||||
double xi = x1 + double((x2 - x1) * k) / times;
|
||||
double yi = y1 + double((y2 - y1) * k) / times;
|
||||
res.push_back(Point2f(xi, yi));
|
||||
}
|
||||
}
|
||||
|
||||
else if(tmp_line.size() > 2)
|
||||
{
|
||||
vector<Func> tmp_func;
|
||||
tmp_func = this->cal_fun(tmp_line);
|
||||
if (tmp_func.empty()) {
|
||||
cout << "in splineInterpTimes: cal_fun failed" << endl;
|
||||
return res;
|
||||
}
|
||||
for(int j = 0; j < tmp_func.size(); j++)
|
||||
{
|
||||
double delta = tmp_func[j].h / times;
|
||||
for(int k = 0; k < times; k++)
|
||||
{
|
||||
double t1 = delta*k;
|
||||
double x1 = tmp_func[j].a_x + tmp_func[j].b_x*t1 + tmp_func[j].c_x*pow(t1,2) + tmp_func[j].d_x*pow(t1,3);
|
||||
double y1 = tmp_func[j].a_y + tmp_func[j].b_y*t1 + tmp_func[j].c_y*pow(t1,2) + tmp_func[j].d_y*pow(t1,3);
|
||||
res.push_back(Point2f(x1, y1));
|
||||
}
|
||||
}
|
||||
res.push_back(tmp_line[tmp_line.size() - 1]);
|
||||
}
|
||||
else {
|
||||
cerr << "in splineInterpTimes: not enough points" << endl;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
vector<Point2f> Spline::splineInterpStep(vector<Point2f> tmp_line, double step) {
|
||||
vector<Point2f> res;
|
||||
/*
|
||||
if (tmp_line.size() == 2) {
|
||||
double x1 = tmp_line[0].x;
|
||||
double y1 = tmp_line[0].y;
|
||||
double x2 = tmp_line[1].x;
|
||||
double y2 = tmp_line[1].y;
|
||||
|
||||
for (double yi = std::min(y1, y2); yi < std::max(y1, y2); yi += step) {
|
||||
double xi;
|
||||
if (yi == y1) xi = x1;
|
||||
else xi = (x2 - x1) / (y2 - y1) * (yi - y1) + x1;
|
||||
res.push_back(Point2f(xi, yi));
|
||||
}
|
||||
}*/
|
||||
if (tmp_line.size() == 2) {
|
||||
double x1 = tmp_line[0].x;
|
||||
double y1 = tmp_line[0].y;
|
||||
double x2 = tmp_line[1].x;
|
||||
double y2 = tmp_line[1].y;
|
||||
tmp_line[1].x = (x1 + x2) / 2;
|
||||
tmp_line[1].y = (y1 + y2) / 2;
|
||||
tmp_line.push_back(Point2f(x2, y2));
|
||||
}
|
||||
if (tmp_line.size() > 2) {
|
||||
vector<Func> tmp_func;
|
||||
tmp_func = this->cal_fun(tmp_line);
|
||||
double ystart = tmp_line[0].y;
|
||||
double yend = tmp_line[tmp_line.size() - 1].y;
|
||||
bool down;
|
||||
if (ystart < yend) down = 1;
|
||||
else down = 0;
|
||||
if (tmp_func.empty()) {
|
||||
cerr << "in splineInterpStep: cal_fun failed" << endl;
|
||||
}
|
||||
|
||||
for(int j = 0; j < tmp_func.size(); j++)
|
||||
{
|
||||
for(double t1 = 0; t1 < tmp_func[j].h; t1 += step)
|
||||
{
|
||||
double x1 = tmp_func[j].a_x + tmp_func[j].b_x*t1 + tmp_func[j].c_x*pow(t1,2) + tmp_func[j].d_x*pow(t1,3);
|
||||
double y1 = tmp_func[j].a_y + tmp_func[j].b_y*t1 + tmp_func[j].c_y*pow(t1,2) + tmp_func[j].d_y*pow(t1,3);
|
||||
res.push_back(Point2f(x1, y1));
|
||||
}
|
||||
}
|
||||
res.push_back(tmp_line[tmp_line.size() - 1]);
|
||||
}
|
||||
else {
|
||||
cerr << "in splineInterpStep: not enough points" << endl;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
vector<Func> Spline::cal_fun(const vector<Point2f> &point_v)
|
||||
{
|
||||
vector<Func> func_v;
|
||||
int n = point_v.size();
|
||||
if(n<=2) {
|
||||
cout << "in cal_fun: point number less than 3" << endl;
|
||||
return func_v;
|
||||
}
|
||||
|
||||
func_v.resize(point_v.size()-1);
|
||||
|
||||
vector<double> Mx(n);
|
||||
vector<double> My(n);
|
||||
vector<double> A(n-2);
|
||||
vector<double> B(n-2);
|
||||
vector<double> C(n-2);
|
||||
vector<double> Dx(n-2);
|
||||
vector<double> Dy(n-2);
|
||||
vector<double> h(n-1);
|
||||
//vector<func> func_v(n-1);
|
||||
|
||||
for(int i = 0; i < n-1; i++)
|
||||
{
|
||||
h[i] = sqrt(pow(point_v[i+1].x - point_v[i].x, 2) + pow(point_v[i+1].y - point_v[i].y, 2));
|
||||
}
|
||||
|
||||
for(int i = 0; i < n-2; i++)
|
||||
{
|
||||
A[i] = h[i];
|
||||
B[i] = 2*(h[i]+h[i+1]);
|
||||
C[i] = h[i+1];
|
||||
|
||||
Dx[i] = 6*( (point_v[i+2].x - point_v[i+1].x)/h[i+1] - (point_v[i+1].x - point_v[i].x)/h[i] );
|
||||
Dy[i] = 6*( (point_v[i+2].y - point_v[i+1].y)/h[i+1] - (point_v[i+1].y - point_v[i].y)/h[i] );
|
||||
}
|
||||
|
||||
//TDMA
|
||||
C[0] = C[0] / B[0];
|
||||
Dx[0] = Dx[0] / B[0];
|
||||
Dy[0] = Dy[0] / B[0];
|
||||
for(int i = 1; i < n-2; i++)
|
||||
{
|
||||
double tmp = B[i] - A[i]*C[i-1];
|
||||
C[i] = C[i] / tmp;
|
||||
Dx[i] = (Dx[i] - A[i]*Dx[i-1]) / tmp;
|
||||
Dy[i] = (Dy[i] - A[i]*Dy[i-1]) / tmp;
|
||||
}
|
||||
Mx[n-2] = Dx[n-3];
|
||||
My[n-2] = Dy[n-3];
|
||||
for(int i = n-4; i >= 0; i--)
|
||||
{
|
||||
Mx[i+1] = Dx[i] - C[i]*Mx[i+2];
|
||||
My[i+1] = Dy[i] - C[i]*My[i+2];
|
||||
}
|
||||
|
||||
Mx[0] = 0;
|
||||
Mx[n-1] = 0;
|
||||
My[0] = 0;
|
||||
My[n-1] = 0;
|
||||
|
||||
for(int i = 0; i < n-1; i++)
|
||||
{
|
||||
func_v[i].a_x = point_v[i].x;
|
||||
func_v[i].b_x = (point_v[i+1].x - point_v[i].x)/h[i] - (2*h[i]*Mx[i] + h[i]*Mx[i+1]) / 6;
|
||||
func_v[i].c_x = Mx[i]/2;
|
||||
func_v[i].d_x = (Mx[i+1] - Mx[i]) / (6*h[i]);
|
||||
|
||||
func_v[i].a_y = point_v[i].y;
|
||||
func_v[i].b_y = (point_v[i+1].y - point_v[i].y)/h[i] - (2*h[i]*My[i] + h[i]*My[i+1]) / 6;
|
||||
func_v[i].c_y = My[i]/2;
|
||||
func_v[i].d_y = (My[i+1] - My[i]) / (6*h[i]);
|
||||
|
||||
func_v[i].h = h[i];
|
||||
}
|
||||
return func_v;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# Accumulate and calculate whole F1 score on CULane
|
||||
|
||||
import argparse
|
||||
import fcntl
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Settings
|
||||
parser = argparse.ArgumentParser(description='PyTorch 1.6.0')
|
||||
parser.add_argument('--exp-name', type=str, default='',
|
||||
help='Name of experiment')
|
||||
parser.add_argument('--save-dir', type=str, help='Path prefix to save full res.')
|
||||
args = parser.parse_args()
|
||||
|
||||
filename = 'output/' + args.exp_name + '_iou0.5_split.txt'
|
||||
with open(filename, 'r') as f:
|
||||
temp = f.readlines()
|
||||
|
||||
# Count
|
||||
tp = 0
|
||||
fp = 0
|
||||
fn = 0
|
||||
for i in range(9):
|
||||
line = temp[i * 6 + 1].replace('\n', '').split(' ')
|
||||
tp += int(line[1])
|
||||
fp += int(line[3])
|
||||
fn += int(line[5])
|
||||
|
||||
# Calculate
|
||||
precision = tp / (tp + fp)
|
||||
recall = tp / (tp + fn)
|
||||
f1 = 2 * precision * recall / (precision + recall) * 100
|
||||
|
||||
# Log
|
||||
res_str = '\nF1 score: {}\nPrecision: {}\nRecall: {}\n'.format(f1, precision * 100, recall * 100)
|
||||
print(res_str)
|
||||
with open('../../log.txt', 'a') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
f.write(args.exp_name + ': ' + str(f1) + '\n')
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
if args.save_dir is not None:
|
||||
import os
|
||||
save_dir = os.path.join('../../', args.save_dir, args.exp_name)
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
with open(os.path.join(save_dir, 'test_result.txt'), 'a') as f:
|
||||
f.write('Total:' + res_str)
|
||||
@@ -0,0 +1,96 @@
|
||||
# Copied from Turoad/lanedet
|
||||
# Slightly differs from official metric, recommend using this only for visualization
|
||||
import cv2
|
||||
import numpy as np
|
||||
from scipy.interpolate import splprep, splev
|
||||
from scipy.optimize import linear_sum_assignment
|
||||
from shapely.geometry import LineString, Polygon
|
||||
try:
|
||||
from utils.common import warnings
|
||||
except ImportError:
|
||||
import warnings
|
||||
|
||||
|
||||
def draw_lane(lane, img=None, img_shape=None, width=30):
|
||||
if img is None:
|
||||
img = np.zeros(img_shape, dtype=np.uint8)
|
||||
lane = lane.astype(np.int32)
|
||||
for p1, p2 in zip(lane[:-1], lane[1:]):
|
||||
cv2.line(img, tuple(p1), tuple(p2), color=1, thickness=width)
|
||||
return img
|
||||
|
||||
|
||||
def discrete_cross_iou(xs, ys, width=30, img_shape=(590, 1640, 3)):
|
||||
xs = [draw_lane(lane, img_shape=img_shape[:2], width=width) for lane in xs]
|
||||
ys = [draw_lane(lane, img_shape=img_shape[:2], width=width) for lane in ys]
|
||||
|
||||
ious = np.zeros((len(xs), len(ys)))
|
||||
for i, x in enumerate(xs):
|
||||
for j, y in enumerate(ys):
|
||||
inter = (x * y).sum()
|
||||
ious[i, j] = inter / (x.sum() + y.sum() - inter)
|
||||
return ious
|
||||
|
||||
|
||||
def continuous_cross_iou(xs, ys, width=30, img_shape=(590, 1640, 3)):
|
||||
h, w, _ = img_shape
|
||||
image = Polygon([(0, 0), (0, h - 1), (w - 1, h - 1), (w - 1, 0)])
|
||||
xs = [LineString(lane).buffer(distance=width / 2., cap_style=1, join_style=2).intersection(image) for lane in xs]
|
||||
ys = [LineString(lane).buffer(distance=width / 2., cap_style=1, join_style=2).intersection(image) for lane in ys]
|
||||
|
||||
ious = np.zeros((len(xs), len(ys)))
|
||||
for i, x in enumerate(xs):
|
||||
for j, y in enumerate(ys):
|
||||
ious[i, j] = x.intersection(y).area / x.union(y).area
|
||||
|
||||
return ious
|
||||
|
||||
|
||||
def remove_consecutive_duplicates(x):
|
||||
"""Remove consecutive duplicates"""
|
||||
y = []
|
||||
for t in x:
|
||||
if len(y) > 0 and y[-1] == t:
|
||||
warnings.warn('Removed consecutive duplicate point ({}, {})!'.format(t[0], t[1]))
|
||||
continue
|
||||
y.append(t)
|
||||
return y
|
||||
|
||||
|
||||
def interp(points, n=50):
|
||||
if len(points) == 2:
|
||||
return np.array(points)
|
||||
|
||||
# Consecutive duplicates (can happen with parametric curves)
|
||||
# cause internal error for scipy's splprep:
|
||||
# https://stackoverflow.com/a/47949170/15449902
|
||||
points = remove_consecutive_duplicates(points)
|
||||
|
||||
x = [x for x, _ in points]
|
||||
y = [y for _, y in points]
|
||||
tck, u = splprep([x, y], s=0, t=n, k=min(3, len(points) - 1))
|
||||
|
||||
u = np.linspace(0., 1., num=(len(u) - 1) * n + 1)
|
||||
return np.array(splev(u, tck)).T
|
||||
|
||||
|
||||
def culane_metric(pred, anno, width=30, iou_threshold=0.5, official=True, img_shape=(590, 1640, 3)):
|
||||
if len(pred) == 0:
|
||||
return 0, 0, len(anno), np.zeros(len(pred)), np.zeros(len(pred), dtype=bool)
|
||||
if len(anno) == 0:
|
||||
return 0, len(pred), 0, np.zeros(len(pred)), np.zeros(len(pred), dtype=bool)
|
||||
interp_pred = [interp(pred_lane) for pred_lane in pred] # (4, 50, 2)
|
||||
interp_anno = [interp(anno_lane) for anno_lane in anno] # (4, 50, 2)
|
||||
|
||||
if official:
|
||||
ious = discrete_cross_iou(interp_pred, interp_anno, width=width, img_shape=img_shape)
|
||||
else:
|
||||
ious = continuous_cross_iou(interp_pred, interp_anno, width=width, img_shape=img_shape)
|
||||
|
||||
row_ind, col_ind = linear_sum_assignment(1 - ious)
|
||||
tp = int((ious[row_ind, col_ind] > iou_threshold).sum())
|
||||
fp = len(pred) - tp
|
||||
fn = len(anno) - tp
|
||||
pred_ious = np.zeros(len(pred))
|
||||
pred_ious[row_ind] = ious[row_ind, col_ind]
|
||||
return tp, fp, fn, pred_ious, pred_ious > iou_threshold
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
root=../../
|
||||
data_dir=../../../../dataset/culane/
|
||||
exp=$1
|
||||
detect_dir=../../output/
|
||||
|
||||
# These can not be changed
|
||||
w_lane=30;
|
||||
iou=0.5; # Set iou to 0.3 or 0.5
|
||||
im_w=1640
|
||||
im_h=590
|
||||
list0=${data_dir}list/test_split/test0_normal.txt
|
||||
list1=${data_dir}list/test_split/test1_crowd.txt
|
||||
list2=${data_dir}list/test_split/test2_hlight.txt
|
||||
list3=${data_dir}list/test_split/test3_shadow.txt
|
||||
list4=${data_dir}list/test_split/test4_noline.txt
|
||||
list5=${data_dir}list/test_split/test5_arrow.txt
|
||||
list6=${data_dir}list/test_split/test6_curve.txt
|
||||
list7=${data_dir}list/test_split/test7_cross.txt
|
||||
list8=${data_dir}list/test_split/test8_night.txt
|
||||
out0=./output/out0_normal.txt
|
||||
out1=./output/out1_crowd.txt
|
||||
out2=./output/out2_hlight.txt
|
||||
out3=./output/out3_shadow.txt
|
||||
out4=./output/out4_noline.txt
|
||||
out5=./output/out5_arrow.txt
|
||||
out6=./output/out6_curve.txt
|
||||
out7=./output/out7_cross.txt
|
||||
out8=./output/out8_night.txt
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list0 -w $w_lane -t $iou -c $im_w -r $im_h -o $out0
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list1 -w $w_lane -t $iou -c $im_w -r $im_h -o $out1
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list2 -w $w_lane -t $iou -c $im_w -r $im_h -o $out2
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list3 -w $w_lane -t $iou -c $im_w -r $im_h -o $out3
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list4 -w $w_lane -t $iou -c $im_w -r $im_h -o $out4
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list5 -w $w_lane -t $iou -c $im_w -r $im_h -o $out5
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list6 -w $w_lane -t $iou -c $im_w -r $im_h -o $out6
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list7 -w $w_lane -t $iou -c $im_w -r $im_h -o $out7
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list8 -w $w_lane -t $iou -c $im_w -r $im_h -o $out8
|
||||
cat ./output/out*.txt>./output/${exp}_iou${iou}_split.txt
|
||||
|
||||
if ! [ -z "$2" ]
|
||||
then
|
||||
mkdir -p ../../${2}/${1}
|
||||
cp ./output/${exp}_iou${iou}_split.txt ../../${2}/${1}/test_result.txt
|
||||
fi
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
root=../../
|
||||
data_dir=../../../../dataset/culane/
|
||||
exp=$1
|
||||
detect_dir=../../output/
|
||||
|
||||
# These can not be changed
|
||||
w_lane=30;
|
||||
iou=0.5; # Set iou to 0.3 or 0.5
|
||||
im_w=1640
|
||||
im_h=590
|
||||
list=${data_dir}list/val.txt
|
||||
out=./output/${exp}_iou${iou}_validation.txt
|
||||
python evaluate.py -a $data_dir -d $detect_dir -l $list -w $w_lane -t $iou -c $im_w -r $im_h -o $out
|
||||
|
||||
if ! [ -z "$2" ]
|
||||
then
|
||||
mkdir -p ../../${2}/${1}
|
||||
cp ${out} ../../${2}/${1}/val_result.txt
|
||||
fi
|
||||
@@ -0,0 +1,88 @@
|
||||
# modified from Turoad/lanedet to align with original Cpp scripts
|
||||
import os
|
||||
import argparse
|
||||
from functools import partial
|
||||
from p_tqdm import p_map
|
||||
|
||||
from culane_metric import culane_metric
|
||||
|
||||
|
||||
def load_culane_data_one(path):
|
||||
with open(path, 'r') as data_file:
|
||||
t = data_file.readlines()
|
||||
t = [line.split() for line in t]
|
||||
t = [list(map(float, lane)) for lane in t]
|
||||
t = [[(lane[i], lane[i + 1]) for i in range(0, len(lane), 2)] for lane in t]
|
||||
t = [lane for lane in t if len(lane) >= 2]
|
||||
|
||||
return t
|
||||
|
||||
|
||||
def load_culane_data(data_dir, file_list_path):
|
||||
with open(file_list_path, 'r') as file_list:
|
||||
filepaths = [
|
||||
os.path.join(data_dir, line[1 if line[0] == '/' else 0:].rstrip().replace('.jpg', '.lines.txt'))
|
||||
for line in file_list.readlines()
|
||||
]
|
||||
|
||||
data = []
|
||||
for path in filepaths:
|
||||
img_data = load_culane_data_one(path)
|
||||
data.append(img_data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def eval_predictions(args, official=True):
|
||||
print('List file: {}'.format(args.list_path))
|
||||
print('Width lane: {}'.format(args.width))
|
||||
print('IoU threshold: {}'.format(args.threshold))
|
||||
print('Image height: {}'.format(args.img_height))
|
||||
print('Image width: {}'.format(args.img_width))
|
||||
print('Loading prediction data...')
|
||||
predictions = load_culane_data(args.pred_dir, args.list_path)
|
||||
print('Loading annotation data...')
|
||||
annotations = load_culane_data(args.anno_dir, args.list_path)
|
||||
print('Calculating metric in parallel...')
|
||||
img_shape = (args.img_height, args.img_width, 3)
|
||||
results = p_map(partial(culane_metric, width=args.width, iou_threshold=args.threshold, official=official, img_shape=img_shape),
|
||||
predictions, annotations)
|
||||
total_tp = sum(tp for tp, _, _, _, _ in results)
|
||||
total_fp = sum(fp for _, fp, _, _, _ in results)
|
||||
total_fn = sum(fn for _, _, fn, _, _ in results)
|
||||
if total_tp == 0:
|
||||
precision = 0.0
|
||||
recall = 0.0
|
||||
f1 = 0.0
|
||||
else:
|
||||
precision = float(total_tp) / (total_tp + total_fp)
|
||||
recall = float(total_tp) / (total_tp + total_fn)
|
||||
f1 = 2 * precision * recall / (precision + recall)
|
||||
|
||||
return {'TP': total_tp, 'FP': total_fp, 'FN': total_fn, 'Precision': precision, 'Recall': recall, 'F1': f1}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Settings
|
||||
# retain original defaults
|
||||
# remove unused/not needed: -i -f -s
|
||||
parser = argparse.ArgumentParser(description='CULane test')
|
||||
parser.add_argument('-a', '--anno-dir', type=str, help='directory for annotation files', default='/data/driving/eval_data/anno_label/')
|
||||
parser.add_argument('-d', '--pred-dir', type=str, help='directory for detection files', default='/data/driving/eval_data/predict_label/')
|
||||
parser.add_argument('-l', '--list-path', type=str, help='directory for image files', default='/data/driving/eval_data/img/')
|
||||
parser.add_argument('-w', '--width', type=int, help='width of the lanes', default=10)
|
||||
parser.add_argument('-t', '--threshold', type=float, help='threshold of iou', default=0.4)
|
||||
parser.add_argument('-c', '--img-width', type=int, help='cols (max image width)', default=1920)
|
||||
parser.add_argument('-r', '--img-height', type=int, help='rows (max image height)', default=1080)
|
||||
parser.add_argument('-o', '--output-path', type=str, help='result txt output path', default='./output.txt')
|
||||
|
||||
_args = parser.parse_args()
|
||||
res = eval_predictions(_args)
|
||||
for k, v in res.items():
|
||||
print('{}: {:.6f}'.format(k, v) if type(v) == float else '{}: {}'.format(k, v))
|
||||
with open(_args.output_path, 'w') as f:
|
||||
f.write('file: {}\n'.format(_args.output_path))
|
||||
f.write('tp: {} fp: {} fn: {}\n'.format(res['TP'], res['FP'], res['FN']))
|
||||
f.write('precision: {:.6f}\n'.format(res['Precision']))
|
||||
f.write('recall: {:.6f}\n'.format(res['Recall']))
|
||||
f.write('Fmeasure: {:.6f}\n\n'.format(res['F1']))
|
||||
@@ -0,0 +1,25 @@
|
||||
# CULane (official lists)
|
||||
# /driver_23_30frame/05151649_0422.MP4/00000.jpg /laneseg_label_w16/driver_23_30frame/05151649_0422.MP4/00000.png 1 1 1 1 =>
|
||||
# /driver_23_30frame/05151649_0422.MP4/00000 1 1 1 1
|
||||
|
||||
import os
|
||||
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from configs.lane_detection.common.datasets._utils import CULANE_ROOT as base
|
||||
|
||||
root = os.path.join(base, 'lists')
|
||||
old_file_names = ['train_gt.txt', 'val_gt.txt', 'val.txt', 'test.txt']
|
||||
new_file_names = ['train.txt', 'valfast.txt', 'val.txt', 'test.txt']
|
||||
for i in range(len(old_file_names)):
|
||||
file_name = os.path.join(root, old_file_names[i])
|
||||
with open(file_name, 'r') as f:
|
||||
temp = f.readlines()
|
||||
for x in range(len(temp)):
|
||||
if new_file_names[i] == 'test.txt' or new_file_names[i] == 'val.txt':
|
||||
temp[x] = temp[x].replace('.jpg', '')[1:]
|
||||
else:
|
||||
temp[x] = temp[x][1: temp[x].find('.jpg')] + temp[x][temp[x].find('.png') + 4:]
|
||||
file_name = os.path.join(root, new_file_names[i])
|
||||
with open(file_name, 'w') as f:
|
||||
f.writelines(temp)
|
||||
@@ -0,0 +1,16 @@
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from configs.lane_detection.common.datasets._utils import TUSIMPLE_ROOT, CULANE_ROOT, LLAMAS_ROOT
|
||||
|
||||
|
||||
root_map = {
|
||||
'tusimple': TUSIMPLE_ROOT,
|
||||
'culane': CULANE_ROOT,
|
||||
'llamas': LLAMAS_ROOT
|
||||
}
|
||||
|
||||
size_map = {
|
||||
'tusimple': (720, 1280),
|
||||
'culane': (590, 1640),
|
||||
'llamas': (717, 1276)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
from loader import SimpleKPLoader
|
||||
from _utils import root_map, size_map
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from utils.curve_utils import BezierCurve as Bezier
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='PytorchAutoDrive Bezier curve GT generation')
|
||||
parser.add_argument('--dataset', type=str, default='culane')
|
||||
parser.add_argument('--image-set', type=str, default='train', help='train.txt/test.txt/val.txt/valfast.txt')
|
||||
parser.add_argument('--order', type=int, default=3, help='the order of curve')
|
||||
parser.add_argument('--norm', action='store_true', default=False, help='normalize coordinates')
|
||||
args = parser.parse_args()
|
||||
|
||||
root = root_map[args.dataset]
|
||||
image_size = size_map[args.dataset]
|
||||
order = args.order
|
||||
lane_interpolate = True if args.dataset == 'curvelanes' else False
|
||||
lkp = SimpleKPLoader(root=root, image_set=args.image_set, data_set=args.dataset, image_size=image_size, norm=False)
|
||||
keypoints = lkp.load_annotations()
|
||||
all_lanes_kps = []
|
||||
for kps in tqdm(keypoints.keys()):
|
||||
temp = []
|
||||
for kp in keypoints[kps]:
|
||||
if kp.shape[0] == 0:
|
||||
continue
|
||||
fcns = Bezier(order=order)
|
||||
fcns.get_control_points(kp[:, 0], kp[:, 1], interpolate=lane_interpolate)
|
||||
matrix = fcns.save_control_points()
|
||||
flatten = [round(p, 3) for sub_m in matrix for p in sub_m]
|
||||
temp.append(flatten)
|
||||
|
||||
formatted = {
|
||||
"raw_file": kps,
|
||||
"bezier_control_points": temp
|
||||
}
|
||||
all_lanes_kps.append(json.dumps(formatted))
|
||||
|
||||
dir_name = os.path.join(root, 'bezier_labels')
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
save_path = os.path.join(dir_name, args.image_set + "_" + str(args.order) + ".json")
|
||||
with open(save_path, 'w') as f:
|
||||
for lane in all_lanes_kps:
|
||||
print(lane, end="\n", file=f)
|
||||
@@ -0,0 +1,130 @@
|
||||
import os
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class SimpleKPLoader(object):
|
||||
def __init__(self, root, image_size, image_set='test', data_set='tusimple', norm=False):
|
||||
|
||||
self.image_set = image_set
|
||||
self.data_set = data_set
|
||||
self.root = root
|
||||
self.norm = norm
|
||||
self.image_height = image_size[0]
|
||||
self.image_width = image_size[-1]
|
||||
|
||||
if self.image_set == 'test' and data_set == 'llamas':
|
||||
raise ValueError
|
||||
|
||||
if data_set == 'tusimple':
|
||||
self.image_dir = root
|
||||
elif data_set == 'culane':
|
||||
self.image_dir = root
|
||||
self.annotations_suffix = '.lines.txt'
|
||||
elif data_set == 'curvelanes':
|
||||
self.image_dir = root
|
||||
self.annotations_suffix = '.lines.txt'
|
||||
elif data_set == 'llamas':
|
||||
self.image_dir = os.path.join(root, 'color_images')
|
||||
self.annotations_suffix = '.lines.txt'
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
self.splits_dir = os.path.join(root, 'lists')
|
||||
|
||||
def load_txt_path(self, dataset):
|
||||
split_f = os.path.join(self.splits_dir, self.image_set + '.txt')
|
||||
with open(split_f, "r") as f:
|
||||
contents = [x.strip() for x in f.readlines()]
|
||||
if self.image_set in ['test', 'val']:
|
||||
path_lists = [os.path.join(self.image_dir, x + self.annotations_suffix) for x in contents]
|
||||
elif self.image_set == 'train':
|
||||
if dataset == 'curvelanes':
|
||||
path_lists = [os.path.join(self.image_dir, x + self.annotations_suffix) for x in contents]
|
||||
else:
|
||||
path_lists = [os.path.join(self.image_dir, x[:x.find(' ')] +
|
||||
self.annotations_suffix) for x in contents]
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
return path_lists
|
||||
|
||||
def load_json(self):
|
||||
if self.image_set == 'test':
|
||||
json_name = [os.path.join(self.image_dir, 'test_label.json')]
|
||||
elif self.image_set == 'val':
|
||||
json_name = [os.path.join(self.image_dir, 'label_data_0531.json')]
|
||||
elif self.image_set == 'train':
|
||||
json_name = [os.path.join(self.image_dir, 'label_data_0313.json'),
|
||||
os.path.join(self.image_dir, 'label_data_0601.json')]
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
return json_name
|
||||
|
||||
def get_points_in_txtfile(self, file_path):
|
||||
coords = []
|
||||
with open(file_path, 'r') as f:
|
||||
for lane in f.readlines():
|
||||
lane = lane.split(' ')
|
||||
coord = []
|
||||
for i in range(0, len(lane) - 1, 2):
|
||||
if float(lane[i]) >= 0:
|
||||
coord.append([float(lane[i]), float(lane[i + 1])])
|
||||
coord = np.array(coord)
|
||||
if self.norm and len(coord) != 0:
|
||||
coord[:, 0] = coord[:, 0] / self.image_width
|
||||
coord[:, -1] = coord[:, -1] / self.image_height
|
||||
coords.append(coord)
|
||||
|
||||
return coords
|
||||
|
||||
def get_points_in_json(self, itm):
|
||||
lanes = itm['lanes']
|
||||
coords_list = []
|
||||
h_sample = itm['h_samples']
|
||||
for lane in lanes:
|
||||
coord = []
|
||||
for x, y in zip(lane, h_sample):
|
||||
if x >= 0:
|
||||
coord.append([float(x), float(y)])
|
||||
coord = np.array(coord)
|
||||
if self.norm and len(coord) != 0:
|
||||
coord[:, 0] = coord[:, 0] / self.image_width
|
||||
coord[:, -1] = coord[:, -1] / self.image_height
|
||||
coords_list.append(coord)
|
||||
|
||||
return coords_list
|
||||
|
||||
def load_annotations(self):
|
||||
print('Loading dataset...')
|
||||
coords = OrderedDict()
|
||||
if self.data_set in ['culane', 'llamas', 'curvelanes']:
|
||||
file_lists = self.load_txt_path(dataset=self.data_set)
|
||||
for f in tqdm(file_lists):
|
||||
coords[f[len(self.root) + 1:]] = self.get_points_in_txtfile(f)
|
||||
elif self.data_set == 'tusimple':
|
||||
jsonfiles = self.load_json()
|
||||
|
||||
results = []
|
||||
for jsonfile in jsonfiles:
|
||||
with open(jsonfile, 'r') as f:
|
||||
results += [json.loads(x.strip()) for x in f.readlines()]
|
||||
for lane_json in tqdm(results):
|
||||
coords[lane_json['raw_file']] = self.get_points_in_json(lane_json)
|
||||
else:
|
||||
raise ValueError
|
||||
print('Finished')
|
||||
|
||||
return coords
|
||||
|
||||
def concat_jsons(self, filenames):
|
||||
# Concat tusimple lists in jsons (actually only each line is json)
|
||||
results = []
|
||||
for filename in filenames:
|
||||
with open(filename, 'r') as f:
|
||||
results += [json.loads(x.strip()) for x in f.readlines()]
|
||||
|
||||
return results
|
||||
@@ -0,0 +1,180 @@
|
||||
import argparse
|
||||
import cv2
|
||||
import numpy as np
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
from tqdm import tqdm
|
||||
from tabulate import tabulate
|
||||
from scipy.spatial import distance
|
||||
|
||||
|
||||
def show_preds(pred, gt):
|
||||
img = np.zeros((720, 1280, 3), dtype=np.uint8)
|
||||
print(len(gt), 'gts and', len(pred), 'preds')
|
||||
for lane in gt:
|
||||
for p in lane:
|
||||
cv2.circle(img, tuple(map(int, p)), 5, thickness=-1, color=(255, 0, 255))
|
||||
for lane in pred:
|
||||
for p in lane:
|
||||
cv2.circle(img, tuple(map(int, p)), 4, thickness=-1, color=(0, 255, 0))
|
||||
cv2.imshow('img', img)
|
||||
cv2.waitKey(0)
|
||||
|
||||
|
||||
def area_distance(pred_x, pred_y, gt_x, gt_y, placeholder=np.nan):
|
||||
pred = np.vstack([pred_x, pred_y]).T
|
||||
gt = np.vstack([gt_x, gt_y]).T
|
||||
|
||||
# pred = pred[pred[:, 0] > 0][:3, :]
|
||||
# gt = gt[gt[:, 0] > 0][:5, :]
|
||||
|
||||
dist_matrix = distance.cdist(pred, gt, metric='euclidean')
|
||||
|
||||
dist = 0.5 * (np.min(dist_matrix, axis=0).sum() + np.min(dist_matrix, axis=1).sum())
|
||||
d = np.max(gt_y) - np.min(gt_y)
|
||||
if d == 0:
|
||||
d = 1.0
|
||||
dist /= d
|
||||
|
||||
return dist
|
||||
|
||||
|
||||
def area_metric(pred, gt, debug=None):
|
||||
pred = sorted(pred, key=lambda ps: abs(ps[0][0] - 720 / 2.))[:2]
|
||||
gt = sorted(gt, key=lambda ps: abs(ps[0][0] - 720 / 2.))[:2]
|
||||
if len(pred) == 0:
|
||||
return 0., 0., len(gt)
|
||||
line_dists = []
|
||||
fp = 0.
|
||||
matched = 0.
|
||||
gt_matches = [False] * len(gt)
|
||||
pred_matches = [False] * len(pred)
|
||||
pred_dists = [None] * len(pred)
|
||||
|
||||
distances = np.ones((len(gt), len(pred)), dtype=np.float32)
|
||||
for i_gt, gt_points in enumerate(gt):
|
||||
x_gts = [x for x, _ in gt_points]
|
||||
y_gts = [y for _, y in gt_points]
|
||||
for i_pred, pred_points in enumerate(pred):
|
||||
x_preds = [x for x, _ in pred_points]
|
||||
y_preds = [y for _, y in pred_points]
|
||||
distances[i_gt, i_pred] = area_distance(x_preds, y_preds, x_gts, y_gts)
|
||||
|
||||
best_preds = np.argmin(distances, axis=1)
|
||||
best_gts = np.argmin(distances, axis=0)
|
||||
fp = 0.
|
||||
fn = 0.
|
||||
dist = 0.
|
||||
is_fp = []
|
||||
is_fn = []
|
||||
for i_pred, best_gt in enumerate(best_gts):
|
||||
if best_preds[best_gt] == i_pred:
|
||||
dist += distances[best_gt, i_pred]
|
||||
is_fp.append(False)
|
||||
else:
|
||||
fp += 1
|
||||
is_fp.append(True)
|
||||
for i_gt, best_pred in enumerate(best_preds):
|
||||
if best_gts[best_pred] != i_gt:
|
||||
fn += 1
|
||||
is_fn.append(True)
|
||||
else:
|
||||
is_fn.append(False)
|
||||
if debug:
|
||||
print('is fp')
|
||||
print(is_fp)
|
||||
print('is fn')
|
||||
print(is_fn)
|
||||
print('distances')
|
||||
dists = np.min(distances, axis=0)
|
||||
dists[np.array(is_fp)] = 0
|
||||
print(dists)
|
||||
show_preds(pred, gt)
|
||||
|
||||
return dist, fp, fn
|
||||
|
||||
|
||||
def convert_tusimple_format(json_gt):
|
||||
output = []
|
||||
for data in json_gt:
|
||||
lanes = [[(x, y) for (x, y) in zip(lane, data['h_samples']) if x >= 0] for lane in data['lanes']
|
||||
if any(x > 0 for x in lane)]
|
||||
output.append({
|
||||
'raw_file': data['raw_file'],
|
||||
'run_time': data['run_time'] if 'run_time' in data else None,
|
||||
'lanes': lanes
|
||||
})
|
||||
return output
|
||||
|
||||
|
||||
def eval_json(pred_file, gt_file, json_type=None, debug=False):
|
||||
try:
|
||||
json_pred = [json.loads(line) for line in open(pred_file).readlines()]
|
||||
except BaseException as e:
|
||||
raise Exception('Fail to load json file of the prediction.')
|
||||
json_gt = [json.loads(line) for line in open(gt_file).readlines()]
|
||||
if len(json_gt) != len(json_pred):
|
||||
raise Exception('We do not get the predictions of all the test tasks')
|
||||
|
||||
if json_type == 'tusimple':
|
||||
for gt, pred in zip(json_gt, json_pred):
|
||||
pred['h_samples'] = gt['h_samples']
|
||||
json_gt = convert_tusimple_format(json_gt)
|
||||
json_pred = convert_tusimple_format(json_pred)
|
||||
gts = {l['raw_file']: l for l in json_gt}
|
||||
|
||||
total_distance, total_fp, total_fn, run_time = 0., 0., 0., 0.
|
||||
for pred in tqdm(json_pred):
|
||||
if 'raw_file' not in pred or 'lanes' not in pred:
|
||||
raise Exception('raw_file or lanes not in some predictions.')
|
||||
raw_file = pred['raw_file']
|
||||
pred_lanes = pred['lanes']
|
||||
run_time += pred['run_time'] if 'run_time' in pred else 1.
|
||||
|
||||
if raw_file not in gts:
|
||||
raise Exception('Some raw_file from your predictions do not exist in the test tasks.')
|
||||
gt = gts[raw_file]
|
||||
gt_lanes = gt['lanes']
|
||||
|
||||
distance, fp, fn = area_metric(pred_lanes, gt_lanes, debug=debug)
|
||||
|
||||
total_distance += distance
|
||||
total_fp += fp
|
||||
total_fn += fn
|
||||
|
||||
num = len(gts)
|
||||
return json.dumps([{
|
||||
'name': 'Distance',
|
||||
'value': total_distance / num,
|
||||
'order': 'desc'
|
||||
}, {
|
||||
'name': 'FP',
|
||||
'value': total_fp,
|
||||
'order': 'asc'
|
||||
}, {
|
||||
'name': 'FN',
|
||||
'value': total_fn,
|
||||
'order': 'asc'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="PytorchAutoDrive curve utils")
|
||||
parser.add_argument('--preds', required=True, type=str, help=".json with the predictions")
|
||||
parser.add_argument('--gt', required=True, type=str, help=".json with the GT")
|
||||
parser.add_argument('--gt-type', type=str, help='pass `tusimple` if using the TuSimple file format')
|
||||
parser.add_argument('--debug', action='store_true', help='show metrics and preds/gts')
|
||||
argv = vars(parser.parse_args())
|
||||
|
||||
result = json.loads(eval_json(argv['preds'], argv['gt'], argv['gt_type'], argv['debug']))
|
||||
|
||||
# pretty-print
|
||||
table = {}
|
||||
for metric in result:
|
||||
if metric['name'] not in table.keys():
|
||||
table[metric['name']] = []
|
||||
table[metric['name']].append(metric['value'])
|
||||
print(tabulate(table, headers='keys'))
|
||||
@@ -0,0 +1,122 @@
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
from loader import SimpleKPLoader
|
||||
from _utils import root_map, size_map
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from utils.curve_utils import BezierCurve as Bezier, Polynomial as Poly
|
||||
from utils.common import warnings
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='PytorchAutoDrive curve utils')
|
||||
parser.add_argument('--dataset', type=str, default='culane')
|
||||
parser.add_argument('--state', type=int, default=1, help='1: test set/2: val test')
|
||||
parser.add_argument('--fit-function', type=str, default='bezier', help='bezier/poly')
|
||||
parser.add_argument('--num-points', type=int, default=50, help='the number of sampled points')
|
||||
parser.add_argument('--order', type=int, default=2)
|
||||
parser.add_argument('--norm', action='store_true', default=False, help='normalize coordinates')
|
||||
args = parser.parse_args()
|
||||
|
||||
root = root_map[args.dataset]
|
||||
image_size = size_map[args.dataset]
|
||||
bezier_threshold = 5
|
||||
image_set = 'test' if args.state == 1 else 'val'
|
||||
if args.dataset == 'llamas' and image_set != 'val':
|
||||
warnings.warn('LLAMAS test labels not available! Switching to validation set!')
|
||||
image_set = 'val'
|
||||
order = args.order
|
||||
lkp = SimpleKPLoader(root=root, image_set=image_set, data_set=args.dataset, image_size=image_size, norm=args.norm)
|
||||
lane_interpolate = True if args.dataset == 'curvelanes' else False
|
||||
keypoints = lkp.load_annotations()
|
||||
all_lanes = []
|
||||
for kps in tqdm(keypoints.keys()):
|
||||
coordinates = []
|
||||
for kp in keypoints[kps]:
|
||||
|
||||
if args.fit_function == 'bezier':
|
||||
fcns = Bezier(order=order)
|
||||
fcns.get_control_points(kp[:, 0], kp[:, 1], interpolate=lane_interpolate)
|
||||
elif args.fit_function == 'poly':
|
||||
fcns = Poly(order=order)
|
||||
fcns.poly_fit(kp[:, 0], kp[:, 1], interpolate=lane_interpolate)
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
if args.dataset == 'tusimple':
|
||||
|
||||
temp = []
|
||||
if args.fit_function == 'bezier':
|
||||
h_samples = [(160 + y * 10) for y in range(56)]
|
||||
sampled_points = fcns.get_sample_point(n=args.num_points,
|
||||
image_size=image_size if args.norm else None)
|
||||
for h_sample in h_samples:
|
||||
dis = np.abs(h_sample - sampled_points[:, 1])
|
||||
idx = np.argmin(dis)
|
||||
if dis[idx] > bezier_threshold:
|
||||
temp.append(-2)
|
||||
else:
|
||||
temp.append(round(sampled_points[:, 0][idx], 3))
|
||||
coordinates.append(temp)
|
||||
elif args.fit_function == 'poly':
|
||||
if args.norm:
|
||||
h_samples = [(160 + y * 10) / image_size[0] for y in range(56)]
|
||||
else:
|
||||
h_samples = [(160 + y * 10) for y in range(56)]
|
||||
# sampled_points = fcns.get_sample_point(kp[:, 1])
|
||||
start_y = kp[:, 1][0]
|
||||
end_y = kp[:, 1][-1]
|
||||
for h_sample in h_samples:
|
||||
if h_sample < start_y:
|
||||
temp.append(-2)
|
||||
elif h_sample >= start_y and h_sample <= end_y:
|
||||
temp.append(
|
||||
round(fcns.compute_x_based_y(h_sample, image_size=image_size if args.norm else None),
|
||||
3))
|
||||
elif h_sample > end_y:
|
||||
temp.append(-2)
|
||||
coordinates.append(temp)
|
||||
else:
|
||||
raise ValueError
|
||||
else:
|
||||
if args.fit_function == 'bezier':
|
||||
coordinates.append(
|
||||
fcns.get_sample_point(n=args.num_points, image_size=image_size if args.norm else None))
|
||||
elif args.fit_function == 'poly':
|
||||
coordinates.append(fcns.get_sample_point(kp[:, 1], image_size=image_size if args.norm else None))
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
# save the result
|
||||
if args.dataset in ['culane', 'llamas', 'curvelanes']:
|
||||
|
||||
filepath = os.path.join('./output', kps)
|
||||
if args.dataset == 'llamas':
|
||||
filepath = filepath.replace('/color_images', '')
|
||||
filepath = filepath.replace('_color_rect', '')
|
||||
|
||||
dir_name = filepath[:filepath.rfind('/')]
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
with open(filepath, "w") as f:
|
||||
for coordinate in coordinates:
|
||||
for j in range(len(coordinate)):
|
||||
print("{} {}".format(round(coordinate[j][0], 3), round(coordinate[j][1]), 3), end=" ", file=f)
|
||||
print(file=f)
|
||||
|
||||
elif args.dataset == 'tusimple':
|
||||
formatted = {
|
||||
"h_samples": [160 + y * 10 for y in range(56)],
|
||||
"lanes": coordinates,
|
||||
"run_time": 0,
|
||||
"raw_file": kps
|
||||
}
|
||||
all_lanes.append(json.dumps(formatted))
|
||||
|
||||
if args.dataset == 'tusimple':
|
||||
with open('./output/upperbound_' + args.fit_function + '_' + str(args.order) + '.json', 'w') as f:
|
||||
for lane in all_lanes:
|
||||
print(lane, end="\n", file=f)
|
||||
@@ -0,0 +1,48 @@
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from utils.models.common_models import DCN_v2_Ref
|
||||
|
||||
import torch
|
||||
# from fvcore.nn import FlopCountAnalysis
|
||||
from thop import profile
|
||||
|
||||
|
||||
def thop_count(x, y, f):
|
||||
macs, params = profile(f, inputs=(x, y))
|
||||
|
||||
return macs * 2, params
|
||||
|
||||
|
||||
# def fvcore_count(x, y, f):
|
||||
# flops = FlopCountAnalysis(f, (x, y))
|
||||
|
||||
# return flops.total() * 2, 0
|
||||
|
||||
|
||||
H = 360
|
||||
W = 640
|
||||
C = 256
|
||||
OS = 16
|
||||
fH = (H - 1) // OS + 1
|
||||
fW = (W - 1) // OS + 1
|
||||
|
||||
inputs1 = torch.ones(1, C, fH, fW).cuda()
|
||||
inputs2 = torch.ones(1, C, (H - 1) // OS + 1, (W - 1) // OS + 1).cuda()
|
||||
# model = SimpleFlip_2D(channels=256).cuda()
|
||||
model = DCN_v2_Ref(C, C, kernel_size=(3, 3), padding=1).cuda()
|
||||
model.eval()
|
||||
print(thop_count(inputs1, inputs2, model)) # Also doubly counts sigmoid (damn the activations)
|
||||
# print(fvcore_count(inputs1, inputs2, model))
|
||||
|
||||
|
||||
# Only modulated_deform_conv2d() is ignored
|
||||
# conv Flops = 2 x k^2 x Cin x Cout x W x H
|
||||
# dcn Flops = + W x H x k^2 x 13 + W x H x k^2 x 9 x Cin (bilinear interpolate)
|
||||
# or just: ->0 + W x H x k^2 x 9 x Cin (3 weighted plus)
|
||||
# ->0 coordinates: W x H x k^2 x 13
|
||||
# dcnv2: ++ W x H x k^2 x Cin
|
||||
# ~ (additional flops on bias is not considered):
|
||||
standard_conv_flops = fW * fH * (3 * 3) * C * C
|
||||
ignored_dcnv2_flops = 10 * fW * fH * (3 * 3) * C + fW * fH * (3 * 3) * 13
|
||||
print('DCN_v2_Ref ignored flops by thop:')
|
||||
print(standard_conv_flops + ignored_dcnv2_flops)
|
||||
@@ -0,0 +1,17 @@
|
||||
# This script generates Cityscapes official demos from downloaded demo images
|
||||
import os
|
||||
from utils.frames_to_video import frames2video
|
||||
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from configs.semantic_segmentation.common.datasets._utils import CITYSCAPES_ROOT
|
||||
base = os.path.join(CITYSCAPES_ROOT, 'all_demoVideo', 'leftImg8bit', 'demoVideo')
|
||||
|
||||
# Get image file list
|
||||
sub_dirs = sorted(os.listdir(base))
|
||||
|
||||
for sub_dir in sub_dirs:
|
||||
filenames = sorted(os.listdir(os.path.join(base, sub_dir)))
|
||||
|
||||
# Save video
|
||||
frames2video(os.path.join(base, sub_dir), sub_dir + '.avi', filenames, fps=17)
|
||||
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from configs.semantic_segmentation.common.datasets._utils import GTAV_ROOT as base
|
||||
|
||||
|
||||
# Pad with 0
|
||||
def pad(x):
|
||||
zero = '0'
|
||||
length = len(x)
|
||||
if length < 5:
|
||||
x = zero * (5 - length) + x
|
||||
x += '\n'
|
||||
|
||||
return x
|
||||
|
||||
|
||||
# Count
|
||||
start = 1
|
||||
end = 24966
|
||||
train_list = [pad(str(x)) for x in range(start, end + 1)]
|
||||
print('Whole training set size: ' + str(len(train_list)))
|
||||
|
||||
# Save training list
|
||||
lists_dir = os.path.join(base, "data_lists")
|
||||
if not os.path.exists(lists_dir):
|
||||
os.makedirs(lists_dir)
|
||||
with open(os.path.join(lists_dir, "train.txt"), "w") as f:
|
||||
f.writelines(train_list)
|
||||
print("Complete.")
|
||||
@@ -0,0 +1,247 @@
|
||||
"""
|
||||
Copied from https://github.com/karstenBehrendt/unsupervised_llamas/blob/master/culane_metric/evaluate.py
|
||||
Evaluation script for the CULane metric on the LLAMAS dataset.
|
||||
This script will compute the F1, precision and recall metrics as described in the CULane benchmark.
|
||||
The predictions format is the same one used in the CULane benchmark.
|
||||
In summary, for every annotation file:
|
||||
labels/a/b/c.json
|
||||
There should be a prediction file:
|
||||
predictions/a/b/c.lines.txt
|
||||
Inside each .lines.txt file each line will contain a sequence of points (x, y) separated by spaces.
|
||||
For more information, please see https://xingangpan.github.io/projects/CULane.html
|
||||
This script uses two methods to compute the IoU: one using an image to draw the lanes (named `discrete` here) and
|
||||
another one that uses shapes with the shapely library (named `continuous` here). The results achieved with the first
|
||||
method are very close to the official CULane implementation. Although the second should be a more exact method and is
|
||||
faster to compute, it deviates more from the official implementation. By default, the method closer to the official
|
||||
metric is used.
|
||||
"""
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import cv2
|
||||
import fcntl
|
||||
import ujson as json
|
||||
import numpy as np
|
||||
from p_tqdm import t_map, p_map
|
||||
from functools import partial
|
||||
from scipy.interpolate import splprep, splev
|
||||
from scipy.optimize import linear_sum_assignment
|
||||
from shapely.geometry import LineString, Polygon
|
||||
from llamas_official_scripts import get_files_from_folder, get_horizontal_values_for_four_lanes, get_label_base
|
||||
|
||||
|
||||
LLAMAS_IMG_RES = [717, 1276]
|
||||
IMAGE_HEIGHT, IMAGE_WIDTH = LLAMAS_IMG_RES[0], LLAMAS_IMG_RES[1]
|
||||
|
||||
|
||||
def add_ys(xs):
|
||||
"""For each x in xs, make a tuple with x and its corresponding y."""
|
||||
xs = np.array(xs[300:])
|
||||
valid = xs >= 0
|
||||
xs = xs[valid]
|
||||
assert len(xs) > 1
|
||||
ys = np.arange(300, 717)[valid]
|
||||
return list(zip(xs, ys))
|
||||
|
||||
|
||||
def draw_lane(lane, img=None, img_shape=None, width=30):
|
||||
"""Draw a lane (a list of points) on an image by drawing a line with width `width` through each
|
||||
pair of points i and i+i"""
|
||||
if img is None:
|
||||
img = np.zeros(img_shape, dtype=np.uint8)
|
||||
lane = lane.astype(np.int32)
|
||||
for p1, p2 in zip(lane[:-1], lane[1:]):
|
||||
cv2.line(img, tuple(p1), tuple(p2), color=(1,), thickness=width)
|
||||
return img
|
||||
|
||||
|
||||
def discrete_cross_iou(xs, ys, width=30, img_shape=LLAMAS_IMG_RES):
|
||||
"""For each lane in xs, compute its Intersection Over Union (IoU) with each lane in ys by drawing the lanes on
|
||||
an image"""
|
||||
xs = [draw_lane(lane, img_shape=img_shape, width=width) > 0 for lane in xs]
|
||||
ys = [draw_lane(lane, img_shape=img_shape, width=width) > 0 for lane in ys]
|
||||
|
||||
ious = np.zeros((len(xs), len(ys)))
|
||||
for i, x in enumerate(xs):
|
||||
for j, y in enumerate(ys):
|
||||
# IoU by the definition: sum all intersections (binary and) and divide by the sum of the union (binary or)
|
||||
ious[i, j] = (x & y).sum() / (x | y).sum()
|
||||
return ious
|
||||
|
||||
|
||||
def continuous_cross_iou(xs, ys, width=30):
|
||||
"""For each lane in xs, compute its Intersection Over Union (IoU) with each lane in ys using the area between each
|
||||
pair of points"""
|
||||
h, w = IMAGE_HEIGHT, IMAGE_WIDTH
|
||||
image = Polygon([(0, 0), (0, h - 1), (w - 1, h - 1), (w - 1, 0)])
|
||||
xs = [LineString(lane).buffer(distance=width / 2., cap_style=1, join_style=2).intersection(image) for lane in xs]
|
||||
ys = [LineString(lane).buffer(distance=width / 2., cap_style=1, join_style=2).intersection(image) for lane in ys]
|
||||
|
||||
ious = np.zeros((len(xs), len(ys)))
|
||||
for i, x in enumerate(xs):
|
||||
for j, y in enumerate(ys):
|
||||
ious[i, j] = x.intersection(y).area / x.union(y).area
|
||||
|
||||
return ious
|
||||
|
||||
|
||||
def remove_con_dup(x):
|
||||
"""Customize a set op to remove consecutive duplications"""
|
||||
y = []
|
||||
for t in x:
|
||||
if len(y) > 0 and y[-1] == t:
|
||||
continue
|
||||
y.append(t)
|
||||
return y
|
||||
|
||||
|
||||
def interpolate_lane(points, n=50):
|
||||
"""Spline interpolation of a lane. Used on the predictions"""
|
||||
# Consecutive duplications cause internal error for scipy's splprep:
|
||||
# https://stackoverflow.com/a/47949170/15449902
|
||||
points = remove_con_dup(points)
|
||||
|
||||
# B-Spline interpolate
|
||||
x = [x for x, _ in points]
|
||||
y = [y for _, y in points]
|
||||
tck, _ = splprep([x, y], s=0, t=n, k=min(3, len(points) - 1))
|
||||
|
||||
u = np.linspace(0., 1., n)
|
||||
return np.array(splev(u, tck)).T
|
||||
|
||||
|
||||
def culane_metric(pred, anno, width=30, iou_threshold=0.5, unofficial=False, img_shape=LLAMAS_IMG_RES):
|
||||
"""Computes CULane's metric for a single image"""
|
||||
if len(pred) == 0:
|
||||
return 0, 0, len(anno)
|
||||
if len(anno) == 0:
|
||||
return 0, len(pred), 0
|
||||
interp_pred = np.array([interpolate_lane(pred_lane, n=50) for pred_lane in pred]) # (4, 50, 2)
|
||||
anno = np.array([np.array(anno_lane) for anno_lane in anno], dtype=object)
|
||||
|
||||
if unofficial:
|
||||
ious = continuous_cross_iou(interp_pred, anno, width=width)
|
||||
else:
|
||||
ious = discrete_cross_iou(interp_pred, anno, width=width, img_shape=img_shape)
|
||||
|
||||
row_ind, col_ind = linear_sum_assignment(1 - ious)
|
||||
tp = int((ious[row_ind, col_ind] > iou_threshold).sum())
|
||||
fp = len(pred) - tp
|
||||
fn = len(anno) - tp
|
||||
return tp, fp, fn
|
||||
|
||||
|
||||
def load_prediction(path):
|
||||
"""Loads an image's predictions
|
||||
Returns a list of lanes, where each lane is a list of points (x,y)
|
||||
"""
|
||||
with open(path, 'r') as data_file:
|
||||
img_data = data_file.readlines()
|
||||
img_data = [line.split() for line in img_data]
|
||||
img_data = [list(map(float, lane)) for lane in img_data]
|
||||
img_data = [[(lane[i], lane[i + 1]) for i in range(0, len(lane), 2)] for lane in img_data]
|
||||
img_data = [lane for lane in img_data if len(lane) >= 2]
|
||||
|
||||
return img_data
|
||||
|
||||
|
||||
def load_prediction_list(label_paths, pred_dir):
|
||||
return [load_prediction(os.path.join(pred_dir, path.replace('.json', '.lines.txt'))) for path in label_paths]
|
||||
|
||||
|
||||
def load_labels(label_dir):
|
||||
"""Loads the annotations and its paths
|
||||
Each annotation is converted to a list of points (x, y)
|
||||
"""
|
||||
label_paths = get_files_from_folder(label_dir, '.json')
|
||||
annos = [[add_ys(xs) for xs in get_horizontal_values_for_four_lanes(label_path) if
|
||||
(np.array(xs) >= 0).sum() > 1] # lanes annotated with a single point are ignored
|
||||
for label_path in label_paths]
|
||||
label_paths = [
|
||||
get_label_base(p) for p in label_paths
|
||||
]
|
||||
return np.array(annos, dtype=object), np.array(label_paths, dtype=object)
|
||||
|
||||
|
||||
def eval_predictions(pred_dir, anno_dir, width=30, unofficial=True, sequential=False):
|
||||
"""Evaluates the predictions in pred_dir and returns CULane's metrics (precision, recall, F1 and its components)"""
|
||||
print(f'Loading annotation data ({anno_dir})...')
|
||||
annotations, label_paths = load_labels(anno_dir)
|
||||
print(f'Loading prediction data ({pred_dir})...')
|
||||
predictions = load_prediction_list(label_paths, pred_dir)
|
||||
print('Calculating metric {}...'.format('sequentially' if sequential else 'in parallel'))
|
||||
if sequential:
|
||||
results = t_map(partial(culane_metric, width=width, unofficial=unofficial, img_shape=LLAMAS_IMG_RES),
|
||||
predictions,
|
||||
annotations)
|
||||
else:
|
||||
results = p_map(partial(culane_metric, width=width, unofficial=unofficial, img_shape=LLAMAS_IMG_RES),
|
||||
predictions,
|
||||
annotations)
|
||||
total_tp = sum(tp for tp, _, _ in results)
|
||||
total_fp = sum(fp for _, fp, _ in results)
|
||||
total_fn = sum(fn for _, _, fn in results)
|
||||
if total_tp == 0:
|
||||
precision = 0
|
||||
recall = 0
|
||||
f1 = 0
|
||||
else:
|
||||
precision = float(total_tp) / (total_tp + total_fp)
|
||||
recall = float(total_tp) / (total_tp + total_fn)
|
||||
f1 = 2 * precision * recall / (precision + recall)
|
||||
|
||||
return {'TP': total_tp, 'FP': total_fp, 'FN': total_fn, 'Precision': precision, 'Recall': recall, 'F1': f1}
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Measure CULane's metric on the LLAMAS dataset")
|
||||
parser.add_argument("--pred_dir", help="Path to directory containing the predicted lanes", required=True)
|
||||
parser.add_argument("--anno_dir", help="Path to directory containing the annotated lanes", required=True)
|
||||
parser.add_argument('--exp_name', type=str, default='', help='Name of experiment')
|
||||
parser.add_argument('--save-dir', type=str, help='Path prefix to save full res.')
|
||||
parser.add_argument("--width", type=int, default=30, help="Width of the lane")
|
||||
parser.add_argument("--sequential", action='store_true', help="Run sequentially instead of in parallel")
|
||||
parser.add_argument("--unofficial", action='store_true', help="Use a faster but unofficial algorithm")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
results = eval_predictions(args.pred_dir,
|
||||
args.anno_dir,
|
||||
width=args.width,
|
||||
unofficial=args.unofficial,
|
||||
sequential=args.sequential)
|
||||
|
||||
header = '=' * 20 + ' Results' + '=' * 20
|
||||
print(header)
|
||||
for metric, value in results.items():
|
||||
if isinstance(value, float):
|
||||
output = '{}: {:.4f}'.format(metric, value)
|
||||
print(output)
|
||||
else:
|
||||
print('{}: {}'.format(metric, value))
|
||||
with open('../../log.txt', 'a') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
f.write(args.exp_name + ': ' + str(results['F1']) + '\n')
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
print('=' * len(header))
|
||||
|
||||
res = json.dumps(results)
|
||||
with open('./output/' + args.exp_name + '.json', 'w') as f:
|
||||
f.write(res)
|
||||
|
||||
if args.save_dir is not None:
|
||||
import os
|
||||
prefix = 'val'
|
||||
if 'valid' not in args.anno_dir:
|
||||
prefix = 'custom_anno_dir_' + args.anno_dir[args.anno_dir.rfind('/') + 1:]
|
||||
save_dir = os.path.join('../../', args.save_dir, args.exp_name)
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
with open(os.path.join(save_dir, prefix + '_result.json'), 'w') as f:
|
||||
f.write(res)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,374 @@
|
||||
# Its license is copied here
|
||||
|
||||
# ##### Begin License ######
|
||||
# MIT License
|
||||
|
||||
# Copyright (c) 2019 Karsten Behrendt, Robert Bosch LLC
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
# ##### End License ######
|
||||
# copied from https://github.com/karstenBehrendt/unsupervised_llamas/tree/master/label_scripts
|
||||
# Start code under the previous license
|
||||
import json
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _extend_lane(lane, projection_matrix):
|
||||
"""Extends marker closest to the camera
|
||||
|
||||
Adds an extra marker that reaches the end of the image
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lane : iterable of markers
|
||||
projection_matrix : 3x3 projection matrix
|
||||
"""
|
||||
# Unfortunately, we did not store markers beyond the image plane. That hurts us now
|
||||
# z is the orthongal distance to the car. It's good enough
|
||||
|
||||
# The markers are automatically detected, mapped, and labeled. There exist faulty ones,
|
||||
# e.g., horizontal markers which need to be filtered
|
||||
filtered_markers = filter(
|
||||
lambda x: (x['pixel_start']['y'] != x['pixel_end']['y'] and x['pixel_start']['x'] != x['pixel_end']['x']),
|
||||
lane['markers'])
|
||||
# might be the first marker in the list but not guaranteed
|
||||
closest_marker = min(filtered_markers, key=lambda x: x['world_start']['z'])
|
||||
|
||||
if closest_marker['world_start']['z'] < 0: # This one likely equals "if False"
|
||||
return lane
|
||||
|
||||
# World marker extension approximation
|
||||
x_gradient = (closest_marker['world_end']['x'] - closest_marker['world_start']['x']) / \
|
||||
(closest_marker['world_end']['z'] - closest_marker['world_start']['z'])
|
||||
y_gradient = (closest_marker['world_end']['y'] - closest_marker['world_start']['y']) / \
|
||||
(closest_marker['world_end']['z'] - closest_marker['world_start']['z'])
|
||||
|
||||
zero_x = closest_marker['world_start']['x'] - (closest_marker['world_start']['z'] - 1) * x_gradient
|
||||
zero_y = closest_marker['world_start']['y'] - (closest_marker['world_start']['z'] - 1) * y_gradient
|
||||
|
||||
# Pixel marker extension approximation
|
||||
pixel_x_gradient = (closest_marker['pixel_end']['x'] - closest_marker['pixel_start']['x']) / \
|
||||
(closest_marker['pixel_end']['y'] - closest_marker['pixel_start']['y'])
|
||||
pixel_y_gradient = (closest_marker['pixel_end']['y'] - closest_marker['pixel_start']['y']) / \
|
||||
(closest_marker['pixel_end']['x'] - closest_marker['pixel_start']['x'])
|
||||
|
||||
pixel_zero_x = closest_marker['pixel_start']['x'] + (716 - closest_marker['pixel_start']['y']) * pixel_x_gradient
|
||||
if pixel_zero_x < 0:
|
||||
left_y = closest_marker['pixel_start']['y'] - closest_marker['pixel_start']['x'] * pixel_y_gradient
|
||||
new_pixel_point = (0, left_y)
|
||||
elif pixel_zero_x > 1276:
|
||||
right_y = closest_marker['pixel_start']['y'] + (1276 - closest_marker['pixel_start']['x']) * pixel_y_gradient
|
||||
new_pixel_point = (1276, right_y)
|
||||
else:
|
||||
new_pixel_point = (pixel_zero_x, 716)
|
||||
|
||||
new_marker = {
|
||||
'lane_marker_id': 'FAKE',
|
||||
'world_end': {
|
||||
'x': closest_marker['world_start']['x'],
|
||||
'y': closest_marker['world_start']['y'],
|
||||
'z': closest_marker['world_start']['z']
|
||||
},
|
||||
'world_start': {
|
||||
'x': zero_x,
|
||||
'y': zero_y,
|
||||
'z': 1
|
||||
},
|
||||
'pixel_end': {
|
||||
'x': closest_marker['pixel_start']['x'],
|
||||
'y': closest_marker['pixel_start']['y']
|
||||
},
|
||||
'pixel_start': {
|
||||
'x': ir(new_pixel_point[0]),
|
||||
'y': ir(new_pixel_point[1])
|
||||
}
|
||||
}
|
||||
lane['markers'].insert(0, new_marker)
|
||||
|
||||
return lane
|
||||
|
||||
|
||||
class SplineCreator():
|
||||
"""
|
||||
For each lane divder
|
||||
- all lines are projected
|
||||
- linearly interpolated to limit oscillations
|
||||
- interpolated by a spline
|
||||
- subsampled to receive individual pixel values
|
||||
|
||||
The spline creation can be optimized!
|
||||
- Better spline parameters
|
||||
- Extend lowest marker to reach bottom of image would also help
|
||||
- Extending last marker may in some cases be interesting too
|
||||
Any help is welcome.
|
||||
|
||||
Call create_all_points and get the points in self.sampled_points
|
||||
It has an x coordinate for each value for each lane
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, json_path):
|
||||
self.json_path = json_path
|
||||
self.json_content = read_json(json_path)
|
||||
self.lanes = self.json_content['lanes']
|
||||
self.lane_marker_points = {}
|
||||
self.sampled_points = {} # <--- the interesting part
|
||||
self.debug_image = np.zeros((717, 1276, 3), dtype=np.uint8)
|
||||
|
||||
def _sample_points(self, lane, ypp=5, between_markers=True):
|
||||
""" Markers are given by start and endpoint. This one adds extra points
|
||||
which need to be considered for the interpolation. Otherwise the spline
|
||||
could arbitrarily oscillate between start and end of the individual markers
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lane: polyline, in theory but there are artifacts which lead to inconsistencies
|
||||
in ordering. There may be parallel lines. The lines may be dashed. It's messy.
|
||||
ypp: y-pixels per point, e.g. 10 leads to a point every ten pixels
|
||||
between_markers : bool, interpolates inbetween dashes
|
||||
|
||||
Notes
|
||||
-----
|
||||
Especially, adding points in the lower parts of the image (high y-values) because
|
||||
the start and end points are too sparse.
|
||||
Removing upper lane markers that have starting and end points mapped into the same pixel.
|
||||
"""
|
||||
|
||||
# Collect all x values from all markers along a given line. There may be multiple
|
||||
# intersecting markers, i.e., multiple entries for some y values
|
||||
x_values = [[] for i in range(717)]
|
||||
for marker in lane['markers']:
|
||||
x_values[marker['pixel_start']['y']].append(marker['pixel_start']['x'])
|
||||
|
||||
height = marker['pixel_start']['y'] - marker['pixel_end']['y']
|
||||
if height > 2:
|
||||
slope = (marker['pixel_end']['x'] - marker['pixel_start']['x']) / height
|
||||
step_size = (marker['pixel_start']['y'] - marker['pixel_end']['y']) / float(height)
|
||||
for i in range(height + 1):
|
||||
x = marker['pixel_start']['x'] + slope * step_size * i
|
||||
y = marker['pixel_start']['y'] - step_size * i
|
||||
x_values[ir(y)].append(ir(x))
|
||||
|
||||
# Calculate average x values for each y value
|
||||
for y, xs in enumerate(x_values):
|
||||
if not xs:
|
||||
x_values[y] = -1
|
||||
else:
|
||||
x_values[y] = sum(xs) / float(len(xs))
|
||||
|
||||
# In the following, we will only interpolate between markers if needed
|
||||
if not between_markers:
|
||||
return x_values # TODO ypp
|
||||
|
||||
# # interpolate between markers
|
||||
current_y = 0
|
||||
while x_values[current_y] == -1: # skip missing first entries
|
||||
current_y += 1
|
||||
|
||||
# Also possible using numpy.interp when accounting for beginning and end
|
||||
next_set_y = 0
|
||||
try:
|
||||
while current_y < 717:
|
||||
if x_values[current_y] != -1: # set. Nothing to be done
|
||||
current_y += 1
|
||||
continue
|
||||
|
||||
# Finds target x value for interpolation
|
||||
while next_set_y <= current_y or x_values[next_set_y] == -1:
|
||||
next_set_y += 1
|
||||
if next_set_y >= 717:
|
||||
raise StopIteration
|
||||
|
||||
x_values[current_y] = x_values[current_y - 1] + (x_values[next_set_y] - x_values[current_y - 1]) / \
|
||||
(next_set_y - current_y + 1)
|
||||
current_y += 1
|
||||
|
||||
except StopIteration:
|
||||
pass # Done with lane
|
||||
|
||||
return x_values
|
||||
|
||||
def _lane_points_fit(self, lane):
|
||||
# TODO name and docstring
|
||||
""" Fits spline in image space for the markers of a single lane (side)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lane: dict as specified in label
|
||||
|
||||
Returns
|
||||
-------
|
||||
Pixel level values for curve along the y-axis
|
||||
|
||||
Notes
|
||||
-----
|
||||
This one can be drastically improved. Probably fairly easy as well.
|
||||
"""
|
||||
# NOTE all variable names represent image coordinates, interpolation coordinates are swapped!
|
||||
lane = _extend_lane(lane, self.json_content['projection_matrix'])
|
||||
sampled_points = self._sample_points(lane, ypp=1)
|
||||
self.sampled_points[lane['lane_id']] = sampled_points
|
||||
|
||||
return sampled_points
|
||||
|
||||
def create_all_points(self, ):
|
||||
""" Creates splines for given label """
|
||||
for lane in self.lanes:
|
||||
self._lane_points_fit(lane)
|
||||
|
||||
|
||||
def get_horizontal_values_for_four_lanes(json_path):
|
||||
""" Gets an x value for every y coordinate for l1, l0, r0, r1
|
||||
|
||||
This allows to easily train a direct curve approximation. For each value along
|
||||
the y-axis, the respective x-values can be compared, e.g. squared distance.
|
||||
Missing values are filled with -1. Missing values are values missing from the spline.
|
||||
There is no extrapolation to the image start/end (yet).
|
||||
But values are interpolated between markers. Space between dashed markers is not missing.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
json_path: str
|
||||
path to label-file
|
||||
|
||||
Returns
|
||||
-------
|
||||
List of [l1, l0, r0, r1], each of which represents a list of ints the length of
|
||||
the number of vertical pixels of the image
|
||||
|
||||
Notes
|
||||
-----
|
||||
The points are currently based on the splines. The splines are interpolated based on the
|
||||
segmentation values. The spline interpolation has lots of room for improvement, e.g.
|
||||
the lines could be interpolated in 3D, a better approach to spline interpolation could
|
||||
be used, there is barely any error checking, sometimes the splines oscillate too much.
|
||||
This was used for a quick poly-line regression training only.
|
||||
"""
|
||||
|
||||
sc = SplineCreator(json_path)
|
||||
sc.create_all_points()
|
||||
|
||||
l1 = sc.sampled_points.get('l1', [-1] * 717)
|
||||
l0 = sc.sampled_points.get('l0', [-1] * 717)
|
||||
r0 = sc.sampled_points.get('r0', [-1] * 717)
|
||||
r1 = sc.sampled_points.get('r1', [-1] * 717)
|
||||
|
||||
lanes = [l1, l0, r0, r1]
|
||||
return lanes
|
||||
|
||||
|
||||
def _filter_lanes_by_size(label, min_height=40):
|
||||
""" May need some tuning """
|
||||
filtered_lanes = []
|
||||
for lane in label['lanes']:
|
||||
lane_start = min([int(marker['pixel_start']['y']) for marker in lane['markers']])
|
||||
lane_end = max([int(marker['pixel_start']['y']) for marker in lane['markers']])
|
||||
if (lane_end - lane_start) < min_height:
|
||||
continue
|
||||
filtered_lanes.append(lane)
|
||||
label['lanes'] = filtered_lanes
|
||||
|
||||
|
||||
def _filter_few_markers(label, min_markers=2):
|
||||
"""Filter lines that consist of only few markers"""
|
||||
filtered_lanes = []
|
||||
for lane in label['lanes']:
|
||||
if len(lane['markers']) >= min_markers:
|
||||
filtered_lanes.append(lane)
|
||||
label['lanes'] = filtered_lanes
|
||||
|
||||
|
||||
def _fix_lane_names(label):
|
||||
""" Given keys ['l3', 'l2', 'l0', 'r0', 'r2'] returns ['l2', 'l1', 'l0', 'r0', 'r1']"""
|
||||
|
||||
# Create mapping
|
||||
l_counter = 0
|
||||
r_counter = 0
|
||||
mapping = {}
|
||||
lane_ids = [lane['lane_id'] for lane in label['lanes']]
|
||||
for key in sorted(lane_ids):
|
||||
if key[0] == 'l':
|
||||
mapping[key] = 'l' + str(l_counter)
|
||||
l_counter += 1
|
||||
if key[0] == 'r':
|
||||
mapping[key] = 'r' + str(r_counter)
|
||||
r_counter += 1
|
||||
for lane in label['lanes']:
|
||||
lane['lane_id'] = mapping[lane['lane_id']]
|
||||
|
||||
|
||||
def read_json(json_path, min_lane_height=20):
|
||||
""" Reads and cleans label file information by path"""
|
||||
with open(json_path, 'r') as jf:
|
||||
label_content = json.load(jf)
|
||||
|
||||
_filter_lanes_by_size(label_content, min_height=min_lane_height)
|
||||
_filter_few_markers(label_content, min_markers=2)
|
||||
_fix_lane_names(label_content)
|
||||
|
||||
content = {'projection_matrix': label_content['projection_matrix'], 'lanes': label_content['lanes']}
|
||||
|
||||
for lane in content['lanes']:
|
||||
for marker in lane['markers']:
|
||||
for pixel_key in marker['pixel_start'].keys():
|
||||
marker['pixel_start'][pixel_key] = int(marker['pixel_start'][pixel_key])
|
||||
for pixel_key in marker['pixel_end'].keys():
|
||||
marker['pixel_end'][pixel_key] = int(marker['pixel_end'][pixel_key])
|
||||
for pixel_key in marker['world_start'].keys():
|
||||
marker['world_start'][pixel_key] = float(marker['world_start'][pixel_key])
|
||||
for pixel_key in marker['world_end'].keys():
|
||||
marker['world_end'][pixel_key] = float(marker['world_end'][pixel_key])
|
||||
return content
|
||||
|
||||
|
||||
def ir(some_value):
|
||||
""" Rounds and casts to int
|
||||
Useful for pixel values that cannot be floats
|
||||
Parameters
|
||||
----------
|
||||
some_value : float
|
||||
numeric value
|
||||
Returns
|
||||
--------
|
||||
Rounded integer
|
||||
Raises
|
||||
------
|
||||
ValueError for non scalar types
|
||||
"""
|
||||
return int(round(some_value))
|
||||
|
||||
|
||||
def get_files_from_folder(directory, extension=None):
|
||||
"""Get all files within a folder that fit the extension """
|
||||
# NOTE Can be replaced by glob for newer python versions
|
||||
label_files = []
|
||||
for root, _, files in os.walk(directory):
|
||||
for some_file in files:
|
||||
label_files.append(os.path.abspath(os.path.join(root, some_file)))
|
||||
if extension is not None:
|
||||
label_files = list(filter(lambda x: x.endswith(extension), label_files))
|
||||
return label_files
|
||||
|
||||
|
||||
def get_label_base(label_path):
|
||||
""" Gets directory independent label path """
|
||||
return '/'.join(label_path.split('/')[-2:])
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
from llamas_evaluation.llamas_official_scripts import get_horizontal_values_for_four_lanes
|
||||
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from configs.lane_detection.common.datasets._utils import LLAMAS_ROOT as base
|
||||
LLAMAS_H = 717
|
||||
|
||||
#
|
||||
list_path = os.path.join(base, 'lists')
|
||||
image_path = os.path.join(base, 'color_images')
|
||||
label_path = os.path.join(base, 'labels')
|
||||
|
||||
if os.path.exists(list_path) is False:
|
||||
os.makedirs(list_path)
|
||||
file_names = ['train', 'val', 'valfast', 'test']
|
||||
|
||||
|
||||
def get_file_paths(dir, type):
|
||||
paths = []
|
||||
for root, dirs, files in os.walk(dir):
|
||||
for file in files:
|
||||
if file.endswith(type):
|
||||
paths.append(os.path.join(root, file))
|
||||
return paths
|
||||
|
||||
|
||||
def coords2str(lane):
|
||||
s = ""
|
||||
for coords in lane:
|
||||
s = s + str(round(coords[0], 3)) + " "
|
||||
s = s + str(coords[1]) + " "
|
||||
s = s + '\n'
|
||||
return s
|
||||
|
||||
|
||||
def get_txtfile(filepath, lanes):
|
||||
with open(filepath, 'a') as f:
|
||||
for lane in lanes:
|
||||
f.writelines(coords2str(lane))
|
||||
return 0
|
||||
|
||||
|
||||
def existence2str(exist):
|
||||
s = ""
|
||||
for idx in exist:
|
||||
s = s + str(idx) + " "
|
||||
return s
|
||||
|
||||
|
||||
def spline_annotation(json_name, image_name, get_txt):
|
||||
spline_lanes = get_horizontal_values_for_four_lanes(json_name)
|
||||
lanes = [[(x, y) for x, y in zip(lane, range(LLAMAS_H)) if x >= 0] for lane in spline_lanes]
|
||||
lanes_exist = [1 if len(lane) > 0 else 0 for lane in lanes]
|
||||
lanes = [lane for lane in lanes if len(lane) > 0]
|
||||
if get_txt is True:
|
||||
txt_path = image_name.replace('.png', '.lines.txt')
|
||||
get_txtfile(txt_path, lanes)
|
||||
return lanes_exist
|
||||
|
||||
|
||||
def get_spline(filetype, filename, get_txt=False, existence=False, ant_exist=True):
|
||||
images_list = get_file_paths(os.path.join(image_path, filetype), ".png")
|
||||
images_list.sort()
|
||||
json_list = get_file_paths(os.path.join(label_path, filetype), ".json")
|
||||
if len(json_list) != 0:
|
||||
json_list.sort()
|
||||
length_of_list = len(images_list)
|
||||
for idx in tqdm(range(0, length_of_list)):
|
||||
lanes_exist = []
|
||||
if ant_exist:
|
||||
lanes_exist = spline_annotation(json_list[idx], images_list[idx], get_txt)
|
||||
with open(os.path.join(list_path, filename), 'a') as f:
|
||||
if existence is True:
|
||||
f.writelines(
|
||||
images_list[idx][len(image_path) + 1:].replace('.png', '') + " " + existence2str(lanes_exist) + "\n")
|
||||
else:
|
||||
f.writelines(images_list[idx][len(image_path) + 1:].replace('.png', '') + "\n")
|
||||
return 0
|
||||
|
||||
|
||||
def generate_spline_annotation():
|
||||
for file_name in file_names:
|
||||
if file_name == 'train':
|
||||
print(file_name+".txt is processing...")
|
||||
get_spline(file_name, file_name + '.txt', get_txt=True, existence=True, ant_exist=True)
|
||||
elif file_name == 'valfast':
|
||||
print(file_name + ".txt is processing...")
|
||||
get_spline('valid', file_name + '.txt', get_txt=True, existence=True, ant_exist=True)
|
||||
elif file_name == 'val':
|
||||
print(file_name + ".txt is processing...")
|
||||
get_spline('valid', file_name + '.txt', get_txt=False, existence=False, ant_exist=False)
|
||||
elif file_name == 'test':
|
||||
print(file_name + ".txt is processing...")
|
||||
get_spline(file_name, file_name + '.txt', get_txt=False, existence=False, ant_exist=False)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
generate_spline_annotation()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import argparse
|
||||
import torch
|
||||
|
||||
from importmagician import import_from
|
||||
with import_from('./'):
|
||||
from utils.args import read_config, parse_arg_cfg, cmd_dict, add_shortcuts
|
||||
from utils.models import MODELS
|
||||
from utils.common import load_checkpoint
|
||||
from utils.profiling_utils import init_dataset, speed_evaluate_real, speed_evaluate_simple, model_profile
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Settings
|
||||
parser = argparse.ArgumentParser(description='PytorchAutoDrive Profiling', conflict_handler='resolve')
|
||||
add_shortcuts(parser)
|
||||
|
||||
parser.add_argument('--config', type=str, help='Path to config file', required=True)
|
||||
|
||||
# Optional args/to overwrite configs
|
||||
parser.add_argument('--height', type=int, default=288,
|
||||
help='Image input height (default: 288)')
|
||||
parser.add_argument('--width', type=int, default=800,
|
||||
help='Image input width (default: 800)')
|
||||
parser.add_argument('--mode', type=str, default='simple',
|
||||
help='Profiling mode (simple/real)')
|
||||
parser.add_argument('--times', type=int, default=1,
|
||||
help='Select test times')
|
||||
parser.add_argument('--cfg-options', type=cmd_dict,
|
||||
help='Override config options with \"x1=y1 x2=y2 xn=yn\"')
|
||||
|
||||
group2 = parser.add_mutually_exclusive_group()
|
||||
group2.add_argument('--continue-from', type=str,
|
||||
help='[Deprecated] Continue training from a previous checkpoint')
|
||||
group2.add_argument('--checkpoint', type=str,
|
||||
help='Continue/Load from a previous checkpoint')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Parse configs and build model
|
||||
cfg = read_config(args.config)
|
||||
args, cfg = parse_arg_cfg(args, cfg)
|
||||
net = MODELS.from_dict(cfg['model'])
|
||||
|
||||
device = torch.device('cpu')
|
||||
if torch.cuda.is_available():
|
||||
device = torch.device('cuda:0')
|
||||
print(device)
|
||||
|
||||
net.to(device)
|
||||
|
||||
if args.mode == 'simple':
|
||||
dummy = torch.ones((1, 3, args.height, args.width))
|
||||
print(dummy.dtype)
|
||||
fps = []
|
||||
for i in range(0, args.times):
|
||||
fps.append(speed_evaluate_simple(net=net, device=device, dummy=dummy, num=300))
|
||||
print('GPU FPS: {: .2f}'.format(max(fps)))
|
||||
elif args.mode == 'real':
|
||||
if cfg['test']['checkpoint'] is not None:
|
||||
load_checkpoint(net=net, optimizer=None, lr_scheduler=None, filename=cfg['test']['checkpoint'])
|
||||
val_loader = init_dataset(cfg['dataset'], cfg['test_augmentations'], (args.height, args.width))
|
||||
fps = []
|
||||
gpu_fps = []
|
||||
for i in range(0, args.times):
|
||||
fps_item, gpu_fps_item = speed_evaluate_real(net=net, device=device, loader=val_loader, num=300)
|
||||
fps.append(fps_item)
|
||||
gpu_fps.append(gpu_fps_item)
|
||||
print('Real FPS: {: .2f}'.format(max(fps)))
|
||||
print('GPU FPS: {: .2f}'.format(max(gpu_fps)))
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
macs, _ = model_profile(net, args.height, args.width, device)
|
||||
flops = 2 * macs
|
||||
try:
|
||||
net.eval(profiling=True)
|
||||
except TypeError:
|
||||
net.eval()
|
||||
params = sum(p.numel() for p in net.parameters())
|
||||
print('FLOPs(G): {: .2f}'.format(flops / 1e9))
|
||||
print('Number of parameters: {: .2f}'.format(params / 1e6))
|
||||
print('Profiling, please clear your GPU memory before doing this.')
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv2_cityscapes_256x512_20201225.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv2/resnet101_cityscapes_256x512.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv2/resnet101_cityscapes_256x512.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv2_cityscapes_256x512_fp32_20201227.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv2/resnet101_cityscapes_256x512.py
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv2/resnet101_cityscapes_256x512.py
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv2_cityscapes_512x1024_20201219.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv2/resnet101_cityscapes_512x1024.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv2/resnet101_cityscapes_512x1024.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv2_gtav_512x1024_20201223.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv2/resnet101_gtav_512x1024.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv2/resnet101_gtav_512x1024.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv2_pascalvoc_321x321_20201108.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv2/resnet101_pascalvoc_321x321.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv2/resnet101_pascalvoc_321x321.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv2_synthia_512x1024_20201225.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv2/resnet101_synthia_512x1024.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv2/resnet101_synthia_512x1024.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv3_city_256x512_20201226.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv3/resnet101_cityscapes_256x512.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv3/resnet101_cityscapes_256x512.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv3_cityscapes_512x1024_20210322.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv3/resnet101_cityscapes_512x1024.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv3/resnet101_cityscapes_512x1024.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: deeplabv3_voc_321x321_20201110.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/deeplabv3/resnet101_pascalvoc_321x321.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/deeplabv3/resnet101_pascalvoc_321x321.py --mixed-precision
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: enet_baseline_culane_20210312.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/enet_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/enet_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh enet_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: enet_baseline_tusimple_20210312.pt
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/enet_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/enet_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh enet_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: enet_cityscapes_512x1024_20210219.pt
|
||||
# Step-1: Pre-train encoder
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/enet/cityscapes_512x1024_encoder.py --mixed-precision
|
||||
|
||||
# Step-2: Train the entire network
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/enet/cityscapes_512x1024.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/enet/cityscapes_512x1024.py --mixed-precision
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_baseline_tusimple-aug_20210723.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --config=configs/lane_detection/baseline/erfnet_tusimple_aug.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/erfnet_tusimple.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh erfnet_baseline_tusimple-aug test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_baseline_culane_20210204.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/erfnet_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/erfnet_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh erfnet_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_baseline_llamas_20210625.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/erfnet_llamas.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --val --config=configs/lane_detection/baseline/erfnet_llamas.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_llamas.sh erfnet_baseline_llamas val checkpoints
|
||||
# Predict lane points for the eval server, find results in ./output
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/erfnet_llamas.py --mixed-precision
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_baseline_tusimple_20210424.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/erfnet_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/erfnet_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh erfnet_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_cityscapes_512x1024_20200918.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/erfnet/cityscapes_512x1024.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/erfnet/cityscapes_512x1024.py --mixed-precision
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/resa/erfnet_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/erfnet_culane.py
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh erfnet_resa_culane test checkpoints
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/resa/erfnet_tusimple.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/erfnet_tusimple.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh erfnet_resa_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_scnn_tusimple-aug_20210723.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --config=configs/lane_detection/scnn/erfnet_tusimple_aug.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/erfnet_tusimple.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh erfnet_scnn_tusimple-aug test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_scnn_culane_20210206.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/erfnet_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/erfnet_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh erfnet_scnn_culane test checkpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_scnn_llamas_20210625.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/erfnet_llamas.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --val --config=configs/lane_detection/scnn/erfnet_llamas.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_llamas.sh erfnet_scnn_llamas val checkpoints
|
||||
# Predict lane points for the eval server, find results in ./output
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/erfnet_llamas.py --mixed-precision
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: erfnet_scnn_tusimple_20210202.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/erfnet_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/erfnet_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh erfnet_scnn_tusimple test checkpoints
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: fcn_cityscapes_256x512_20201226.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/fcn/resnet101_cityscapes_256x512.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/fcn/resnet101_cityscapes_256x512.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: fcn_pascalvoc_321x321_20201111.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/fcn/resnet101_pascalvoc_321x321.py --mixed-precision
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/fcn/resnet101_pascalvoc_321x321.py --mixed-precision
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: fcn_pascalvoc_321x321_fp32_20201111.pt
|
||||
python main_semseg.py --train --config=configs/semantic_segmentation/fcn/resnet101_pascalvoc_321x321.py
|
||||
|
||||
# Val
|
||||
python main_semseg.py --val --config=configs/semantic_segmentation/fcn/resnet101_pascalvoc_321x321.py
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv2_baseline_culane_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/mobilenetv2_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/mobilenetv2_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh mobilenetv2_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv2_baseline_tusimple_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/mobilenetv2_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/mobilenetv2_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh mobilenetv2_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv2_resa_culane_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/resa/mobilenetv2_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/mobilenetv2_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh mobilenetv2_resa_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv2_resa_tusimple_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/resa/mobilenetv2_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/mobilenetv2_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh mobilenetv2_resa_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv3-large_baseline_culane_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/mobilenetv3_large_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/mobilenetv3_large_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh mobilenetv3-large_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv3-large_baseline_tusimple_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/mobilenetv3_large_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/mobilenetv3_large_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh mobilenetv3-large_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv3-large_resa_culane_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/resa/mobilenetv3_large_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/mobilenetv3_large_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh mobilenetv3-large_resa_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: mobilenetv3-large_resa_tusimple_20220209.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/resa/mobilenetv3_large_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/mobilenetv3_large_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh mobilenetv3-large_resa_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: repvgg-a0_baseline_culane_20220112.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --mixed-precision --config configs/lane_detection/baseline/repvgg_a0_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config configs/lane_detection/baseline/repvgg_a0_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh repvgg-a0_baseline_culane test
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: repvgg-a1_baseline_culane_20220112.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --mixed-precision --config configs/lane_detection/baseline/repvgg_a1_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config configs/lane_detection/baseline/repvgg_a1_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh repvgg-a1_baseline_culane test
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: repvgg-a1_scnn_culane_20220112.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --mixed-precision --config configs/lane_detection/scnn/repvgg_a1_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config configs/lane_detection/scnn/repvgg_a1_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh repvgg-a1_scnn_culane test
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: repvgg-b0_baseline_culane_20220112.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --mixed-precision --config configs/lane_detection/baseline/repvgg_b0_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config configs/lane_detection/baseline/repvgg_b0_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh repvgg-b0_baseline_culane test
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: repvgg-b1g2_baseline_culane_20220112.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --mixed-precision --config configs/lane_detection/baseline/repvgg_b1g2_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config configs/lane_detection/baseline/repvgg_b1g2_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh repvgg-b1g2_baseline_culane test
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: repvgg-b2_baseline_culane_20220112.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --mixed-precision --config configs/lane_detection/baseline/repvgg_b2_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config configs/lane_detection/baseline/repvgg_b2_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh repvgg-b2_baseline_culane test
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet101_baseline_culane_20210312.pt
|
||||
# Training, scale lr linearly on 11G GPU (square root scaling does not converge on this dataset)
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet101_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet101_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet101_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet101_baseline_tusimple_20210424.pt
|
||||
# Training, scale lr by square root on 11G GPU
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet101_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet101_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet101_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet101_resa_culane_20211016.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=8 --use_env main_landet.py --train --config=configs/lane_detection/resa/resnet101_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/resnet101_culane.py
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet101_resa_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet101_resa_tusimple_20211019.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=8 --use_env main_landet.py --train --config=configs/lane_detection/resa/resnet101_tusimple.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/resnet101_tusimple.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet101_resa_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet101_scnn_culane_20210314.pt
|
||||
# Training, scale lr linearly on 11G GPU (square root scaling does not converge on this dataset)
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/resnet101_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/resnet101_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet101_scnn_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet101_scnn_tusimple_20210218.pt
|
||||
# Training, scale lr by square root on 11G GPU
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/resnet101_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/resnet101_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet101_scnn_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_baseline_culane_20210222.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet18_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet18_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_baseline_tusimple_20210215.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet18_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet18_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet18_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_bezierlanenet_culane-aug1b_20211109.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/bezierlanenet/resnet18_culane_aug1b.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/bezierlanenet/resnet18_culane_aug1b.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18_bezierlanenet_culane-aug1b test checkpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_bezierlanenet_llamas-aug1b_20211109.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/bezierlanenet/resnet18_llamas_aug1b.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --val --config=configs/lane_detection/bezierlanenet/resnet18_llamas_aug1b.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_llamas.sh resnet18_bezierlanenet_llamas-aug1b val checkpoints
|
||||
# Predict lane points for the eval server, find results in ./output
|
||||
python main_landet.py --test --config=configs/lane_detection/bezierlanenet/resnet18_llamas_aug1b.py --mixed-precision
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_bezierlanenet_tusimple-aug1b_20211109.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/bezierlanenet/resnet18_tusimple_aug1b.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/bezierlanenet/resnet18_tusimple_aug1b.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet18_bezierlanenet_tusimple-aug1b test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_laneatt_culane_20220225.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --config=configs/lane_detection/laneatt/resnet18_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/laneatt/resnet18_culane.py
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18_laneatt_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_resa_culane_20211016.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=4 --use_env main_landet.py --train --config=configs/lane_detection/resa/resnet18_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/resnet18_culane.py
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18_resa_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_resa_tusimple_20211019.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=4 --use_env main_landet.py --train --config=configs/lane_detection/resa/resnet18_tusimple.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/resa/resnet18_tusimple.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet18_resa_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_scnn_culane_20210222.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/resnet18_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/resnet18_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18_scnn_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18_scnn_tusimple_20210424.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/scnn/resnet18_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/scnn/resnet18_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet18_scnn_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18s_lstr-aug_culane_20210721.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/lstr/resnet18s_culane_aug.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/lstr/resnet18s_culane_aug.py
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18s_lstr-aug_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18s_lstr-aug_tusimple_20210629.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/lstr/resnet18s_tusimple_aug.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/lstr/resnet18s_tusimple_aug.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet18s_lstr-aug_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18s_lstr_culane_20210722.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/lstr/resnet18s_culane.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/lstr/resnet18s_culane.py
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet18s_lstr_culane test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet18s_lstr_tusimple_20210701.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/lstr/resnet18s_tusimple.py
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/lstr/resnet18s_tusimple.py
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet18s_lstr_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet34_baseline-aug_tusimple_20210723.pt
|
||||
# Training
|
||||
python -m torch.distributed.launch --nproc_per_node=2 --use_env main_landet.py --train --config=configs/lane_detection/baseline/resnet34_tusimple_aug.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet34_tusimple_aug.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple-aug.sh resnet34_baseline_tusimple-aug test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet34_baseline_culane_20210219.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet34_culane.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet34_culane.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet34_baseline_culane test checkpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet34_baseline_llamas_20210625.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet34_llamas.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --val --config=configs/lane_detection/baseline/resnet34_llamas.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_llamas.sh resnet34_baseline_llamas val checkpoints
|
||||
# Predict lane points for the eval server, find results in ./output
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet34_llamas.py --mixed-precision
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet34_baseline_tusimple_20210424.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/baseline/resnet34_tusimple.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/baseline/resnet34_tusimple.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_tusimple.sh resnet34_baseline_tusimple test checkpoints
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet34_bezierlanenet_culane-aug1b_20211109.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/bezierlanenet/resnet34_culane_aug1b.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --test --config=configs/lane_detection/bezierlanenet/resnet34_culane_aug1b.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_culane.sh resnet34_bezierlanenet_culane-aug1b test checkpoints
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Trained weights: resnet34_bezierlanenet_llamas-aug1b_20211109.pt
|
||||
# Training
|
||||
python main_landet.py --train --config=configs/lane_detection/bezierlanenet/resnet34_llamas_aug1b.py --mixed-precision
|
||||
# Predicting lane points for testing
|
||||
python main_landet.py --val --config=configs/lane_detection/bezierlanenet/resnet34_llamas_aug1b.py --mixed-precision
|
||||
# Testing with official scripts
|
||||
./autotest_llamas.sh resnet34_bezierlanenet_llamas-aug1b val checkpoints
|
||||
# Predict lane points for the eval server, find results in ./output
|
||||
python main_landet.py --test --config=configs/lane_detection/bezierlanenet/resnet34_llamas_aug1b.py --mixed-precision
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user