Commit 81e8a40597cb70ae5126f68891f3bddd9f864355

Authored by Hu Chunming
1 parent d26b9f64

初步完成SDK

algorithm/proj_tz_road_seg_231018/linux/Makefile 100755 → 100644
1 CC = gcc 1 CC = gcc
2 XX = c++ 2 XX = c++
3 3
4 -TARGET = libroad_seg_vdec.so  
5 4
6 -PROJ_ALL_PATH = $(PWD)/../..  
7 -CUR_PROJ_PATH = $(PWD)/.. 5 +
  6 +PROJ_ALL_PATH = /home/cmhu/village_ascend_arm/village_ascend_arm
  7 +CUR_PROJ_PATH = $(PROJ_ALL_PATH)/src
  8 +BIN_PATH = $(PROJ_ALL_PATH)/bin
  9 +
  10 +DEFS=-DOS_LINUX -DENABLE_DVPP_INTERFACE
  11 +
  12 +TARGET = $(BIN_PATH)/test_road_seg
  13 +
8 ACL_PATH = $(ASCEND_AICPU_PATH)/acllib 14 ACL_PATH = $(ASCEND_AICPU_PATH)/acllib
  15 +OPENCV_PATH = $(PROJ_ALL_PATH)/3rdparty/opencv_4_1
  16 +SPDLOG_ROOT = $(PROJ_ALL_PATH)/3rdparty/spdlog-1.9.2/release
  17 +ALGORITHM_PATH = $(PROJ_ALL_PATH)/algorithm
9 18
10 -INCLUDES = -I$(PROJ_ALL_PATH)/common \  
11 - -I$(PROJ_ALL_PATH)/common/dvppx \  
12 - -I$(PROJ_ALL_PATH)/common/road_cnn \  
13 - -I$(CUR_PROJ_PATH)/src \ 19 +INCLUDES = -I$(PROJ_ALL_PATH)/src/common \
  20 + -I$(PROJ_ALL_PATH)/src/common/dvpp \
  21 + -I$(PROJ_ALL_PATH)/src/common/cnn \
  22 + -I$(PROJ_ALL_PATH)/src/common/cnn_cls \
  23 + -I$(PROJ_ALL_PATH)/src/common/road_cnn \
  24 + -I$(PROJ_ALL_PATH)/src/common/dvppx \
  25 + -I$(PROJ_ALL_PATH)/src/common/model_process \
  26 + -I$(CUR_PROJ_PATH)/../va \
  27 + -I$(OPENCV_PATH)/include \
  28 + -I$(OPENCV_PATH)/include/opencv2 \
14 -I$(ACL_PATH)/include \ 29 -I$(ACL_PATH)/include \
  30 + -I$(SPDLOG_ROOT)/include \
  31 + -I$(CUR_PROJ_PATH)/ai_engine_module \
  32 + -I$(ALGORITHM_PATH)/vehicle_analysis \
  33 + -I$(ALGORITHM_PATH)/vid_clothes2/vid_clothes \
  34 + -I$(ALGORITHM_PATH)/hp2/hp \
  35 + -I$(ALGORITHM_PATH)/hcp2/hcp \
  36 +
15 37
16 -CFLAGS = -O2 -std=c++11 -fPIC -shared $(INCLUDES) -DENABLE_DVPP_INTERFACE -D_GLIBCXX_USE_CXX11_ABI=0 38 +# CXXFLAGS = -O0 -std=c++11 $(INCLUDES) -DENABLE_DVPP_INTERFACE -D_GLIBCXX_USE_CXX11_ABI=0
  39 +# CXXFLAGS= -g -O0 -fPIC $(INCLUDES) -lpthread -lrt -lz -fexceptions -std=c++11 -DENABLE_DVPP_INTERFACE -D_GLIBCXX_USE_CXX11_ABI=0 -fvisibility=hidden -Wall -Wno-deprecated -Wdeprecated-declarations -Wl,-Bsymbolic -ldl
17 40
18 local_shared_libs_dirs := \ 41 local_shared_libs_dirs := \
  42 + $(OPENCV_PATH)/lib \
  43 + $(SPDLOG_ROOT)/lib/aarch64-linux-gnu \
19 $(ACL_PATH)/lib64 \ 44 $(ACL_PATH)/lib64 \
20 - $(PROJ_ALL_PATH)/common \ 45 + $(BIN_PATH)/
21 46
22 local_shared_libs := \ 47 local_shared_libs := \
  48 + opencv_world \
23 ascendcl \ 49 ascendcl \
24 acl_dvpp \ 50 acl_dvpp \
25 - sycheck \ 51 + vehicle_analysis \
  52 + vid_clothes \
  53 + hp \
  54 + hcp \
26 55
  56 +
27 SHARED_LIBRARIES := $(foreach shared_lib, $(local_shared_libs), -l$(shared_lib)) 57 SHARED_LIBRARIES := $(foreach shared_lib, $(local_shared_libs), -l$(shared_lib))
28 -SHARED_LIBRARIES_DIRS := $(foreach shared_lib_dir, $(local_shared_libs_dirs), -L$(shared_lib_dir) -Wl,-z,relro,-z,now,-z,noexecstack,-rpath-link,$(shared_lib_dir)) 58 +#SHARED_LIBRARIES_DIRS := $(foreach shared_lib_dir, $(local_shared_libs_dirs), -L$(shared_lib_dir) -Wl,-z,relro,-z,now,-z,noexecstack,-rpath-link,$(shared_lib_dir))
  59 +SHARED_LIBRARIES_DIRS := $(foreach shared_lib_dir, $(local_shared_libs_dirs), -L$(shared_lib_dir) -Wl,-z,relro,-z,now,-z,noexecstack,-rpath,$(shared_lib_dir))
  60 +
  61 +CXXFLAGS= -g -O0 -fPIC $(INCLUDES) $(DEFS) -lpthread -lrt -lz -fexceptions -std=c++11 -DENABLE_DVPP_INTERFACE -D_GLIBCXX_USE_CXX11_ABI=0 -fvisibility=hidden -Wall -Wno-deprecated -Wdeprecated-declarations -Wl,-Bsymbolic -ldl
  62 +CFLAGS = -O2 -std=c++11 $(INCLUDES) -DENABLE_DVPP_INTERFACE -D_GLIBCXX_USE_CXX11_ABI=0
