diff --git a/README.md b/README.md index 1dcb8c74e..3ab272849 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,10 @@ Host-side DepthAI source code - Install development environment dependencies: - Linux: - sudo apt-get install -y git python-pip cmake cmake-gui libusb-1.0-0-dev + sudo apt-get install -y git python-pip cmake cmake-gui libusb-1.0-0-dev libcurl4-openssl-dev - macOS: - brew install coreutils python3 cmake libusb wget opencv + brew install coreutils python3 cmake libusb wget opencv curl - After cloning the repo, update the third-party libraries used: diff --git a/host/core/model_downloader.cpp b/host/core/model_downloader.cpp new file mode 100644 index 000000000..2d6c2e3e0 --- /dev/null +++ b/host/core/model_downloader.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include + +#define BLUE "\033[94m" +#define ENDC "\033[0m" +#define RED "\033[91m" + +#define MAX_SHAVES (16) +#define MAX_CMX_SLICES (16) +#define MAX_NCES (2) + +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string *)userp)->append((char *)contents, size * nmemb); + return size * nmemb; +} + +static int progress_func(void *ptr, double TotalToDownload, double NowDownloaded, + double TotalToUpload, double NowUploaded) +{ + int progress_bar_width = 50; + double download_progress = TotalToDownload ? (NowDownloaded / TotalToDownload) : 0; + int progress_indicator_width = download_progress * progress_bar_width; + + int p_bar_i = 0; + printf("%3.0f%% ", download_progress * 100); + printf(RED "[" ENDC); + printf(BLUE); + for (; p_bar_i < progress_indicator_width; p_bar_i++) + { + printf("-"); + } + printf(ENDC); + for (; p_bar_i < progress_bar_width; p_bar_i++) + { + printf(" "); + } + printf(RED "]" ENDC "\r"); + fflush(stdout); + return 0; +} + +static int check_input(int nr_shaves, int nr_cmx_slices, int nr_NCEs) +{ + if (nr_shaves <= 0 || nr_shaves > MAX_SHAVES) + { + std::cerr << RED "Number of shaves must be between (0, 16]" << ENDC << std::endl; + return 1; + } + if (nr_cmx_slices <= 0 || nr_cmx_slices > MAX_CMX_SLICES) + { + std::cerr << RED "Number of shaves must be between (0, 16]" << ENDC << std::endl; + return 2; + } + if (nr_NCEs < 0 || nr_NCEs > MAX_NCES) + { + std::cerr << RED "Number of shaves must be between [0, 2]" << ENDC << std::endl; + return 3; + } + return 0; +} + +int download_model(std::string model_name, int nr_shaves, int nr_cmx_slices, int nr_NCEs, std::string output_folder_path) +{ + if (check_input(nr_shaves, nr_cmx_slices, nr_NCEs)) + return 3; + CURL *hnd = curl_easy_init(); + + if (0) + curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1); + + curl_easy_setopt(hnd, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.A.B.C Safari/525.13"); + curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(hnd, CURLOPT_URL, "http://luxonis.com:8080/"); + + std::string readBuffer; + + curl_easy_setopt(hnd, CURLOPT_TIMEOUT, 120L); + curl_easy_setopt(hnd, CURLOPT_CONNECTTIMEOUT, 10L); + + struct curl_slist *headers = NULL; + // headers = curl_slist_append(headers, "postman-token: 5824015d-b523-e286-69e0-9a4da23af7da"); + // headers = curl_slist_append(headers, "cache-control: no-cache"); + headers = curl_slist_append(headers, "content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"); + curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); + + curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &readBuffer); + + // Internal CURL progressmeter must be disabled if we provide our own callback + curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0); + // Install the callback function + curl_easy_setopt(hnd, CURLOPT_PROGRESSFUNCTION, progress_func); + + std::string boundary = "------WebKitFormBoundary7MA4YWxkTrZu0gW"; + std::string conent_header = "Content-Disposition: form-data; "; + std::string end_form_line = "\r\n"; + std::string end_form = end_form_line + end_form_line; + + std::ostringstream os; + + std::string platform = nr_NCEs == 0 ? "VPU_MYRIAD_2450" : "VPU_MYRIAD_2480"; + + os << boundary << end_form_line; + os << conent_header << "name=\"compile_type\"" << end_form << "zoo\r\n"; + os << boundary << end_form_line; + os << conent_header << "name=\"model_name\"" << end_form << model_name << end_form_line; + os << boundary << end_form_line; + os << conent_header << "name=\"model_downloader_params\"" << end_form << "--precisions FP16 --num_attempts 5\r\n"; + os << boundary << end_form_line; + os << conent_header << "name=\"intermediate_compiler_params\"" << end_form << "--data_type=FP16 --mean_values [127.5,127.5,127.5] --scale_values [255,255,255]\r\n"; + os << boundary << end_form_line; + os << conent_header << "name=\"compiler_params\"" << end_form << "-ip U8 -VPU_MYRIAD_PLATFORM " << platform << " -VPU_NUMBER_OF_SHAVES " << nr_shaves << " -VPU_NUMBER_OF_CMX_SLICES " << nr_cmx_slices << end_form_line; + os << boundary << "--"; + + std::string options_builder = os.str(); + // std::cout << options_builder << std::endl; + + curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, options_builder.c_str()); + + CURLcode ret = curl_easy_perform(hnd); + // std::cout << ret << std::endl; + std::cout << std::endl; + + int ret_code = 0; + + long http_code = 0; + curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &http_code); + if (http_code == 200 && ret == CURLE_OK) + { + //Succeeded + std::cout << "Model downloaded!" << std::endl; + // std::cout << readBuffer.size() << std::endl; + ret_code = 0; + std::ofstream wf(output_folder_path, std::ios::out | std::ios::binary); + if (!wf) + { + std::cerr << "Cannot open file!" << std::endl; + ret_code = 1; + goto cleanup; + } + + wf.write(readBuffer.c_str(), readBuffer.size()); + wf.close(); + if (!wf.good()) + { + std::cerr << "Error occurred at writing time!" << std::endl; + ret_code = 2; + goto cleanup; + } + } + else + { + //Failed + std::cerr << RED "Model download failed with http code : " << http_code << ", curl code: " << ret << ENDC << std::endl; + std::cerr << readBuffer << std::endl; + ret_code = http_code; + } + +cleanup: + curl_easy_cleanup(hnd); + + return (int)ret_code; +} + +// int main(void) +// { +// std::string model_name = "mobilenet-ssd"; +// int nr_shaves = 6; +// int nr_cmx_slices = 6; +// int nr_NCEs = 0; +// std::string output_path = "."; +// std::ostringstream path; +// path << output_path << "/" << model_name << ".blob" << ".sh" << nr_shaves << "cmx" << nr_cmx_slices; +// if(nr_NCEs == 0) +// { +// path << "NO_NCE"; +// } +// return download_model(model_name, nr_shaves, nr_cmx_slices, nr_NCEs, path.str()); +// } \ No newline at end of file diff --git a/host/core/model_downloader.hpp b/host/core/model_downloader.hpp new file mode 100644 index 000000000..869db4677 --- /dev/null +++ b/host/core/model_downloader.hpp @@ -0,0 +1,4 @@ +#pragma once +#include + +int download_model(std::string model_name, int nr_shaves, int nr_cmx_slices, int nr_NCEs, std::string output_folder_path); \ No newline at end of file diff --git a/host/core/pipeline/host_pipeline_config.cpp b/host/core/pipeline/host_pipeline_config.cpp index dcd2b2394..6f1ebc926 100644 --- a/host/core/pipeline/host_pipeline_config.cpp +++ b/host/core/pipeline/host_pipeline_config.cpp @@ -3,6 +3,17 @@ #include "host_pipeline_config.hpp" +static std::unordered_map rgb_cam_supported_configs = +{ + {1080, 0}, + {2160, 1} +}; + +static std::unordered_map mono_cam_supported_configs = +{ + {720, 0}, +}; + #define WARNING "\033[1;5;31m" #define ENDC "\033[0m" @@ -121,6 +132,44 @@ bool HostPipelineConfig::initWithJSON(const json &json_obj) { ai.keep_aspect_ratio = ai_obj.at("keep_aspect_ratio").get(); } + + if (ai_obj.contains("shaves")) + { + ai.shaves = ai_obj.at("shaves").get(); + } + if (ai.shaves <= 0 || ai.shaves > 16) + { + std::cerr << WARNING "ai.shaves should be in the range (0 .. 16]\n" ENDC; + break; + } + + if (ai_obj.contains("cmx_slices")) + { + ai.cmx_slices = ai_obj.at("cmx_slices").get(); + } + if (ai.cmx_slices <= 0 || ai.cmx_slices > 19) + { + std::cerr << WARNING "ai.cmx_slices should be in the range (0 .. 19]\n" ENDC; + break; + } + + if (ai.shaves > ai.cmx_slices) + { + std::cerr << WARNING "ai.shaves should be <= than ai.cmx_slices\n" ENDC; + break; + } + + + if (ai_obj.contains("NCEs")) + { + ai.NCEs = ai_obj.at("NCEs").get(); + } + if (ai.NCEs < 0 || ai.NCEs > 2) + { + std::cerr << WARNING "ai.NCEs should be in the range [0 .. 2]\n" ENDC; + break; + } + } // "ot" @@ -215,6 +264,53 @@ bool HostPipelineConfig::initWithJSON(const json &json_obj) } } + if (json_obj.contains("camera")) + { + auto& camera_conf_obj = json_obj.at("camera"); + + if (camera_conf_obj.contains("rgb")) + { + auto& rgb_camera_conf_obj = camera_conf_obj.at("rgb"); + + rgb_cam_config.resolution_h = rgb_camera_conf_obj.at("resolution_h").get(); + rgb_cam_config.fps = rgb_camera_conf_obj.at("fps").get(); + + auto it = rgb_cam_supported_configs.find(rgb_cam_config.resolution_h); + + if (it == rgb_cam_supported_configs.end()) { + std::cerr << WARNING "Requested rgb cam config not available!\n" ENDC; + + std::cerr << "Supported configs.\n"; + for(auto elem : rgb_cam_supported_configs) + { + std::cerr << elem.first << "\n"; + } + break; + } + } + + if (camera_conf_obj.contains("mono")) + { + auto& mono_camera_conf_obj = camera_conf_obj.at("mono"); + + mono_cam_config.resolution_h = mono_camera_conf_obj.at("resolution_h").get(); + mono_cam_config.fps = mono_camera_conf_obj.at("fps").get(); + + auto it = mono_cam_supported_configs.find(mono_cam_config.resolution_h); + + if (it == mono_cam_supported_configs.end()) { + std::cerr << WARNING "Requested mono cam config not available!\n" ENDC; + + std::cerr << "Supported configs.\n"; + for(auto elem : mono_cam_supported_configs) + { + std::cerr << elem.first << "\n"; + } + break; + } + } + } + result = true; } while (false); diff --git a/host/core/pipeline/host_pipeline_config.hpp b/host/core/pipeline/host_pipeline_config.hpp index cba9bb191..ccd9629c2 100644 --- a/host/core/pipeline/host_pipeline_config.hpp +++ b/host/core/pipeline/host_pipeline_config.hpp @@ -33,6 +33,9 @@ struct HostPipelineConfig std::string blob_file_config; bool calc_dist_to_bb = false; bool keep_aspect_ratio = true; + int32_t shaves = 4; + int32_t cmx_slices = 4; + int32_t NCEs = 1; } ai; struct OT @@ -56,6 +59,18 @@ struct HostPipelineConfig std::string revision; } board_config; + struct RGBCamConfig + { + int32_t resolution_h; + int32_t fps; + } rgb_cam_config; + + struct MonoCamConfig + { + int32_t resolution_h; + int32_t fps; + } mono_cam_config; + bool initWithJSON(const json &json_obj); bool hasStream(const std::string &stream_name) const; diff --git a/host/py_module/CMakeLists.txt b/host/py_module/CMakeLists.txt index 03ba3553b..41f5681b5 100644 --- a/host/py_module/CMakeLists.txt +++ b/host/py_module/CMakeLists.txt @@ -56,6 +56,7 @@ pybind11_add_module( ../core/host_data_reader.cpp ../core/host_capture_command.cpp ../core/disparity_stream_post_processor.cpp + ../core/model_downloader.cpp ../../shared/json_helper.cpp ../../shared/stream/stream_info.cpp ../../shared/xlink/xlink_wrapper.cpp @@ -102,7 +103,9 @@ endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../../shared/version.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/../../shared/version.hpp @ONLY) - +set(CURL_LIBRARY "-lcurl") +find_package(CURL REQUIRED) +include_directories(${CURL_INCLUDE_DIR}) # link libraries target_link_libraries( @@ -111,6 +114,7 @@ target_link_libraries( -L/usr/local/lib # For macOS -lusb-1.0 -lpthread + ${CURL_LIBRARIES} nlohmann_json::nlohmann_json nlohmann_json_schema_validator ) diff --git a/host/py_module/py_bindings.cpp b/host/py_module/py_bindings.cpp index bd9670a37..4a9c3317c 100644 --- a/host/py_module/py_bindings.cpp +++ b/host/py_module/py_bindings.cpp @@ -32,11 +32,15 @@ #include "../../shared/xlink/xlink_wrapper.hpp" #include "../core/host_json_helper.hpp" #include "host_capture_command.hpp" +#include "model_downloader.hpp" #include "capture_af_bindings.hpp" #include "../../shared/metadata/capture_metadata.hpp" +#define WARNING "\033[1;5;31m" +#define ENDC "\033[0m" + namespace py = pybind11; std::string config_backup; @@ -342,6 +346,11 @@ std::map get_nn_to_depth_bbox_mapping() return nn_to_depth_mapping; } +int download_blob(std::string model_name, int nr_shaves, int nr_cmx_slices, int nr_NCEs, std::string output_folder_path) +{ + return download_model(model_name, nr_shaves, nr_cmx_slices, nr_NCEs, output_folder_path); +} + std::shared_ptr create_pipeline( const std::string &config_json_str ) @@ -354,7 +363,7 @@ std::shared_ptr create_pipeline( // check xlink if (nullptr == g_xlink) { - std::cout << "device is not initialized\n"; + std::cerr << WARNING "device is not initialized\n" ENDC; break; } @@ -362,7 +371,7 @@ std::shared_ptr create_pipeline( json config_json; if (!getJSONFromString(config_json_str, config_json)) { - std::cout << "Error: Cant parse json config :" << config_json_str << "\n"; + std::cerr << WARNING "Error: Cant parse json config :" << config_json_str << "\n" ENDC; break; } @@ -370,7 +379,7 @@ std::shared_ptr create_pipeline( HostPipelineConfig config; if (!config.initWithJSON(config_json)) { - std::cout << "Error: Cant init configs with json: " << config_json.dump() << "\n"; + std::cerr << "Error: Cant init configs with json: " << config_json.dump() << "\n"; break; } @@ -405,7 +414,7 @@ std::shared_ptr create_pipeline( HostDataReader calibration_reader; if (!calibration_reader.init(config.depth.calibration_file)) { - std::cout << "depthai: Error opening calibration file: " << config.depth.calibration_file << "\n"; + std::cerr << WARNING "depthai: Error opening calibration file: " << config.depth.calibration_file << "\n" ENDC; break; } @@ -453,8 +462,29 @@ std::shared_ptr create_pipeline( {"_streams", json::array()} }; + json_config_obj["camera"]["rgb"]["resolution_h"] = config.rgb_cam_config.resolution_h; + json_config_obj["camera"]["rgb"]["fps"] = config.rgb_cam_config.fps; + json_config_obj["camera"]["mono"]["resolution_h"] = config.mono_cam_config.resolution_h; + json_config_obj["camera"]["mono"]["fps"] = config.mono_cam_config.fps; + + HostDataReader _blob_reader; + int size_blob = 0; + if (!config.ai.blob_file.empty()) + { + if (!_blob_reader.init(config.ai.blob_file)) + { + std::cerr << WARNING "depthai: Error opening blob file: " << config.ai.blob_file << "\n" ENDC; + break; + } + size_blob = _blob_reader.getSize(); + } + + json_config_obj["ai"]["blob_size"] = size_blob; json_config_obj["ai"]["calc_dist_to_bb"] = config.ai.calc_dist_to_bb; json_config_obj["ai"]["keep_aspect_ratio"] = config.ai.keep_aspect_ratio; + json_config_obj["ai"]["shaves"] = config.ai.shaves; + json_config_obj["ai"]["cmx_slices"] = config.ai.cmx_slices; + json_config_obj["ai"]["NCEs"] = config.ai.NCEs; json_config_obj["ot"]["max_tracklets"] = config.ot.max_tracklets; json_config_obj["ot"]["confidence_threshold"] = config.ot.confidence_threshold; @@ -517,7 +547,7 @@ std::shared_ptr create_pipeline( pipeline_config_str_packed.data()) ) { - std::cout << "depthai: pipelineConfig write error;\n"; + std::cerr << WARNING "depthai: pipelineConfig write error\n" ENDC; break; } @@ -534,14 +564,6 @@ std::shared_ptr create_pipeline( } else { - HostDataReader _blob_reader; - if (!_blob_reader.init(config.ai.blob_file)) - { - std::cout << "depthai: Error opening blob file: " << config.ai.blob_file << "\n"; - break; - } - int size_blob = _blob_reader.getSize(); - std::vector buff_blob(size_blob); std::cout << "Read: " << _blob_reader.readData(buff_blob.data(), size_blob) << std::endl; @@ -553,7 +575,7 @@ std::shared_ptr create_pipeline( if (!g_xlink->openWriteAndCloseStream(blobInfo, buff_blob.data())) { - std::cout << "depthai: pipelineConfig write error;\n"; + std::cout << "depthai: pipelineConfig write error: Blob size too big: " << size_blob << "\n"; break; } printf("depthai: done sending Blob file %s\n", config.ai.blob_file.c_str()); @@ -605,21 +627,26 @@ std::shared_ptr create_pipeline( }; // check CMX slices & used shaves - int device_cmx_for_nnet = g_config_d2h.at("_resources").at("cmx").at("for_nnet").get(); - if (cnn_input_info.number_of_cmx_slices != device_cmx_for_nnet) + if (cnn_input_info.number_of_cmx_slices > config.ai.cmx_slices) { - std::cout << "Error: Blob is compiled for " << cnn_input_info.number_of_cmx_slices - << " cmx slices but device can calculate on " << device_cmx_for_nnet << "\n"; + std::cerr << WARNING "Error: Blob is compiled for " << cnn_input_info.number_of_cmx_slices + << " cmx slices but device is configured to calculate on " << config.ai.cmx_slices << "\n" ENDC; break; } - int device_shaves_for_nnet = g_config_d2h.at("_resources").at("shaves").at("for_nnet").get(); - if (cnn_input_info.number_of_shaves != device_shaves_for_nnet) + if (cnn_input_info.number_of_shaves > config.ai.shaves) { - std::cout << "Error: Blob is compiled for " << cnn_input_info.number_of_shaves - << " shaves but device can calculate on " << device_shaves_for_nnet << "\n"; + std::cerr << WARNING "Error: Blob is compiled for " << cnn_input_info.number_of_shaves + << " shaves but device is configured to calculate on " << config.ai.shaves << "\n" ENDC; break; } + + if(!cnn_input_info.satisfied_resources) + { + std::cerr << WARNING "ERROR: requested CNN resources overlaps with RGB camera \n" ENDC; + break; + } + } @@ -812,7 +839,11 @@ PYBIND11_MODULE(depthai, m) py::arg("config") = py::dict() ); - + m.def( + "download_blob", + &download_blob, + "Function that downloads and saves blob file from cloud." + ); // FrameMetadata struct binding py::class_(m, "FrameMetadata") diff --git a/shared/cnn_info.hpp b/shared/cnn_info.hpp index 69c986ed8..b1f1c4c03 100644 --- a/shared/cnn_info.hpp +++ b/shared/cnn_info.hpp @@ -19,4 +19,5 @@ struct cnn_info uint16_t number_of_shaves; uint32_t offsets[7]; nn_to_depth_bbox_map nn_to_depth; + int32_t satisfied_resources; }; diff --git a/shared/depthai_constants.hpp b/shared/depthai_constants.hpp index 9765d74bd..885242c38 100644 --- a/shared/depthai_constants.hpp +++ b/shared/depthai_constants.hpp @@ -16,7 +16,7 @@ // TODO: remove next constant std::unordered_map g_streams_pc_to_myriad = { - {"config_h2d", StreamInfo("config_h2d", 1000)}, + {"config_h2d", StreamInfo("config_h2d", 5000)}, {"host_capture", StreamInfo("host_capture", sizeof(CaptureMetadata))} }; @@ -31,12 +31,12 @@ std::unordered_map c_streams_myriad_to_pc = {"depth_sipp", StreamInfo("depth_sipp", 0, { 720, 1280}, 2 )}, {"depth_color_h", StreamInfo("depth_color_h", 720*1280*3, { 720, 1280, 3} )}, - {"metaout", StreamInfo("metaout", 2*2816)}, // 1408 + {"metaout", StreamInfo("metaout", 4*1024*1024)}, // 4 mb max metaout size {"previewout", StreamInfo("previewout", 1920256)}, {"meta_d2h", StreamInfo("meta_d2h", 1024*1024)}, - {"jpegout", StreamInfo("jpegout", 1*1024*1024)}, - {"video", StreamInfo("video", 2*1024*1024)}, + {"jpegout", StreamInfo("jpegout", 10*1024*1024)}, + {"video", StreamInfo("video", 10*1024*1024)}, {"object_tracker", StreamInfo("object_tracker", 2000)} }; diff --git a/shared/version.hpp b/shared/version.hpp index 321921f51..36b901660 100644 --- a/shared/version.hpp +++ b/shared/version.hpp @@ -1,4 +1,4 @@ #pragma once -const char *c_depthai_dev_version = "17d0aaf9269019040108840f7846b37ac8164499"; +const char *c_depthai_dev_version = "4c20afa7ef4cf174e2e4112762210d64572a4891"; const char *c_depthai_version = "0.1.0"; diff --git a/shared/xlink/xlink_wrapper.cpp b/shared/xlink/xlink_wrapper.cpp index 6602c8ee2..1e426d615 100644 --- a/shared/xlink/xlink_wrapper.cpp +++ b/shared/xlink/xlink_wrapper.cpp @@ -125,8 +125,9 @@ bool XLinkWrapper::initFromHostSide( printf("\rNo USB device [03e7:2485], still looking"); if (!usb_device.empty()) printf(" on port %s", usb_device.c_str()); - printf("... %.3fs ", tdiff.count()); + printf("... %.1fs ", tdiff.count()); fflush(stdout); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else { if (print_found) printf("[FOUND]\n");