29 63
30 -SRCS := $(wildcard $(CUR_PROJ_PATH)/src/*.cpp)  
31 -SRCS += $(wildcard $(PROJ_ALL_PATH)/common/*.cpp)  
32 -SRCS += $(wildcard $(PROJ_ALL_PATH)/common/road_cnn/*.cpp)  
33 -SRCS += $(wildcard $(PROJ_ALL_PATH)/common/dvppx/*.cpp) 64 +SRCS := $(wildcard /home/cmhu/village_ascend_arm/village_ascend_arm/algorithm/proj_tz_road_seg_231018/src/test/*.cpp) \
  65 + $(wildcard $(CUR_PROJ_PATH)/common/*.cpp) \
  66 + $(wildcard $(CUR_PROJ_PATH)/common/road_cnn/*.cpp) \
  67 + $(wildcard $(CUR_PROJ_PATH)/common/dvppx/*.cpp) \
  68 + $(wildcard /home/cmhu/village_ascend_arm/village_ascend_arm/algorithm/proj_tz_road_seg_231018/src/*.cpp) \
34 69
35 DIRS := $(notdir $(SRCS)) 70 DIRS := $(notdir $(SRCS))
36 OBJS := $(patsubst %cpp, %o, $(DIRS)) 71 OBJS := $(patsubst %cpp, %o, $(DIRS))
@@ -38,16 +73,26 @@ OBJS := $(patsubst %cpp, %o, $(DIRS)) @@ -38,16 +73,26 @@ OBJS := $(patsubst %cpp, %o, $(DIRS))
38 all: $(TARGET) 73 all: $(TARGET)
39 74
40 $(TARGET):$(OBJS) 75 $(TARGET):$(OBJS)
41 - $(XX) $(CFLAGS) -o $@ $^ $(SHARED_LIBRARIES_DIRS) $(SHARED_LIBRARIES)  
42 -%.o:$(CUR_PROJ_PATH)/src/%.cpp  
43 - $(XX) $(CFLAGS) -c $<  
44 -%.o:$(PROJ_ALL_PATH)/common/%.cpp  
45 - $(XX) $(CFLAGS) -c $<  
46 -%.o:$(PROJ_ALL_PATH)/common/road_cnn/%.cpp  
47 - $(XX) $(CFLAGS) -c $<  
48 -%.o:$(PROJ_ALL_PATH)/common/dvppx/%.cpp  
49 - $(XX) $(CFLAGS) -c $< 76 + $(XX) -o $@ $^ $(CXXFLAGS) $(SHARED_LIBRARIES_DIRS) $(SHARED_LIBRARIES)
  77 +
  78 +%.o:/home/cmhu/village_ascend_arm/village_ascend_arm/algorithm/proj_tz_road_seg_231018/src/test/%.cpp
  79 + $(XX) $(CXXFLAGS) -c $<
  80 +
  81 +%.o:$(CUR_PROJ_PATH)/common/%.cpp
  82 + $(XX) $(CXXFLAGS) -c $<
  83 +
  84 +%.o:$(CUR_PROJ_PATH)/common/road_cnn/%.cpp
  85 + $(XX) $(CXXFLAGS) -c $<
  86 +
  87 +%.o:$(CUR_PROJ_PATH)/common/dvppx/%.cpp
  88 + $(XX) $(CXXFLAGS) -c $<
  89 +
  90 +%.o:/home/cmhu/village_ascend_arm/village_ascend_arm/algorithm/proj_tz_road_seg_231018/src/%.cpp
  91 + $(XX) $(CXXFLAGS) -c $<
50 92
51 clean: 93 clean:
52 @rm -f $(TARGET) 94 @rm -f $(TARGET)
53 @rm -f $(OBJS) 95 @rm -f $(OBJS)
  96 +
  97 +cleano:
  98 + @rm -f $(OBJS)
54 \ No newline at end of file 99 \ No newline at end of file
algorithm/proj_tz_road_seg_231018/src/road_seg.cpp
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 #include <time.h> 5 #include <time.h>
6 #include <sys/time.h> 6 #include <sys/time.h>
7 #include "stream_data.h" 7 #include "stream_data.h"
8 -#include "sycheck.h" 8 +// #include "sycheck.h"
9 #include <algorithm> 9 #include <algorithm>
10 #include <map> 10 #include <map>
11 #include <cstring> 11 #include <cstring>
@@ -38,9 +38,9 @@ typedef struct Tools { @@ -38,9 +38,9 @@ typedef struct Tools {
38 38
39 int rs_init(void **handle, rs_param param){ 39 int rs_init(void **handle, rs_param param){
40 40
41 - if (!sycheck()) {  
42 - return SY_VERSION_EXPIRED;  
43 - } 41 + // if (!sycheck()) {
  42 + // return SY_VERSION_EXPIRED;
  43 + // }
44 int ret = SY_SUCCESS; 44 int ret = SY_SUCCESS;
45 Tools* tools = new Tools; 45 Tools* tools = new Tools;
46 // init resource 46 // init resource
algorithm/proj_tz_road_seg_231018/src/test/test.cpp
@@ -606,7 +606,7 @@ int main() { @@ -606,7 +606,7 @@ int main() {
606 rs_param param; 606 rs_param param;
607 // param.modelNames = "tzroad_seg_310p.om"; 607 // param.modelNames = "tzroad_seg_310p.om";
608 // param.modelNames = "tzroad_seg1027_310p.om"; 608 // param.modelNames = "tzroad_seg1027_310p.om";
609 - param.modelNames = "tzroad_seg240108_310p.om"; 609 + param.modelNames = "./models/road_seg/tzroad_seg240108_310p.om";
610 param.thresld = 0.25; 610 param.thresld = 0.25;
611 param.devId = 0; 611 param.devId = 0;
612 612
@@ -625,7 +625,7 @@ int main() { @@ -625,7 +625,7 @@ int main() {
625 cout << "init success " << endl; 625 cout << "init success " << endl;
626 626
627 // const char* img_file_path="imgs/"; 627 // const char* img_file_path="imgs/";
628 - const char* img_file_path="test_img/"; 628 + const char* img_file_path="img/";
629 std::vector<std::string> fileList; 629 std::vector<std::string> fileList;
630 630
631 getAllNm(img_file_path,fileList); 631 getAllNm(img_file_path,fileList);
@@ -697,9 +697,12 @@ int main() { @@ -697,9 +697,12 @@ int main() {
697 cv::addWeighted(cvImg, alpha, overlayed_img, 1 - alpha, 0, overlayed_img); 697 cv::addWeighted(cvImg, alpha, overlayed_img, 1 - alpha, 0, overlayed_img);
698 cv::Mat img_lane = imshow_lanes(overlayed_img, results[b].reg_array, results[b].lane_count); 698 cv::Mat img_lane = imshow_lanes(overlayed_img, results[b].reg_array, results[b].lane_count);
699 699
700 - string strpath = "results/" + file; 700 + string strpath = "res/results/" + file;
701 cv::imwrite(strpath, img_lane); 701 cv::imwrite(strpath, img_lane);
702 702
  703 +
  704 +
  705 +
703 cv::Mat overlayed_direct_img(cv::Size(w,h), CV_8UC3, cv::Scalar(0, 0, 0)); 706 cv::Mat overlayed_direct_img(cv::Size(w,h), CV_8UC3, cv::Scalar(0, 0, 0));
704 // 将车道线标签转换为彩色图像 707 // 将车道线标签转换为彩色图像
705 for (int i = 0; i < h; ++i) { 708 for (int i = 0; i < h; ++i) {
@@ -714,7 +717,7 @@ int main() { @@ -714,7 +717,7 @@ int main() {
714 // 将原始图像和彩色车道线图进行混合 717 // 将原始图像和彩色车道线图进行混合
715 cv::addWeighted(cvImg, alpha, overlayed_direct_img, 1 - alpha, 0, overlayed_direct_img); 718 cv::addWeighted(cvImg, alpha, overlayed_direct_img, 1 - alpha, 0, overlayed_direct_img);
716 cv::Mat img_direct_lane = imshow_lanes(overlayed_direct_img, results[b].reg_array, results[b].lane_count); 719 cv::Mat img_direct_lane = imshow_lanes(overlayed_direct_img, results[b].reg_array, results[b].lane_count);
717 - strpath = "results_direct/" + file; 720 + strpath = "res/results_direct/" + file;
718 cv::imwrite(strpath, img_direct_lane); 721 cv::imwrite(strpath, img_direct_lane);
719 #endif 722 #endif
720 723
@@ -736,13 +739,13 @@ int main() { @@ -736,13 +739,13 @@ int main() {
736 cv::Mat vis_image = mask_to_rgb(image, seg_output); 739 cv::Mat vis_image = mask_to_rgb(image, seg_output);
737 cv::Mat direct_image = mask_to_rgb(image, direct_output); 740 cv::Mat direct_image = mask_to_rgb(image, direct_output);
738 cv::Mat merge_image = mask_to_rgb(image, merge_output); 741 cv::Mat merge_image = mask_to_rgb(image, merge_output);
739 - strpath = "lane_cpp/" + file; 742 + strpath = "res/lane_cpp/" + file;
740 cv::imwrite(strpath, vis_image); 743 cv::imwrite(strpath, vis_image);
741 - strpath = "direct_img/" + file; 744 + strpath = "res/direct_img/" + file;
742 cv::imwrite(strpath, direct_image); 745 cv::imwrite(strpath, direct_image);
743 - strpath = "merge_img/" + file; 746 + strpath = "res/merge_img/" + file;
744 cv::imwrite(strpath, merge_image); 747 cv::imwrite(strpath, merge_image);
745 - strpath = "background_mask/" + file; 748 + strpath = "res/background_mask/" + file;
746 cv::imwrite(strpath, background_mask); 749 cv::imwrite(strpath, background_mask);
747 #endif 750 #endif
748 //debug===================================== 751 //debug=====================================
@@ -820,7 +823,7 @@ int main() { @@ -820,7 +823,7 @@ int main() {
820 } 823 }
821 } 824 }
822 } 825 }
823 - strpath = "reg_cpp/" + file; 826 + strpath = "res/reg_cpp/" + file;
824 cv::imwrite(strpath, vis_image); 827 cv::imwrite(strpath, vis_image);
825 //debug end================================= 828 //debug end=================================
826 829
build/demo/Makefile
@@ -40,7 +40,7 @@ INCLUDES = -I$(PROJ_ALL_PATH)/src/common \ @@ -40,7 +40,7 @@ INCLUDES = -I$(PROJ_ALL_PATH)/src/common \
40 40
41 local_shared_libs_dirs := \ 41 local_shared_libs_dirs := \
42 $(OPENCV_PATH)/lib \ 42 $(OPENCV_PATH)/lib \
43 - $(SPDLOG_ROOT)/lib/aarch64-linux-gnu \ 43 + $(SPDLOG_ROOT)/lib \
44 $(ACL_PATH)/lib64 \ 44 $(ACL_PATH)/lib64 \
45 $(BIN_PATH)/ 45 $(BIN_PATH)/
46 46
src/PicAnalysis.cpp
@@ -187,8 +187,10 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){ @@ -187,8 +187,10 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){
187 m_vehicle_analysis.release_result(result, vec_img.size()); 187 m_vehicle_analysis.release_result(result, vec_img.size());
188 } 188 }
189 189
  190 + bool bConsistent = false;
190 std::vector<RoadInfo> vec_road = m_road_seg_algorithm.detect(vec_img); 191 std::vector<RoadInfo> vec_road = m_road_seg_algorithm.detect(vec_img);
191 if (vec_road.size() == batch_size && vec_result.size() == batch_size) { 192 if (vec_road.size() == batch_size && vec_result.size() == batch_size) {
  193 + bConsistent = true;
192 for (size_t i = 0; i < batch_size; i++) { 194 for (size_t i = 0; i < batch_size; i++) {
193 vec_result[i].vec_line = vec_road[i].vec_line; 195 vec_result[i].vec_line = vec_road[i].vec_line;
194 vec_result[i].vec_road = vec_road[i].vec_road; 196 vec_result[i].vec_road = vec_road[i].vec_road;
@@ -199,6 +201,11 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){ @@ -199,6 +201,11 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){
199 { 201 {
200 vector<VehicleInfo>& vec_info = vec_result[b].info; 202 vector<VehicleInfo>& vec_info = vec_result[b].info;
201 203
  204 + RoadInfo road_info;
  205 + if (bConsistent) {
  206 + road_info = vec_road[b];
  207 + }
  208 +
202 sy_img img = vec_img[b]; 209 sy_img img = vec_img[b];
203 210
204 ImageData src; 211 ImageData src;
@@ -213,11 +220,16 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){ @@ -213,11 +220,16 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){
213 VehicleInfo& result_info = vec_info[c]; 220 VehicleInfo& result_info = vec_info[c];
214 int shot_type=result_info.type; 221 int shot_type=result_info.type;
215 222
  223 + sy_rect vehicle_rect = result_info.vehicle_body_detect_res.rect;
  224 +
  225 + if (road_info.direct_mask.cols > 0 && (1 == shot_type || 0 == shot_type)) {
  226 + result_info.reverse_driving = m_road_seg_algorithm.check_reverse_driving(road_info.direct_mask, vehicle_rect, src.width, src.height, shot_type);
  227 + }
  228 +
216 // 行人 229 // 行人
217 if (6 == shot_type) 230 if (6 == shot_type)
218 { 231 {
219 - sy_rect human_rect = result_info.vehicle_body_detect_res.rect;  
220 - ImageData* human_data = m_crop_util.crop(src, human_rect.left_, human_rect.top_, human_rect.left_ + human_rect.width_, human_rect.top_ + human_rect.height_); 232 + ImageData* human_data = m_crop_util.crop(src, vehicle_rect.left_, vehicle_rect.top_, vehicle_rect.left_ + vehicle_rect.width_, vehicle_rect.top_ + vehicle_rect.height_);
221 233
222 sy_img img; 234 sy_img img;
223 img.w_ = human_data->alignWidth; 235 img.w_ = human_data->alignWidth;
@@ -294,8 +306,7 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){ @@ -294,8 +306,7 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){
294 // analysis_result.motor_helmeted = result_info.mta_res.motor_driver_helmeted.status; 306 // analysis_result.motor_helmeted = result_info.mta_res.motor_driver_helmeted.status;
295 // float score=result_info.mta_res.motor_driver_helmeted.confidence; 307 // float score=result_info.mta_res.motor_driver_helmeted.confidence;
296 308
297 - sy_rect motor_rect = result_info.vehicle_body_detect_res.rect;  
298 - ImageData* motor_data = m_crop_util.crop(src, motor_rect.left_, motor_rect.top_, motor_rect.left_ + motor_rect.width_, motor_rect.top_ + motor_rect.height_); 309 + ImageData* motor_data = m_crop_util.crop(src, vehicle_rect.left_, vehicle_rect.top_, vehicle_rect.left_ + vehicle_rect.width_, vehicle_rect.top_ + vehicle_rect.height_);
299 310
300 sy_img img; 311 sy_img img;
301 img.w_ = motor_data->alignWidth; 312 img.w_ = motor_data->alignWidth;
@@ -314,12 +325,24 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){ @@ -314,12 +325,24 @@ vector&lt;AnalysisResult&gt; PicAnalysis::analysis_img(vector&lt;sy_img&gt; vec_img){
314 vector<int> vec_phone_result = m_motor_phone_algorithm.detect(vec_motor_img); 325 vector<int> vec_phone_result = m_motor_phone_algorithm.detect(vec_motor_img);
315 if (vec_phone_result.size() > 0) 326 if (vec_phone_result.size() > 0)
316 { 327 {
317 - int phoning = vec_phone_result[0]; 328 + result_info.phoning = vec_phone_result[0];
  329 + }
  330 +
  331 + std::vector<HumanCarResult> vec_hcp_result = m_human_car_algorithm.detect(vec_motor_img);
  332 + if (vec_hcp_result.size() > 0) {
  333 + if (vec_hcp_result[11] == 0 || vec_hcp_result[11] == 1) {
  334 + result_info.reverse_driving = m_road_seg_algorithm.check_reverse_driving(road_info.direct_mask, vehicle_rect, src.width, src.height, vec_hcp_result[11]);
  335 + }
318 } 336 }
319 337
320 delete motor_data; 338 delete motor_data;
321 motor_data = nullptr; 339 motor_data = nullptr;
322 } 340 }
  341 +
  342 + //压黄实线
  343 + result_info.cross_line = m_road_seg_algorithm.check_cross_line(road_info.vec_line, vehicle_rect);
  344 + // 压导流线
  345 + result_info.cross_diversion_line = m_road_seg_algorithm.check_cross_region(road_info.vec_road, vehicle_rect, 3); //3是导流线区域
323 } 346 }
324 } 347 }
325 348
src/ai_engine_module/HumanCarAnalysis.cpp
@@ -28,9 +28,9 @@ int HumanCarAnalysis::init(int devId){ @@ -28,9 +28,9 @@ int HumanCarAnalysis::init(int devId){
28 return SY_SUCCESS; 28 return SY_SUCCESS;
29 } 29 }
30 30
31 -int HumanCarAnalysis::detect(vector<sy_img> vec_img){ 31 +std::vector<HumanCarResult> HumanCarAnalysis::detect(vector<sy_img> vec_img){
32 32
33 - ACL_CALL(aclrtSetCurrentContext(ctx), SY_SUCCESS, SY_FAILED); 33 + std::vector<HumanCarResult> vec_result;
34 34
35 const int batchsize = vec_img.size(); 35 const int batchsize = vec_img.size();
36 hcp_analysis_result * results = new hcp_analysis_result[batchsize]; 36 hcp_analysis_result * results = new hcp_analysis_result[batchsize];
@@ -39,6 +39,12 @@ int HumanCarAnalysis::detect(vector&lt;sy_img&gt; vec_img){ @@ -39,6 +39,12 @@ int HumanCarAnalysis::detect(vector&lt;sy_img&gt; vec_img){
39 39
40 do 40 do
41 { 41 {
  42 + ret = aclrtSetCurrentContext(ctx);
  43 + if (SY_SUCCESS != ret) {
  44 + printf("aclrtSetCurrentContext failed!");
  45 + break;
  46 + }
  47 +
42 ret = hcp_batch(m_handle, vec_img.data(), batchsize, results); 48 ret = hcp_batch(m_handle, vec_img.data(), batchsize, results);
43 if (SY_SUCCESS != ret) { 49 if (SY_SUCCESS != ret) {
44 printf("hcp_batch failed!"); 50 printf("hcp_batch failed!");
@@ -47,7 +53,10 @@ int HumanCarAnalysis::detect(vector&lt;sy_img&gt; vec_img){ @@ -47,7 +53,10 @@ int HumanCarAnalysis::detect(vector&lt;sy_img&gt; vec_img){
47 53
48 for(int batchIdx = 0;batchIdx<batchsize;batchIdx++){ 54 for(int batchIdx = 0;batchIdx<batchsize;batchIdx++){
49 for (int i = 0; i < HCP_ATTRI_INDEX_SIZE; i++) { 55 for (int i = 0; i < HCP_ATTRI_INDEX_SIZE; i++) {
50 - printf("hcp index:%d,confidence:%f\n",results[batchIdx].res_objs[i].res_index,results[batchIdx].res_objs[i].res_prob); 56 + HumanCarResult one_result;
  57 + one_result.type = results[batchIdx].res_objs[i].res_index;
  58 + one_result.prob = results[batchIdx].res_objs[i].res_prob;
  59 + vec_result.push_back(one_result);
51 } 60 }
52 } 61 }
53 } while (0); 62 } while (0);
@@ -56,7 +65,7 @@ int HumanCarAnalysis::detect(vector&lt;sy_img&gt; vec_img){ @@ -56,7 +65,7 @@ int HumanCarAnalysis::detect(vector&lt;sy_img&gt; vec_img){
56 delete [] results; 65 delete [] results;
57 } 66 }
58 67
59 - return ret; 68 + return vec_result;
60 } 69 }
61 70
62 int HumanCarAnalysis::release() { 71 int HumanCarAnalysis::release() {
src/ai_engine_module/HumanCarAnalysis.h
1 #include "include.h" 1 #include "include.h"
2 #include "human_car_parsing.h" 2 #include "human_car_parsing.h"
3 3
  4 +struct HumanCarResult {
  5 + int type;
  6 + float prob;
  7 +};
  8 +
4 class HumanCarAnalysis 9 class HumanCarAnalysis
5 { 10 {
6 public: 11 public:
@@ -9,7 +14,7 @@ public: @@ -9,7 +14,7 @@ public:
9 14
10 int init(int devId); 15 int init(int devId);
11 16
12 - int detect(vector<sy_img> vec_img); 17 + std::vector<HumanCarResult> detect(vector<sy_img> vec_img);
13 18
14 private: 19 private:
15 int release(); 20 int release();
src/ai_engine_module/RoadSegAnalysis.cpp
@@ -32,6 +32,489 @@ void lanes_process(const rs_lane* lanes, int lane_count, std::vector&lt;std::pair&lt;s @@ -32,6 +32,489 @@ void lanes_process(const rs_lane* lanes, int lane_count, std::vector&lt;std::pair&lt;s
32 } 32 }
33 } 33 }
34 34
  35 +cv::Mat mask_to_rgb(cv::Mat img, cv::Mat mask) {
  36 + cv::Mat rgb = img.clone();
  37 + int reg_cls = 9;
  38 + for (int i = 0; i < rgb.rows; i++) {
  39 + for (int j = 0; j < rgb.cols; j++) {
  40 + for (int k = 1; k < reg_cls; k++) {
  41 + if (mask.at<int>(i,j) == k) {
  42 + rgb.at<cv::Vec3b>(i,j)[0] = seg_colors[k][0];
  43 + rgb.at<cv::Vec3b>(i,j)[1] = seg_colors[k][1];
  44 + rgb.at<cv::Vec3b>(i,j)[2] = seg_colors[k][2];
  45 + }
  46 + }
  47 + }
  48 + }
  49 + return rgb;
  50 +}
  51 +
  52 +float contourArea(std::vector<cv::Point> contour, cv::Point2f& center) {
  53 + cv::RotatedRect rect = cv::minAreaRect(contour); // 最小外接矩形 rect[0]中心点 rect[1]宽 高 rect[2]旋转角度
  54 + center = rect.center;
  55 + return cv::contourArea(contour);
  56 +}
  57 +
  58 +int Mask2LanePoints(const cv::Mat& pred, std::vector<std::vector<cv::Point>>&lanes, std::vector<int>& cats) {
  59 + std::vector<int> labels = {9, 10, 11, 12, 13};
  60 + for(auto cat: labels) {
  61 + cv::Mat b_masks, measure_labels, stats, centroids;
  62 + cv::compare(pred, cat, b_masks, cv::CMP_EQ); //将pred元素逐个和cat比较,相同255,不同0
  63 + // cv::threshold(pred, b_masks, 126, 255, cv::THRESH_OTSU); //生成二值图
  64 + // 连通域计算 b_masks:闭操作后的二值图像 measure_labels:和原图一样大的标记图 centroids:nccomps×2的矩阵,表示每个连通域的质心(x, y)
  65 + // stats:nccomps×5的矩阵 表示每个连通区域的外接矩形和面积(x, y, w, h, area),索引0是背景信息
  66 + int nccomps = cv::connectedComponentsWithStats (b_masks, measure_labels, stats, centroids, 4); //8
  67 + for(int cv_measure_id = 1; cv_measure_id < nccomps; cv_measure_id++ ) { //跳过背景信息0
  68 + cv::Mat cv_measure_mask;
  69 + cv::compare(measure_labels, cv_measure_id, cv_measure_mask, cv::CMP_EQ);
  70 + std::vector< std::vector< cv::Point> > contours;
  71 + cv::findContours(cv_measure_mask,contours,cv::noArray(),cv::RETR_TREE,cv::CHAIN_APPROX_SIMPLE);
  72 + std::vector<cv::Point> contours_poly;
  73 + for (int j = 0; j < contours.size();j++) {
  74 + // float area = cv::contourArea(contours[j]); if (area < 60) continue; //30
  75 + float area = cv::contourArea(contours[j]); if (area < 30) continue; //30
  76 + cv::approxPolyDP(cv::Mat(contours[j]), contours_poly, 6, true); //减小epsilon提高拟合精度(需要调试,epsilon过小会使多边形不够简化,单条线变多条)
  77 + if (contours_poly.size() == 1) continue;
  78 + lanes.push_back(contours_poly);
  79 + cats.push_back(cat);
  80 + }
  81 + }
  82 + }
  83 + return 0;
  84 +}
  85 +
  86 +int findMainMaskV2(bool large_resolution, const cv::Mat& solve_masks, const std::vector<int>& labels, std::vector<std::vector<cv::Point>> &poly_masks, std::vector<int> &region_classes, std::vector<std::vector<cv::Point>> &main_masks, cv::Mat& new_mask, cv::Mat& new_mask_forlane) {
  87 + int h = 360, w = 640;
  88 + int seg_num_cls = 8;
  89 + int seg_num_seg = 4;
  90 + int seg_min_region_area = 512; //10
  91 + // mask部分区域
  92 + if (large_resolution) solve_masks(cv::Rect(0, 0, w, int(h * 0.14))) = 0;
  93 + else solve_masks(cv::Rect(0, 0, w, int(h * 0.22))) = 0;
  94 + solve_masks(cv::Rect(0, int(h * 0.95), w, int(h * 0.05))) = 0;
  95 + solve_masks(cv::Rect(0, 0, int(w * 0.02), h)) = 0;
  96 + solve_masks(cv::Rect(int(w * 0.95), 0, int(w * 0.05), h)) = 0;
  97 +
  98 + float hf_w = w*0.5;
  99 + std::vector<std::vector<cv::Point>> all_emerg_contours, all_com_contours, mid_com_contours, mid_zd_contours;
  100 + std::vector<cv::Point> emerg_contours, mid_contours, com_contours, zd_contours;
  101 + float emerg_max_area = 0, max_area = 0, zd_max_area = 0;
  102 +
  103 + for(auto cat: labels) {
  104 + if (cat == 0 || cat > seg_num_seg)
  105 + continue;
  106 +
  107 + // std::cout << cat << std::endl;
  108 + cv::Mat b_masks, measure_labels, stats, centroids;
  109 + cv::Mat n_masks = cv::Mat_<int>(h,w);
  110 + n_masks = cv::Scalar(255);
  111 + cv::compare(solve_masks, cat, b_masks, cv::CMP_EQ); //将solve_masks元素逐个和cat比较,相同255,不同0
  112 + //连通域计算
  113 + int nccomps = cv::connectedComponentsWithStats (b_masks, measure_labels, stats, centroids, 8);
  114 + for(int i = 1; i < nccomps; i++ ) { //跳过背景信息0
  115 + //移除过小的区域,并将对应位置置为0
  116 + if (stats.at<int>(i, cv::CC_STAT_AREA) < seg_min_region_area) {
  117 + cv::Mat comparison_result;
  118 + cv::compare(measure_labels, cv::Scalar(i), comparison_result, cv::CMP_EQ); //相等为255不等为0
  119 + n_masks.setTo(0, comparison_result); // 将comparison_result中非零区域置为0
  120 + cv::bitwise_and(solve_masks,n_masks,solve_masks);
  121 + continue;
  122 + }
  123 +
  124 + double centr_x = centroids.at<double>(i, 0);
  125 + double centr_y = centroids.at<double>(i, 1);
  126 +
  127 + int region_class = cat;
  128 + cv::Mat region_mask;
  129 + cv::bitwise_and(measure_labels,n_masks,measure_labels);/////
  130 + cv::compare(measure_labels, i, region_mask, cv::CMP_EQ);
  131 +
  132 + // 闭运算,先膨胀再腐蚀,去除较暗部分
  133 + cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(17, 5));
  134 + cv::morphologyEx(region_mask, region_mask, cv::MORPH_CLOSE, kernel);
  135 +
  136 + std::vector< std::vector< cv::Point> > contours;
  137 + cv::findContours(region_mask,contours,cv::noArray(),cv::RETR_TREE,cv::CHAIN_APPROX_SIMPLE);
  138 + std::vector<cv::Point> contours_poly;
  139 +
  140 + // 应急车道
  141 + if(region_class == 2) {
  142 + for (auto contour : contours) {
  143 + cv::Point2f center;
  144 + float area_ = contourArea(contour, center);
  145 + if (area_ > emerg_max_area) {
  146 + emerg_max_area = area_;
  147 + emerg_contours = contour;
  148 + }
  149 + all_emerg_contours.push_back(contour);
  150 + }
  151 + }
  152 +
  153 + // 普通行车道
  154 + if(region_class == 1) {
  155 + for (auto contour : contours) {
  156 + cv::Point2f center;
  157 + float area_ = contourArea(contour, center);
  158 + if (area_ > max_area) {
  159 + max_area = area_;
  160 + com_contours = contour;
  161 + }
  162 + // 取中点所在的连通域
  163 + cv::Point2f point(int(w*0.5),int(h*0.95)-1);
  164 + double distance = cv::pointPolygonTest(contour, point, false);
  165 + if (distance >= 0) {
  166 + mid_contours = contour;
  167 + hf_w = center.x;
  168 + continue;
  169 + }
  170 + if (abs(center.x - hf_w) < 90) // 取中点所在区域左右90像素内的连通域 230307
  171 + mid_com_contours.push_back(contour);
  172 + }
  173 + }
  174 +
  175 + // 导流线
  176 + if(region_class == 3) {
  177 + for (auto contour : contours) {
  178 + cv::Point2f center;
  179 + float area_ = contourArea(contour, center);
  180 + if (area_ > zd_max_area) {
  181 + zd_max_area = area_;
  182 + zd_contours = contour;
  183 + }
  184 + // if (abs(center.x - w*0.5) < 200) // 取中点左右200像素内的连通域
  185 + if (abs(center.x - w*0.5) < 250) // 取中点左右250像素内的连通域
  186 + mid_zd_contours.push_back(contour);
  187 + }
  188 + }
  189 +
  190 + // 拟合各行驶区域
  191 + for (int j = 0; j < contours.size();j++) {
  192 + cv::approxPolyDP(cv::Mat(contours[j]), contours_poly, 10, true);
  193 + if (contours_poly.size() <= 2) continue;
  194 + poly_masks.push_back(contours_poly);
  195 + region_classes.push_back(cat);
  196 + }
  197 + }
  198 + }
  199 +
  200 + // cv::Mat new_mask(h, w, CV_8UC3, cv::Scalar(0,0,0));
  201 + if (!mid_contours.empty()) {
  202 + // cv::fillConvexPoly(new_mask, mid_contours, cv::Scalar(255,255,255)); //填充255
  203 + cv::fillConvexPoly(new_mask, mid_contours, cv::Scalar(255)); //填充255
  204 + cv::fillConvexPoly(new_mask_forlane, mid_contours, cv::Scalar(255)); //填充255
  205 + std::vector<cv::Point> contours_poly;
  206 + cv::approxPolyDP(cv::Mat(mid_contours), contours_poly, 10, true);
  207 + if (contours_poly.size() > 2) main_masks.push_back(contours_poly);
  208 + all_com_contours.push_back(mid_contours);
  209 + }
  210 + else {
  211 + // cv::fillConvexPoly(new_mask, com_contours, cv::Scalar(255,255,255)); //填充255
  212 + cv::fillConvexPoly(new_mask, com_contours, cv::Scalar(255)); //填充255
  213 + cv::fillConvexPoly(new_mask_forlane, com_contours, cv::Scalar(255)); //填充255
  214 + std::vector<cv::Point> contours_poly;
  215 + cv::approxPolyDP(cv::Mat(com_contours), contours_poly, 10, true);
  216 + if (contours_poly.size() > 2) main_masks.push_back(contours_poly);
  217 + all_com_contours.push_back(com_contours);
  218 + }
  219 +
  220 + for (auto contour_ : mid_com_contours) {
  221 + // cv::fillConvexPoly(new_mask, contour_, cv::Scalar(255,255,255)); //填充255
  222 + cv::fillConvexPoly(new_mask, contour_, cv::Scalar(255)); //填充255
  223 + cv::fillConvexPoly(new_mask_forlane, contour_, cv::Scalar(255)); //填充255
  224 + std::vector<cv::Point> contours_poly;
  225 + cv::approxPolyDP(cv::Mat(contour_), contours_poly, 10, true);
  226 + if (contours_poly.size() > 2) main_masks.push_back(contours_poly);
  227 + all_com_contours.push_back(contour_);
  228 + }
  229 +
  230 + if (!emerg_contours.empty() && !all_emerg_contours.empty()) {
  231 + if (all_emerg_contours.size() == 1)
  232 + // cv::fillConvexPoly(new_mask, emerg_contours, cv::Scalar(255,255,255));
  233 + cv::fillConvexPoly(new_mask, emerg_contours, cv::Scalar(255)); //填充255
  234 + else {
  235 + float sum_centerx = 0, sum_centery = 0;
  236 + float length_emerg_com = 9999999;
  237 + // 计算选中行车道的平均中心点
  238 + for (auto contour_ : all_com_contours) {
  239 + cv::Point2f center;
  240 + float area_ = contourArea(contour_, center);
  241 + sum_centerx += center.x;
  242 + sum_centery += center.y;
  243 + }
  244 + float ave_centerx = sum_centerx / all_com_contours.size();
  245 + float ave_centery = sum_centery / all_com_contours.size();
  246 + // 选择距离最近的应急车道且面积不小于最大应急车道的一半
  247 + for (auto econtour_ : all_emerg_contours) {
  248 + cv::Point2f center;
  249 + float area_ = contourArea(econtour_, center);
  250 + float tmp_length = sqrt(pow((ave_centerx-center.x),2.0) + pow((ave_centery-center.y),2.0));
  251 + if (tmp_length < length_emerg_com and area_ > 0.7*emerg_max_area) {
  252 + length_emerg_com = tmp_length;
  253 + emerg_contours = econtour_;
  254 + }
  255 + }
  256 + // cv::fillConvexPoly(new_mask, emerg_contours, cv::Scalar(255,255,255));
  257 + cv::fillConvexPoly(new_mask, emerg_contours, cv::Scalar(255)); //填充255
  258 + }
  259 + }
  260 +
  261 + if (!mid_zd_contours.empty()) {
  262 + for (auto contour_ : mid_zd_contours)
  263 + // cv::fillConvexPoly(new_mask, contour_, cv::Scalar(255,255,255)); //填充255
  264 + cv::fillConvexPoly(new_mask, contour_, cv::Scalar(255)); //填充255
  265 + }
  266 +
  267 + // 膨胀腐蚀,去除行车道和应急车道间的空洞
  268 + cv::Mat kernel_ = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(17, 5));
  269 + cv::morphologyEx(new_mask, new_mask, cv::MORPH_CLOSE, kernel_);
  270 + cv::morphologyEx(new_mask_forlane, new_mask_forlane, cv::MORPH_CLOSE, kernel_);
  271 +
  272 + return 0;
  273 +}
  274 +
  275 +
  276 +cv::Mat seg_post_process(bool large_resolution, unsigned char *seg_array, std::vector<std::pair<std::vector<cv::Point>, int>> combined, std::vector<std::vector<cv::Point>> &poly_masks, std::vector<int> &region_classes, std::vector<std::vector<cv::Point>> &lanes, std::vector<int> &cats, std::map<double, int> &x_sort, cv::Mat &background_mask) {
  277 + std::vector<int> pred_cls;
  278 + int h = 360, w = 640;
  279 + cv::Mat lanes_masks = cv::Mat_<int>(h,w); //正常分割结果
  280 + cv::Mat mask_rmlane = cv::Mat_<int>(h,w); //车道线区域置为背景
  281 + cv::Mat solve_masks = cv::Mat_<int>(h,w); //计算主行驶区域用(虚线及减速标线归入行车道)
  282 + int step_size = h*w;
  283 + int seg_min_region_area = 512; //1024
  284 +
  285 + for (int i = 0; i < h; ++i) {
  286 + for (int j = 0; j < w; ++j) {
  287 + int max_cls = seg_array[(i * w + j)];
  288 + lanes_masks.ptr<int>(i)[j] = max_cls;
  289 + pred_cls.push_back(max_cls);
  290 + mask_rmlane.ptr<int>(i)[j] = max_cls;
  291 + solve_masks.ptr<int>(i)[j] = max_cls;
  292 + }
  293 + }
  294 +
  295 + for (const auto& lane_info : combined) {
  296 + const auto& xys = lane_info.first;
  297 + int cls = lane_info.second;
  298 + if (cls == 1) cls = 9; /*黄实线*/ if (cls == 2) cls = 10; /*白实线*/
  299 + if (cls == 3) cls = 11; /*虚线*/ if (cls == 4) cls = 12; /*黄虚线*/
  300 + if (cls == 5) cls = 13; /*车道中线*/
  301 + for (size_t i = 1; i < xys.size(); ++i) {
  302 + cv::line(lanes_masks, xys[i - 1], xys[i], cls, 4); //绘制车道线用于求连通域
  303 + if (cls == 11 || cls == 12) cls = 1; //求主行驶区域时将虚线归入行车道
  304 + cv::line(solve_masks, xys[i - 1], xys[i], cls, 4);
  305 +
  306 + }
  307 + }
  308 +
  309 + // 求背景区域--mask车道区域,场景变化用
  310 + cv::compare(lanes_masks, 0, background_mask, cv::CMP_EQ); //将lanes_masks元素逐个和0比较,相同255,不同0
  311 +#if 0
  312 + //--mask远处区域------------------------------------------------
  313 + cv::Mat mask_black = mask_rmlane.clone();
  314 + if (large_resolution) mask_black(cv::Rect(0, 0, w, int(h * 0.14))) = 0;
  315 + else mask_black(cv::Rect(0, 0, w, int(h * 0.22))) = 0;
  316 + mask_black(cv::Rect(0, int(h * 0.95), w, int(h * 0.05))) = 0;
  317 + mask_black(cv::Rect(0, 0, int(w * 0.02), h)) = 0;
  318 + mask_black(cv::Rect(int(w * 0.95), 0, int(w * 0.05), h)) = 0;
  319 + mask_rmlane = mask_black;
  320 + //-------------------------------------------------------------------------
  321 +#endif
  322 + //2.去重获取预测到的类别
  323 + std::vector<int> labels(pred_cls);
  324 + std::sort(labels.begin(),labels.end());
  325 + labels.erase(std::unique(labels.begin(),labels.end()),labels.end());
  326 +
  327 + //4.求车道线区域
  328 + int flag = Mask2LanePoints(lanes_masks, lanes, cats); ///////////////////////// 车道线如何与mask结合
  329 +
  330 + //5.求道路区域
  331 + int count = 0;
  332 + for(auto cat: labels) {
  333 + // std::cout << cat << std::endl;
  334 + cv::Mat b_masks, measure_labels, stats, centroids;
  335 + cv::Mat n_masks = cv::Mat_<int>(h,w);
  336 + n_masks = cv::Scalar(255);
  337 + cv::compare(mask_rmlane, cat, b_masks, cv::CMP_EQ); //将mask_rmlane元素逐个和cat比较,相同255,不同0
  338 + //连通域计算
  339 + int nccomps = cv::connectedComponentsWithStats (b_masks, measure_labels, stats, centroids, 8);
  340 + for(int i = 1; i < nccomps; i++ ) { //跳过背景信息0
  341 + //移除过小的区域,并将对应位置置为0
  342 + if (stats.at<int>(i, cv::CC_STAT_AREA) < seg_min_region_area) {
  343 + cv::Mat comparison_result;
  344 + cv::compare(measure_labels, cv::Scalar(i), comparison_result, cv::CMP_EQ); //相等为255不等为0
  345 + n_masks.setTo(0, comparison_result); // 将comparison_result中非零区域置为0
  346 +
  347 + cv::bitwise_and(mask_rmlane,n_masks,mask_rmlane);
  348 + continue;
  349 + }
  350 +
  351 + double centr_x = centroids.at<double>(i, 0);
  352 + double centr_y = centroids.at<double>(i, 1);
  353 +
  354 + int region_class = cat;
  355 + // printf("region_class: %d\n", region_class);
  356 + cv::Mat region_mask;
  357 + cv::bitwise_and(measure_labels,n_masks,measure_labels);/////
  358 + cv::compare(measure_labels, i, region_mask, cv::CMP_EQ);
  359 +
  360 + std::vector< std::vector< cv::Point> > contours;
  361 + cv::findContours(region_mask,contours,cv::noArray(),cv::RETR_TREE,cv::CHAIN_APPROX_SIMPLE);
  362 + std::vector<cv::Point> contours_poly;
  363 + for (int j = 0; j < contours.size();j++) {
  364 + cv::approxPolyDP(cv::Mat(contours[j]), contours_poly, 10, true);
  365 + if (contours_poly.size() <= 2) continue;
  366 + poly_masks.push_back(contours_poly);
  367 + region_classes.push_back(cat);
  368 + if (x_sort.count(centr_x)) centr_x += 0.0001;
  369 + x_sort.insert(std::make_pair(centr_x, count));
  370 + ++ count;
  371 + // for (auto iter: contours_poly) {
  372 + // std::cout << "contour " << iter << " " << cat << std::endl;
  373 + // }
  374 +
  375 + }
  376 + }
  377 + }
  378 +
  379 +#if 0
  380 + //6.draw lanes
  381 + for (int i = 0; i < lanes.size(); ++i) {
  382 + int thickness = 4;
  383 + for (int j = 0; j < lanes[i].size()-1; ++j) {
  384 + cv::line(mask_rmlane, lanes[i][j], lanes[i][j+1], cats[i], thickness);
  385 + // std::cout << lanes[i][j] << " " << lanes[i][j+1] << " " << cats[i] << std::endl;
  386 + }
  387 + }
  388 +#endif
  389 + return mask_rmlane;
  390 +}
  391 +
  392 +
  393 +
  394 +cv::Mat direct_post_process(bool large_resolution, unsigned char *direct_array, std::vector<std::vector<cv::Point>> &poly_masks, std::vector<int> &region_classes) {
  395 + std::vector<int> pred_cls;
  396 + int h = 360, w = 640, step_size = h*w, seg_min_region_area = 512;
  397 + cv::Mat mask_rmlane = cv::Mat_<int>(h,w); //车道线区域置为背景
  398 + for (int i = 0; i < h; ++i) {
  399 + for (int j = 0; j < w; ++j) {
  400 + int max_cls = direct_array[(i * w + j)];
  401 + pred_cls.push_back(max_cls);
  402 + mask_rmlane.ptr<int>(i)[j] = max_cls;
  403 + }
  404 + }
  405 +
  406 +#if 1 /*mask远处区域*/
  407 + cv::Mat mask_black = mask_rmlane.clone();
  408 + if (large_resolution) mask_black(cv::Rect(0, 0, w, int(h * 0.14))) = 0;
  409 + else mask_black(cv::Rect(0, 0, w, int(h * 0.22))) = 0;
  410 + mask_black(cv::Rect(0, int(h * 0.95), w, int(h * 0.05))) = 0;
  411 + mask_black(cv::Rect(0, 0, int(w * 0.02), h)) = 0;
  412 + mask_black(cv::Rect(int(w * 0.95), 0, int(w * 0.05), h)) = 0;
  413 + mask_rmlane = mask_black;
  414 +#endif
  415 + //去重获取预测到的类别
  416 + std::vector<int> labels(pred_cls);
  417 + std::sort(labels.begin(),labels.end());
  418 + labels.erase(std::unique(labels.begin(),labels.end()),labels.end());
  419 +
  420 + //求道路区域
  421 + for(auto cat: labels) {
  422 + cv::Mat b_masks, measure_labels, stats, centroids;
  423 + cv::Mat n_masks = cv::Mat_<int>(h,w); n_masks = cv::Scalar(255);
  424 + cv::compare(mask_rmlane, cat, b_masks, cv::CMP_EQ); //将mask_rmlane元素逐个和cat比较,相同255,不同0
  425 + //连通域计算
  426 + int nccomps = cv::connectedComponentsWithStats (b_masks, measure_labels, stats, centroids, 8);
  427 + for(int i = 1; i < nccomps; i++ ) { //跳过背景信息0
  428 + if (stats.at<int>(i, cv::CC_STAT_AREA) < seg_min_region_area) { //移除过小的区域,并将对应位置置为0
  429 + cv::Mat comparison_result;
  430 + cv::compare(measure_labels, cv::Scalar(i), comparison_result, cv::CMP_EQ); //相等为255不等为0
  431 + n_masks.setTo(0, comparison_result); // 将comparison_result中非零区域置为0
  432 + cv::bitwise_and(mask_rmlane,n_masks,mask_rmlane);
  433 + continue;
  434 + }
  435 +
  436 + cv::Mat region_mask;
  437 + cv::bitwise_and(measure_labels,n_masks,measure_labels);/////
  438 + cv::compare(measure_labels, i, region_mask, cv::CMP_EQ);
  439 +
  440 + std::vector<std::vector<cv::Point>> contours;
  441 + cv::findContours(region_mask,contours,cv::noArray(),cv::RETR_TREE,cv::CHAIN_APPROX_SIMPLE);
  442 + std::vector<cv::Point> contours_poly;
  443 + for (int j = 0; j < contours.size();j++) {
  444 + cv::approxPolyDP(cv::Mat(contours[j]), contours_poly, 10, true);
  445 + if (contours_poly.size() <= 2) continue;
  446 + poly_masks.push_back(contours_poly);
  447 + region_classes.push_back(cat);
  448 + }
  449 + }
  450 + }
  451 + return mask_rmlane;
  452 +}
  453 +
  454 +
  455 +// 合并来去区域
  456 +cv::Mat merge_direct_process(bool large_resolution, unsigned char *direct_array, std::vector<std::vector<cv::Point>> &poly_masks, std::vector<int> &region_classes) {
  457 + std::vector<int> pred_cls;
  458 + int h = 360, w = 640, step_size = h*w, seg_min_region_area = 512;
  459 + cv::Mat mask_rmlane = cv::Mat_<int>(h,w); //车道线区域置为背景
  460 + for (int i = 0; i < h; ++i) {
  461 + for (int j = 0; j < w; ++j) {
  462 + int max_cls = direct_array[(i * w + j)];
  463 + if (max_cls == 1 || max_cls == 2) max_cls = 5; // 来去区域合并的类别为5
  464 + if (max_cls == 3 || max_cls == 4) max_cls = 6; // 近远区域合并的类别为6
  465 + pred_cls.push_back(max_cls);
  466 + mask_rmlane.ptr<int>(i)[j] = max_cls;
  467 + }
  468 + }
  469 +
  470 +#if 0 /*mask远处区域*/
  471 + cv::Mat mask_black = mask_rmlane.clone();
  472 + if (large_resolution) mask_black(cv::Rect(0, 0, w, int(h * 0.14))) = 0;
  473 + else mask_black(cv::Rect(0, 0, w, int(h * 0.22))) = 0;
  474 + mask_black(cv::Rect(0, int(h * 0.95), w, int(h * 0.05))) = 0;
  475 + mask_black(cv::Rect(0, 0, int(w * 0.02), h)) = 0;
  476 + mask_black(cv::Rect(int(w * 0.95), 0, int(w * 0.05), h)) = 0;
  477 + mask_rmlane = mask_black;
  478 +#endif
  479 + //去重获取预测到的类别
  480 + std::vector<int> labels(pred_cls);
  481 + std::sort(labels.begin(),labels.end());
  482 + labels.erase(std::unique(labels.begin(),labels.end()),labels.end());
  483 +
  484 + //求道路区域
  485 + for(auto cat: labels) {
  486 + cv::Mat b_masks, measure_labels, stats, centroids;
  487 + cv::Mat n_masks = cv::Mat_<int>(h,w); n_masks = cv::Scalar(255);
  488 + cv::compare(mask_rmlane, cat, b_masks, cv::CMP_EQ); //将mask_rmlane元素逐个和cat比较,相同255,不同0
  489 + //连通域计算
  490 + int nccomps = cv::connectedComponentsWithStats (b_masks, measure_labels, stats, centroids, 8);
  491 + for(int i = 1; i < nccomps; i++ ) { //跳过背景信息0
  492 + if (stats.at<int>(i, cv::CC_STAT_AREA) < seg_min_region_area) { //移除过小的区域,并将对应位置置为0
  493 + cv::Mat comparison_result;
  494 + cv::compare(measure_labels, cv::Scalar(i), comparison_result, cv::CMP_EQ); //相等为255不等为0
  495 + n_masks.setTo(0, comparison_result); // 将comparison_result中非零区域置为0
  496 + cv::bitwise_and(mask_rmlane,n_masks,mask_rmlane);
  497 + continue;
  498 + }
  499 +
  500 + cv::Mat region_mask;
  501 + cv::bitwise_and(measure_labels,n_masks,measure_labels);/////
  502 + cv::compare(measure_labels, i, region_mask, cv::CMP_EQ);
  503 +
  504 + std::vector<std::vector<cv::Point>> contours;
  505 + cv::findContours(region_mask,contours,cv::noArray(),cv::RETR_TREE,cv::CHAIN_APPROX_SIMPLE);
  506 + std::vector<cv::Point> contours_poly;
  507 + for (int j = 0; j < contours.size();j++) {
  508 + cv::approxPolyDP(cv::Mat(contours[j]), contours_poly, 10, true);
  509 + if (contours_poly.size() <= 2) continue;
  510 + poly_masks.push_back(contours_poly);
  511 + region_classes.push_back(cat);
  512 + }
  513 + }
  514 + }
  515 + return mask_rmlane;
  516 +}
  517 +
35 RoadSegAnalysis::RoadSegAnalysis(/* args */) 518 RoadSegAnalysis::RoadSegAnalysis(/* args */)
36 { 519 {
37 } 520 }
@@ -104,6 +587,9 @@ std::vector&lt;RoadInfo&gt; RoadSegAnalysis::detect(vector&lt;sy_img&gt; vec_img){ @@ -104,6 +587,9 @@ std::vector&lt;RoadInfo&gt; RoadSegAnalysis::detect(vector&lt;sy_img&gt; vec_img){
104 } 587 }
105 588
106 one_road.vec_road = parse_seg(one_result, vec_img[b]); 589 one_road.vec_road = parse_seg(one_result, vec_img[b]);
  590 + one_road.direct_mask = parse_direct(one_result, vec_img[b]);
  591 +
  592 + post_direct(one_result, vec_img[b]);
107 593
108 vec_road.push_back(one_road); 594 vec_road.push_back(one_road);
109 } 595 }
@@ -167,7 +653,7 @@ std::vector&lt;SegInfo&gt; RoadSegAnalysis::parse_seg(rs_result one_result, sy_img src @@ -167,7 +653,7 @@ std::vector&lt;SegInfo&gt; RoadSegAnalysis::parse_seg(rs_result one_result, sy_img src
167 SegInfo info; 653 SegInfo info;
168 info.seg_type = cls; 654 info.seg_type = cls;
169 // cv::Scalar color(lane_colors[cls][0],lane_colors[cls][1],lane_colors[cls][2]); 655 // cv::Scalar color(lane_colors[cls][0],lane_colors[cls][1],lane_colors[cls][2]);
170 - for (size_t i = 1; i < xys.size(); ++i) { 656 + for (size_t i = 0; i < xys.size(); ++i) {
171 // cv::line(overlayed_img, xys[i - 1], xys[i], color, 4); 657 // cv::line(overlayed_img, xys[i - 1], xys[i], color, 4);
172 sy_point pt; 658 sy_point pt;
173 pt.x_ = xys[i].x; 659 pt.x_ = xys[i].x;
@@ -178,4 +664,217 @@ std::vector&lt;SegInfo&gt; RoadSegAnalysis::parse_seg(rs_result one_result, sy_img src @@ -178,4 +664,217 @@ std::vector&lt;SegInfo&gt; RoadSegAnalysis::parse_seg(rs_result one_result, sy_img src
178 } 664 }
179 665
180 return vec_seg; 666 return vec_seg;
  667 +}
  668 +
  669 +cv::Mat RoadSegAnalysis::parse_direct(rs_result one_result, sy_img src){
  670 + int src_width = src.w_;
  671 +
  672 + std::vector<std::pair<std::vector<cv::Point>, int>> combined;
  673 + lanes_process(one_result.reg_array, one_result.lane_count, combined);
  674 + std::vector<std::vector<cv::Point>> poly_masks, lanes, direct_masks, merge_masks;
  675 + std::vector<int> region_classes, cats, direct_classes, merge_classes;
  676 + cv::Mat background_mask;
  677 + std::map<double, int> x_sort;
  678 + bool large_resolution = false;
  679 + if (src_width > 1920) large_resolution = true;
  680 + cv::Mat seg_output = seg_post_process(large_resolution, one_result.seg_array, combined, poly_masks, region_classes, lanes, cats, x_sort, background_mask); //m_masks:mask前的结果 poly_masks后的结果
  681 + cv::Mat direct_output = direct_post_process(large_resolution, one_result.direct_seg, direct_masks, direct_classes);
  682 +
  683 +
  684 + // std::vector<std::vector<cv::Point>> direct_masks;
  685 + // std::vector<int> direct_classes;
  686 + // bool large_resolution = false;
  687 + // if (src_width > 1920) large_resolution = true;
  688 + // cv::Mat direct_output = direct_post_process(large_resolution, one_result.direct_seg, direct_masks, direct_classes);
  689 +
  690 + // cv::Mat image(cv::Size(640, 360), CV_8UC3, cv::Scalar(0, 0, 0));
  691 + // cv::Mat merge_image = mask_to_rgb(image, direct_output);
  692 + // cv::imwrite("direct.jpg", merge_image);
  693 +
  694 + int direct = direct_output.at<int>(455, 146);
  695 +
  696 + return direct_output;
  697 +}
  698 +
  699 +// head_or_tail 0:车头 1:车尾
  700 +int RoadSegAnalysis::check_reverse_driving(cv::Mat direct_mask, sy_rect rc, int src_width, int src_height, int head_or_tail) {
  701 + // cv::Mat image(cv::Size(640, 360), CV_8UC3, cv::Scalar(0, 0, 0));
  702 + // cv::Mat merge_image = mask_to_rgb(image, direct_mask);
  703 + // cv::imwrite("direct_mask.jpg", merge_image);
  704 +
  705 + float scale_w = 640.0 / src_width;
  706 + float scale_h = 360.0 / src_height;
  707 + int center_x = (rc.left_ + rc.width_ / 2) * scale_w;
  708 + int center_y = (rc.top_ + rc.height_ / 2) * scale_h;
  709 + if(center_x < 0 ||center_x >= direct_mask.cols || center_y < 0 || center_y >= direct_mask.rows){
  710 + return -1;
  711 + }
  712 + // '来': 1, '去': 2, '近': 3, '远': 4
  713 + int direct = direct_mask.at<int>(center_x, center_y);
  714 + if (direct == 1 && head_or_tail == 0) {
  715 + // 来车道,车头,正常行驶
  716 + return 0;
  717 + } else if (direct == 1 && head_or_tail == 1){
  718 + // 来车道,车尾,逆行
  719 + return 1;
  720 + } else if (direct == 2 && head_or_tail == 0){
  721 + // 去车道,车头,逆行
  722 + return 1;
  723 + } else if (direct == 2 && head_or_tail == 1){
  724 + // 去车道,车尾,正常行驶
  725 + return 0;
  726 + }
  727 +
  728 + return -1;
  729 +}
  730 +
  731 +int RoadSegAnalysis::check_cross_line(std::vector<LineInfo>& vec_line, sy_rect rc) {
  732 +
  733 + std::vector<cv::Point> polygon_pts;
  734 + cv::Point pt_lt;
  735 + pt_lt.x = rc.left_;
  736 + pt_lt.y = rc.top_;
  737 + polygon_pts.push_back(pt_lt);
  738 + cv::Point pt_rt;
  739 + pt_rt.x = rc.left_ + rc.width_;
  740 + pt_rt.y = rc.top_;
  741 + polygon_pts.push_back(pt_rt);
  742 + cv::Point pt_rb;
  743 + pt_rb.x = rc.left_ + rc.width_;
  744 + pt_rb.y = rc.top_ + rc.height_;
  745 + polygon_pts.push_back(pt_rb);
  746 + cv::Point pt_lb;
  747 + pt_lb.x = rc.left_;
  748 + pt_lb.y = rc.top_ + rc.height_;
  749 + polygon_pts.push_back(pt_lb);
  750 +
  751 +
  752 + for (size_t i = 0; i < vec_line.size(); i++) {
  753 + LineInfo& line = vec_line[i];
  754 + if (line.line_type == 1 || line.line_type == 2) {
  755 + // 黄实线
  756 + int in_count = 0;
  757 + std::vector<cv::Point> vec_pt;
  758 + for (size_t j = 0; j < line.vec_pt.size(); j++) {
  759 + double dist = pointPolygonTest(polygon_pts, cv::Point2f(line.vec_pt[j].x_, line.vec_pt[j].y_), false);
  760 + if (dist > 0) {
  761 + in_count ++;
  762 + }
  763 + }
  764 +
  765 + if (in_count > 5)
  766 + {//有5个点就认为是压线了
  767 + return line.line_type;
  768 + }
  769 + }
  770 + }
  771 +
  772 + return -1;
  773 +}
  774 +
  775 +int RoadSegAnalysis::check_cross_region(std::vector<SegInfo>& vec_reg, sy_rect rc, int region_type) {
  776 +
  777 + std::vector<cv::Point> polygon_pts;
  778 + cv::Point pt_lt;
  779 + pt_lt.x = rc.left_;
  780 + pt_lt.y = rc.top_;
  781 + polygon_pts.push_back(pt_lt);
  782 + cv::Point pt_rt;
  783 + pt_rt.x = rc.left_ + rc.width_;
  784 + pt_rt.y = rc.top_;
  785 + polygon_pts.push_back(pt_rt);
  786 + cv::Point pt_rb;
  787 + pt_rb.x = rc.left_ + rc.width_;
  788 + pt_rb.y = rc.top_ + rc.height_;
  789 + polygon_pts.push_back(pt_rb);
  790 + cv::Point pt_lb;
  791 + pt_lb.x = rc.left_;
  792 + pt_lb.y = rc.top_ + rc.height_;
  793 + polygon_pts.push_back(pt_lb);
  794 +
  795 +
  796 + for (size_t i = 0; i < vec_reg.size(); i++) {
  797 + SegInfo& seg = vec_reg[i];
  798 + if (seg.seg_type == region_type) {
  799 + // 黄实线
  800 + int in_count = 0;
  801 + std::vector<cv::Point> vec_pt;
  802 + for (size_t j = 0; j < seg.vec_pt.size(); j++) {
  803 + double dist = pointPolygonTest(polygon_pts, cv::Point2f(seg.vec_pt[j].x_, seg.vec_pt[j].y_), false);
  804 + if (dist > 0) {
  805 + in_count ++;
  806 + }
  807 + }
  808 +
  809 + if (in_count > 5)
  810 + {//有5个点就认为是压线了
  811 + return 1;
  812 + }
  813 + }
  814 + }
  815 +
  816 + return -1;
  817 +}
  818 +
  819 +cv::Mat imshow_lanes(cv::Mat img, const rs_lane* lanes, int lane_count) {
  820 + float scale_w = img.cols / 640.0;
  821 + float scale_h = img.rows / 360.0;
  822 + std::vector<std::pair<std::vector<cv::Point>, int>> combined;
  823 + lanes_process(lanes, lane_count, combined, scale_w, scale_h);
  824 +
  825 + for (const auto& lane_info : combined) {
  826 + const auto& xys = lane_info.first;
  827 + int cls = lane_info.second;
  828 + cv::Scalar color(lane_colors[cls][0],lane_colors[cls][1],lane_colors[cls][2]);
  829 + for (size_t i = 1; i < xys.size(); ++i) {
  830 + cv::line(img, xys[i - 1], xys[i], color, 4);
  831 + }
  832 + }
  833 +
  834 + return img;
  835 +}
  836 +
  837 +void RoadSegAnalysis::post_direct(rs_result one_result, sy_img src){
  838 + int src_width = src.w_;
  839 + int src_height = src.h_;
  840 + int w = 640;
  841 + int h = 360;
  842 + float alpha = 0.75;
  843 + cv::Mat overlayed_direct_img(cv::Size(w,h), CV_8UC3, cv::Scalar(0, 0, 0));
  844 + // 将车道线标签转换为彩色图像
  845 + for (int i = 0; i < h; ++i) {
  846 + for (int j = 0; j < w; ++j) {
  847 + int idx = one_result.direct_seg[(i * w + j)];
  848 + overlayed_direct_img.at<cv::Vec3b>(i, j)[0] = seg_colors[idx][0]; // R通道;
  849 + overlayed_direct_img.at<cv::Vec3b>(i, j)[1] = seg_colors[idx][1]; // G通道
  850 + overlayed_direct_img.at<cv::Vec3b>(i, j)[2] = seg_colors[idx][2]; // B通道
  851 + }
  852 + }
  853 + cv::resize(overlayed_direct_img, overlayed_direct_img, cv::Size(src_width,src_height), 0, 0, cv::INTER_LINEAR);
  854 + // 将原始图像和彩色车道线图进行混合
  855 + // cv::addWeighted(cvImg, alpha, overlayed_direct_img, 1 - alpha, 0, overlayed_direct_img);
  856 + cv::Mat img_direct_lane = imshow_lanes(overlayed_direct_img, one_result.reg_array, one_result.lane_count);
  857 + cv::imwrite("img_direct_lane.jpg", img_direct_lane);
  858 +
  859 +
  860 + std::vector<std::pair<std::vector<cv::Point>, int>> combined;
  861 + lanes_process(one_result.reg_array, one_result.lane_count, combined);
  862 + std::vector<std::vector<cv::Point>> poly_masks, lanes, direct_masks, merge_masks;
  863 + std::vector<int> region_classes, cats, direct_classes, merge_classes;
  864 + cv::Mat background_mask;
  865 + std::map<double, int> x_sort;
  866 + bool large_resolution = false;
  867 + if (src_height > 1920) large_resolution = true;
  868 + cv::Mat seg_output = seg_post_process(large_resolution, one_result.seg_array, combined, poly_masks, region_classes, lanes, cats, x_sort, background_mask); //m_masks:mask前的结果 poly_masks后的结果
  869 + cv::Mat direct_output = direct_post_process(large_resolution, one_result.direct_seg, direct_masks, direct_classes);
  870 + cv::Mat merge_output = merge_direct_process(large_resolution, one_result.direct_seg, merge_masks, merge_classes);
  871 +
  872 + cv::Mat image(cv::Size(w,h), CV_8UC3, cv::Scalar(0, 0, 0));
  873 + cv::Mat vis_image = mask_to_rgb(image, seg_output);
  874 + cv::Mat direct_image = mask_to_rgb(image, direct_output);
  875 + cv::Mat merge_image = mask_to_rgb(image, merge_output);
  876 + cv::imwrite("vis_image.jpg", vis_image);
  877 + cv::imwrite("direct_image.jpg", direct_image);
  878 + cv::imwrite("merge_image.jpg", merge_image);
  879 + cv::imwrite("background_mask.jpg", background_mask);
181 } 880 }
182 \ No newline at end of file 881 \ No newline at end of file
src/ai_engine_module/RoadSegAnalysis.h
@@ -9,6 +9,8 @@ using namespace std; @@ -9,6 +9,8 @@ using namespace std;
9 struct RoadInfo{ 9 struct RoadInfo{
10 std::vector<LineInfo> vec_line; 10 std::vector<LineInfo> vec_line;
11 std::vector<SegInfo> vec_road; 11 std::vector<SegInfo> vec_road;
  12 +
  13 + cv::Mat direct_mask;
12 }; 14 };
13 15
14 class RoadSegAnalysis 16 class RoadSegAnalysis
@@ -21,11 +23,20 @@ public: @@ -21,11 +23,20 @@ public:
21 23
22 std::vector<RoadInfo> detect(vector<sy_img> vec_img); 24 std::vector<RoadInfo> detect(vector<sy_img> vec_img);
23 25
  26 + int check_reverse_driving(cv::Mat direct_mask, sy_rect rc, int src_width, int src_height, int head_or_tail);
  27 +
  28 + int check_cross_line(std::vector<LineInfo>& vec_line, sy_rect rc);
  29 +
  30 + int check_cross_region(std::vector<SegInfo>& vec_reg, sy_rect rc, int region_type);
  31 +
24 private: 32 private:
25 int release(); 33 int release();
26 34
27 std::vector<SegInfo> parse_seg(rs_result one_result, sy_img src); 35 std::vector<SegInfo> parse_seg(rs_result one_result, sy_img src);
28 36
  37 + cv::Mat parse_direct(rs_result one_result, sy_img src);
  38 +
  39 + void post_direct(rs_result one_result, sy_img src);
29 private: 40 private:
30 void* m_handle{nullptr}; 41 void* m_handle{nullptr};
31 aclrtContext ctx{nullptr}; 42 aclrtContext ctx{nullptr};
src/demo/main.cpp
@@ -11,9 +11,12 @@ int main() { @@ -11,9 +11,12 @@ int main() {
11 pic_analysis.init(param); 11 pic_analysis.init(param);
12 12
13 vector<string> vec_path; 13 vector<string> vec_path;
14 - for (size_t i = 0; i < 1; i++) 14 + size_t i = 1;
  15 + // for (i = 1; i < 8; i++)
15 { 16 {
16 - vec_path.push_back("./img/test_road1.jpg"); 17 + string path = "./img/test_phone";
  18 + path = path + to_string(i) + ".jpg";
  19 + vec_path.push_back(path);
17 } 20 }
18 21
19 pic_analysis.analysis_file(vec_path); 22 pic_analysis.analysis_file(vec_path);
src/village_inc.h
@@ -35,7 +35,8 @@ typedef struct VehicleInfo { @@ -35,7 +35,8 @@ typedef struct VehicleInfo {
35 35
36 manned_result manned_res; 36 manned_result manned_res;
37 37
38 - int type; 38 + int type;
  39 +
39 int vpt_type; 40 int vpt_type;
40 int rainshed; //是否安装雨棚, 0 有雨棚 1 无雨棚 41 int rainshed; //是否安装雨棚, 0 有雨棚 1 无雨棚
41 int manned; 42 int manned;
@@ -43,6 +44,12 @@ typedef struct VehicleInfo { @@ -43,6 +44,12 @@ typedef struct VehicleInfo {
43 int human_upper_color; 44 int human_upper_color;
44 int human_lower_color; 45 int human_lower_color;
45 46
  47 + int reverse_driving; // 0 正常行驶 1 逆行 -1 未知
  48 + int phoning; // 骑车打电话
  49 +
  50 + int cross_line; // 压实线, 1 黄实线 2 白实线 -1 其他
  51 + int cross_diversion_line; // 压导流线 1 压了 -1 未压
  52 +
46 } VehicleInfo; 53 } VehicleInfo;
47 54
48 struct LineInfo{ 55 struct LineInfo{