From 4d6e80a6c585f6929f6bf4bf319932732160ce84 Mon Sep 17 00:00:00 2001 From: Michael Welter Date: Thu, 12 May 2022 19:54:57 +0200 Subject: tracker/nn: Add support for recurrent model and further tweaks Regarding tweaks: * EWA smoothing of head ROI. Smoothing strength is a UI setting. * Adjustible zoom into the detected face. The predicted ROI is scaled by a factor the user can set. There is a sweet spot somewhere near 1. * Adjustible number of threads * The ROI is no longer taken as model output directly. This was actually not needed. Perhaps as auxiliary training objective for the network. But the tracker implementation now just uses a square area around the head center according to the predicted head size. * Add comment and debug notification on face ROI model output --- tracker-neuralnet/ftnoir_tracker_neuralnet.cpp | 272 +++++++++++++++++++------ tracker-neuralnet/ftnoir_tracker_neuralnet.h | 27 ++- tracker-neuralnet/lang/nl_NL.ts | 16 ++ tracker-neuralnet/lang/ru_RU.ts | 16 ++ tracker-neuralnet/lang/stub.ts | 16 ++ tracker-neuralnet/lang/zh_CN.ts | 16 ++ tracker-neuralnet/neuralnet-trackercontrols.ui | 146 ++++++++++++- 7 files changed, 432 insertions(+), 77 deletions(-) diff --git a/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp b/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp index 06874e6c..00f3f281 100644 --- a/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp +++ b/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp @@ -16,6 +16,7 @@ #include #include "compat/timer.hpp" #include +#include #ifdef _MSC_VER # pragma warning(disable : 4702) @@ -29,6 +30,7 @@ #include #include #include +#include // Some demo code for onnx // https://github.com/microsoft/onnxruntime/blob/master/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/C_Api_Sample.cpp @@ -43,9 +45,6 @@ using numeric_types::vec2; using numeric_types::mat33; using quat = std::array; -// Minimal difference if at all going from 1 to 2 threads. -static constexpr int num_threads = 1; - #if _MSC_VER std::wstring convert(const QString &s) { return s.toStdWString(); } @@ -69,6 +68,41 @@ cv::Rect_ squarize(const cv::Rect_ &r) } +template +cv::Point_ as_point(const cv::Size_& s) +{ + return { s.width, s.height }; +} + + +template +cv::Size_ as_size(const cv::Point_& p) +{ + return { p.x, p.y }; +} + + +template +cv::Rect_ expand(const cv::Rect_& r, T factor) +{ + // xnew = l+.5*w - w*f*0.5 = l + .5*(w - new_w) + const cv::Size_ new_size = { r.width * factor, r.height * factor }; + const cv::Point_ new_tl = r.tl() + (as_point(r.size()) - as_point(new_size)) / T(2); + return cv::Rect_(new_tl, new_size); +} + + +template +cv::Rect_ ewa_filter(const cv::Rect_& last, const cv::Rect_& current, T alpha) +{ + const auto last_center = T(0.5) * (last.tl() + last.br()); + const auto cur_center = T(0.5) * (current.tl() + current.br()); + const cv::Point_ new_size = as_point(last.size()) + alpha * (as_point(current.size()) - as_point(last.size())); + const cv::Point_ new_center = last_center + alpha * (cur_center - last_center); + return cv::Rect_(new_center - T(0.5) * new_size, as_size(new_size)); +} + + cv::Rect2f unnormalize(const cv::Rect2f &r, int h, int w) { auto unnorm = [](float x) -> float { return 0.5*(x+1); }; @@ -138,6 +172,30 @@ mat33 quaternion_to_mat33(const std::array quat) } +vec3 rotate_vec(const quat& q, const vec3& p) +{ + const float qw = q[0]; + const float qi = q[1]; + const float qj = q[2]; + const float qk = q[3]; + const float pi = p[0]; + const float pj = p[1]; + const float pk = p[2]; + const quat tmp{ + - qi*pi - qj*pj - qk*pk, + qw*pi + qj*pk - qk*pj, + qw*pj - qi*pk + qk*pi, + qw*pk + qi*pj - qj*pi + }; + const vec3 out { + -tmp[0]*qi + tmp[1]*qw - tmp[2]*qk + tmp[3]*qj, + -tmp[0]*qj + tmp[1]*qk + tmp[2]*qw - tmp[3]*qi, + -tmp[0]*qk - tmp[1]*qj + tmp[2]*qi + tmp[3]*qw + }; + return out; +} + + vec3 image_to_world(float x, float y, float size, float reference_size_in_mm, const cv::Size2i& image_size, const CamIntrinsics& intrinsics) { // Compute the location the network outputs in 3d space. @@ -186,8 +244,8 @@ T iou(const cv::Rect_ &a, const cv::Rect_ &b) // have the shape B x C x H x W, where B=C=1. cv::Size get_input_image_shape(const Ort::Session &session) { - if (session.GetInputCount() != 1) - throw std::invalid_argument("Model must take exactly one input tensor"); + if (session.GetInputCount() < 1) + throw std::invalid_argument("Model must take at least one input tensor"); const std::vector shape = session.GetInputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape(); if (shape.size() != 4) @@ -196,6 +254,17 @@ cv::Size get_input_image_shape(const Ort::Session &session) } +Ort::Value create_tensor(const Ort::TypeInfo& info, Ort::Allocator& alloc) +{ + const auto shape = info.GetTensorTypeAndShapeInfo().GetShape(); + auto t = Ort::Value::CreateTensor( + alloc, shape.data(), shape.size()); + memset(t.GetTensorMutableData(), 0, sizeof(float)*info.GetTensorTypeAndShapeInfo().GetElementCount()); + return t; +} + + + int enum_to_fps(int value) { switch (value) @@ -241,10 +310,7 @@ std::pair Localizer::run( Timer t; t.start(); - const auto nt = omp_get_num_threads(); - omp_set_num_threads(num_threads); session.Run(Ort::RunOptions{nullptr}, input_names, &input_val, 1, output_names, &output_val, 1); - omp_set_num_threads(nt); last_inference_time = t.elapsed_ms(); @@ -267,36 +333,85 @@ double Localizer::last_inference_time_millis() const PoseEstimator::PoseEstimator(Ort::MemoryInfo &allocator_info, Ort::Session &&_session) - : session{std::move(_session)} - , model_version(session.GetModelMetadata().GetVersion()) + : model_version{_session.GetModelMetadata().GetVersion()} + , session{std::move(_session)} + , allocator{session, allocator_info} { + using namespace std::literals::string_literals; + + if (session.GetOutputCount() < 2) + throw std::runtime_error("Invalid Model: must have at least two outputs"); + + // WARNING + // If the model was saved without meta data, it seems the version field is uninitialized. + // In that case reading from it is UB. However, we will just get same arbitrary number + // which is hopefully different from the numbers used by models where the version is set. + if (model_version != 2) + model_version = 1; + const cv::Size input_image_shape = get_input_image_shape(session); scaled_frame = cv::Mat(input_image_shape, CV_8U); - input_mat = cv::Mat(input_image_shape, CV_32F); + input_mat = cv::Mat(input_image_shape, CV_32F); { const std::int64_t input_shape[4] = { 1, 1, input_image_shape.height, input_image_shape.width }; - input_val = Ort::Value::CreateTensor(allocator_info, input_mat.ptr(0), input_mat.total(), input_shape, 4); + input_val.push_back( + Ort::Value::CreateTensor(allocator_info, input_mat.ptr(0), input_mat.total(), input_shape, 4)); } { const std::int64_t output_shape[2] = { 1, 3 }; - output_val[0] = Ort::Value::CreateTensor( - allocator_info, &output_coord[0], output_coord.rows, output_shape, 2); + output_val.push_back(Ort::Value::CreateTensor( + allocator_info, &output_coord[0], output_coord.rows, output_shape, 2)); } { const std::int64_t output_shape[2] = { 1, 4 }; - output_val[1] = Ort::Value::CreateTensor( - allocator_info, &output_quat[0], output_quat.rows, output_shape, 2); + output_val.push_back(Ort::Value::CreateTensor( + allocator_info, &output_quat[0], output_quat.rows, output_shape, 2)); } + size_t num_regular_outputs = 2; + + if (session.GetOutputCount() >= 3 && "box"s == session.GetOutputName(2, allocator)) { const std::int64_t output_shape[2] = { 1, 4 }; - output_val[2] = Ort::Value::CreateTensor( - allocator_info, &output_box[0], output_box.rows, output_shape, 2); + output_val.push_back(Ort::Value::CreateTensor( + allocator_info, &output_box[0], output_box.rows, output_shape, 2)); + ++num_regular_outputs; + qDebug() << "Note: Legacy model output for face ROI is currently ignored"; + } + + num_recurrent_states = session.GetInputCount()-1; + if (session.GetOutputCount()-num_regular_outputs != num_recurrent_states) + throw std::runtime_error("Invalid Model: After regular inputs and outputs the model must have equal number of inputs and outputs for tensors holding hidden states of recurrent layers."); + + // Create tensors for recurrent state + for (size_t i = 0; i < num_recurrent_states; ++i) + { + const auto& input_info = session.GetInputTypeInfo(1+i); + const auto& output_info = session.GetOutputTypeInfo(num_regular_outputs+i); + if (input_info.GetTensorTypeAndShapeInfo().GetShape() != + output_info.GetTensorTypeAndShapeInfo().GetShape()) + throw std::runtime_error("Invalid Model: Tensors for recurrent hidden states should have same shape on intput and output"); + input_val.push_back(create_tensor(input_info, allocator)); + output_val.push_back(create_tensor(output_info, allocator)); } + + for (size_t i = 0; i < session.GetInputCount(); ++i) + { + input_names.push_back(session.GetInputName(i, allocator)); + } + for (size_t i = 0; i < session.GetOutputCount(); ++i) + { + output_names.push_back(session.GetOutputName(i, allocator)); + } + + qDebug() << "Model inputs: " << session.GetInputCount() << ", outputs: " << session.GetOutputCount() << ", recurrent states: " << num_recurrent_states; + + assert (input_names.size() == input_val.size()); + assert (output_names.size() == output_val.size()); } @@ -336,7 +451,7 @@ std::optional PoseEstimator::run( }; cv::getRectSubPix(frame, {patch_size, patch_size}, patch_center, cropped); - // Will get failure if patch_center is outside image boundaries. + // Will get failure if patch_center is outside image boundariesettings. // Have to catch this case. if (cropped.rows != patch_size || cropped.cols != patch_size) return {}; @@ -355,26 +470,37 @@ std::optional PoseEstimator::run( assert (input_mat.ptr(0) == p); assert (!input_mat.empty() && input_mat.isContinuous()); - const char* input_names[] = {"x"}; - const char* output_names[] = {"pos_size", "quat", "box"}; Timer t; t.start(); - const auto nt = omp_get_num_threads(); - omp_set_num_threads(num_threads); try { - session.Run(Ort::RunOptions{ nullptr }, input_names, &input_val, 1, output_names, output_val, 3); + session.Run( + Ort::RunOptions{ nullptr }, + input_names.data(), + input_val.data(), + input_val.size(), + output_names.data(), + output_val.data(), + output_val.size()); } catch (const Ort::Exception &e) { qDebug() << "Failed to run the model: " << e.what(); - omp_set_num_threads(nt); return {}; } - omp_set_num_threads(nt); - // FIXME: Execution time fluctuates wildly. 19 to 26 ms. Why??? + for (size_t i = 0; i PoseEstimator::run( // Perform coordinate transformation. // From patch-local normalized in [-1,1] to - // frame unnormalized pixel coordinates. + // frame unnormalized pixel coordinatesettings. const cv::Point2f center = patch_center + (0.5f*patch_size)*cv::Point2f{output_coord[0], output_coord[1]}; @@ -462,7 +588,7 @@ bool neuralnet_tracker::detect() auto face = poseestimator->run(grayscale, *last_roi); last_inference_time += poseestimator->last_inference_time_millis(); - + if (!face) { last_roi.reset(); @@ -470,7 +596,23 @@ bool neuralnet_tracker::detect() return false; } - last_roi = face->box; + { + // Here: compute ROI from head size estimate. This helps make the size prediction more + // invariant to mouth opening. The tracking can be lost more often at extreme + // rotations, depending on the implementation details. The code down here has + // been tweaked so that it works pretty well. + // In old behaviour ROI is taken from the model outputs + const vec3 offset = rotate_vec(face->rotation, vec3{0.f, 0.1f*face->size, face->size*0.3f}); + const float halfsize = face->size/float(settings.roi_zoom); + face->box = cv::Rect2f( + face->center.x + offset[0] - halfsize, + face->center.y + offset[1] - halfsize, + halfsize*2.f, + halfsize*2.f + ); + } + + last_roi = ewa_filter(*last_roi, face->box, float(settings.roi_filter_alpha)); Affine pose = compute_pose(*face); @@ -487,6 +629,8 @@ bool neuralnet_tracker::detect() Affine neuralnet_tracker::compute_pose(const PoseEstimator::Face &face) const { + // Compute the location the network outputs in 3d space. + const mat33 rot_correction = compute_rotation_correction( normalize(face.center, frame.rows, frame.cols), intrinsics.focal_length_w); @@ -507,7 +651,6 @@ Affine neuralnet_tracker::compute_pose(const PoseEstimator::Face &face) const ------------------------ */ - // Compute the location the network outputs in 3d space. const vec3 face_world_pos = image_to_world( face.center.x, face.center.y, face.size, head_size_mm, frame.size(), @@ -519,9 +662,9 @@ Affine neuralnet_tracker::compute_pose(const PoseEstimator::Face &face) const const vec3 pos = face_world_pos + m * vec3{ - static_cast(s.offset_fwd), - static_cast(s.offset_up), - static_cast(s.offset_right)}; + static_cast(settings.offset_fwd), + static_cast(settings.offset_up), + static_cast(settings.offset_right)}; return { m, pos }; } @@ -566,7 +709,7 @@ void neuralnet_tracker::draw_gizmos( cv::circle(frame, cv::Point(xy[0],xy[1]), 5, cv::Scalar(0,0,255), -1); } - if (s.show_network_input) + if (settings.show_network_input) { cv::Mat netinput = poseestimator->last_network_input(); if (!netinput.empty()) @@ -587,7 +730,6 @@ void neuralnet_tracker::draw_gizmos( neuralnet_tracker::neuralnet_tracker() { opencv_init(); - cv::setNumThreads(num_threads); } @@ -609,6 +751,8 @@ module_status neuralnet_tracker::start_tracker(QFrame* videoframe) layout->addWidget(&*videoWidget); videoframe->setLayout(&*layout); videoWidget->show(); + num_threads = settings.num_threads; + cv::setNumThreads(num_threads); start(); return status_ok(); } @@ -633,7 +777,7 @@ bool neuralnet_tracker::load_and_initialize_model() // openmp settings. Which is what we do. omp_set_num_threads directly // before running the inference pass. opts.SetIntraOpNumThreads(num_threads); - opts.SetInterOpNumThreads(num_threads); + opts.SetInterOpNumThreads(1); allocator_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); localizer.emplace( @@ -656,11 +800,11 @@ bool neuralnet_tracker::load_and_initialize_model() bool neuralnet_tracker::open_camera() { - int fps = enum_to_fps(s.force_fps); + int fps = enum_to_fps(settings.force_fps); QMutexLocker l(&camera_mtx); - camera = video::make_camera(s.camera_name); + camera = video::make_camera(settings.camera_name); if (!camera) return false; @@ -673,7 +817,7 @@ bool neuralnet_tracker::open_camera() if (fps) args.fps = fps; - args.use_mjpeg = s.use_mjpeg; + args.use_mjpeg = settings.use_mjpeg; if (!camera->start(args)) { @@ -687,7 +831,7 @@ bool neuralnet_tracker::open_camera() void neuralnet_tracker::set_intrinsics() { const int w = grayscale.cols, h = grayscale.rows; - const double diag_fov = s.fov * M_PI / 180.; + const double diag_fov = settings.fov * M_PI / 180.; const double fov_w = 2.*atan(tan(diag_fov/2.)/sqrt(1. + h/(double)w * h/(double)w)); const double fov_h = 2.*atan(tan(diag_fov/2.)/sqrt(1. + w/(double)h * w/(double)h)); const double focal_length_w = 1. / tan(.5 * fov_w); @@ -746,8 +890,13 @@ void neuralnet_tracker::run() set_intrinsics(); + const auto nt = omp_get_num_threads(); + omp_set_num_threads(num_threads); + detect(); + omp_set_num_threads(nt); + if (frame.rows > 0) videoWidget->update_image(frame); @@ -823,26 +972,29 @@ neuralnet_dialog::neuralnet_dialog() : ui.setupUi(this); make_fps_combobox(); - tie_setting(s.force_fps, ui.cameraFPS); + tie_setting(settings.force_fps, ui.cameraFPS); for (const auto& str : video::camera_names()) ui.cameraName->addItem(str); - tie_setting(s.camera_name, ui.cameraName); - tie_setting(s.fov, ui.cameraFOV); - tie_setting(s.offset_fwd, ui.tx_spin); - tie_setting(s.offset_up, ui.ty_spin); - tie_setting(s.offset_right, ui.tz_spin); - tie_setting(s.show_network_input, ui.showNetworkInput); - tie_setting(s.use_mjpeg, ui.use_mjpeg); + tie_setting(settings.camera_name, ui.cameraName); + tie_setting(settings.fov, ui.cameraFOV); + tie_setting(settings.offset_fwd, ui.tx_spin); + tie_setting(settings.offset_up, ui.ty_spin); + tie_setting(settings.offset_right, ui.tz_spin); + tie_setting(settings.show_network_input, ui.showNetworkInput); + tie_setting(settings.roi_filter_alpha, ui.roiFilterAlpha); + tie_setting(settings.use_mjpeg, ui.use_mjpeg); + tie_setting(settings.roi_zoom, ui.roiZoom); + tie_setting(settings.num_threads, ui.threadCount); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); connect(ui.camera_settings, SIGNAL(clicked()), this, SLOT(camera_settings())); - connect(&s.camera_name, value_::value_changed(), this, &neuralnet_dialog::update_camera_settings_state); + connect(&settings.camera_name, value_::value_changed(), this, &neuralnet_dialog::update_camera_settings_state); - update_camera_settings_state(s.camera_name); + update_camera_settings_state(settings.camera_name); connect(&calib_timer, &QTimer::timeout, this, &neuralnet_dialog::trans_calib_step); calib_timer.setInterval(35); @@ -852,7 +1004,7 @@ neuralnet_dialog::neuralnet_dialog() : void neuralnet_dialog::doOK() { - s.b->save(); + settings.b->save(); close(); } @@ -871,7 +1023,7 @@ void neuralnet_dialog::camera_settings() (void)tracker->camera->show_dialog(); } else - (void)video::show_dialog(s.camera_name); + (void)video::show_dialog(settings.camera_name); } @@ -942,9 +1094,9 @@ void neuralnet_dialog::startstop_trans_calib(bool start) trans_calib.reset(); ui.sample_count_display->setText(QString()); // Tracker must run with zero'ed offset for calibration. - s.offset_fwd = 0; - s.offset_up = 0; - s.offset_right = 0; + settings.offset_fwd = 0; + settings.offset_up = 0; + settings.offset_right = 0; } else { @@ -952,9 +1104,9 @@ void neuralnet_dialog::startstop_trans_calib(bool start) qDebug() << "pt: stopping translation calibration"; { auto [tmp, nsamples] = trans_calib.get_estimate(); - s.offset_fwd = int(tmp[0]); - s.offset_up = int(tmp[1]); - s.offset_right = int(tmp[2]); + settings.offset_fwd = int(tmp[0]); + settings.offset_up = int(tmp[1]); + settings.offset_right = int(tmp[2]); } } ui.tx_spin->setEnabled(!start); @@ -968,7 +1120,7 @@ void neuralnet_dialog::startstop_trans_calib(bool start) } -settings::settings() : opts("neuralnet-tracker") {} +Settings::Settings() : opts("neuralnet-tracker") {} } // neuralnet_tracker_ns diff --git a/tracker-neuralnet/ftnoir_tracker_neuralnet.h b/tracker-neuralnet/ftnoir_tracker_neuralnet.h index b6405d1a..25f1a0a2 100644 --- a/tracker-neuralnet/ftnoir_tracker_neuralnet.h +++ b/tracker-neuralnet/ftnoir_tracker_neuralnet.h @@ -50,7 +50,7 @@ enum fps_choices }; -struct settings : opts { +struct Settings : opts { value offset_fwd { b, "offset-fwd", 200 }, // Millimeters offset_up { b, "offset-up", 0 }, offset_right { b, "offset-right", 0 }; @@ -58,8 +58,11 @@ struct settings : opts { value fov { b, "field-of-view", 56 }; value force_fps { b, "force-fps", fps_default }; value show_network_input { b, "show-network-input", false }; + value roi_filter_alpha{ b, "roi-filter-alpha", 1. }; + value roi_zoom{ b, "roi-zoom", 1. }; value use_mjpeg { b, "use-mjpeg", false }; - settings(); + value num_threads { b, "num-threads", 1 }; + Settings(); }; @@ -118,23 +121,26 @@ class PoseEstimator private: // Operates on the private image data members int find_input_intensity_90_pct_quantile() const; + + int64_t model_version = 0; Ort::Session session{nullptr}; + Ort::Allocator allocator; // Inputs cv::Mat scaled_frame{}, input_mat{}; - Ort::Value input_val{nullptr}; + std::vector input_val; + std::vector input_names; // Outputs cv::Vec output_coord{}; cv::Vec output_quat{}; cv::Vec output_box{}; - Ort::Value output_val[3] = { - Ort::Value{nullptr}, - Ort::Value{nullptr}, - Ort::Value{nullptr}}; + std::vector output_val; + std::vector output_names; + size_t num_recurrent_states = 0; double last_inference_time = 0; - int64_t model_version = 0; }; + class neuralnet_tracker : protected virtual QThread, public ITracker { Q_OBJECT @@ -162,7 +168,7 @@ private: Affine compute_pose(const PoseEstimator::Face &face) const; - settings s; + Settings settings; std::optional localizer; std::optional poseestimator; Ort::Env env{nullptr}; @@ -177,6 +183,7 @@ private: double fps = 0; double last_inference_time = 0; static constexpr double RC = .25; + int num_threads = 1; QMutex mtx; // Protects the pose Affine pose_; @@ -197,7 +204,7 @@ private: void make_fps_combobox(); Ui::Form ui; - settings s; + Settings settings; // Calibration code mostly taken from point tracker QTimer calib_timer; diff --git a/tracker-neuralnet/lang/nl_NL.ts b/tracker-neuralnet/lang/nl_NL.ts index a2dcd958..95da8f4c 100644 --- a/tracker-neuralnet/lang/nl_NL.ts +++ b/tracker-neuralnet/lang/nl_NL.ts @@ -64,6 +64,22 @@ Don't roll or change position. MJPEG + + Tuning / Debug + + + + ROI Smoothing Alpha + + + + ROI Zoom + + + + Thread Count + + neuralnet_tracker_ns::neuralnet_dialog diff --git a/tracker-neuralnet/lang/ru_RU.ts b/tracker-neuralnet/lang/ru_RU.ts index 7e8a9c09..a8252299 100644 --- a/tracker-neuralnet/lang/ru_RU.ts +++ b/tracker-neuralnet/lang/ru_RU.ts @@ -64,6 +64,22 @@ Don't roll or change position. MJPEG + + Tuning / Debug + + + + ROI Smoothing Alpha + + + + ROI Zoom + + + + Thread Count + + neuralnet_tracker_ns::neuralnet_dialog diff --git a/tracker-neuralnet/lang/stub.ts b/tracker-neuralnet/lang/stub.ts index 66e1695c..80103fde 100644 --- a/tracker-neuralnet/lang/stub.ts +++ b/tracker-neuralnet/lang/stub.ts @@ -64,6 +64,22 @@ Don't roll or change position. MJPEG + + Tuning / Debug + + + + ROI Smoothing Alpha + + + + ROI Zoom + + + + Thread Count + + neuralnet_tracker_ns::neuralnet_dialog diff --git a/tracker-neuralnet/lang/zh_CN.ts b/tracker-neuralnet/lang/zh_CN.ts index 8e9513b3..f55c12fc 100644 --- a/tracker-neuralnet/lang/zh_CN.ts +++ b/tracker-neuralnet/lang/zh_CN.ts @@ -64,6 +64,22 @@ Don't roll or change position. MJPEG + + Tuning / Debug + + + + ROI Smoothing Alpha + + + + ROI Zoom + + + + Thread Count + + neuralnet_tracker_ns::neuralnet_dialog diff --git a/tracker-neuralnet/neuralnet-trackercontrols.ui b/tracker-neuralnet/neuralnet-trackercontrols.ui index 5f72a809..acbfe909 100644 --- a/tracker-neuralnet/neuralnet-trackercontrols.ui +++ b/tracker-neuralnet/neuralnet-trackercontrols.ui @@ -9,15 +9,15 @@ 0 0 - 727 - 202 + 721 + 277 Tracker settings - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -364,11 +364,143 @@ Don't roll or change position. - - - - Show Network Input + + + + + 0 + 0 + + + + + 0 + 0 + + + + Tuning / Debug + + + + + Qt::Vertical + + + + + + + Thread Count + + + + + + + + 0 + 0 + + + + ROI Smoothing Alpha + + + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + Show Network Input + + + + + + + + 0 + 0 + + + + + 150 + 16777215 + + + + false + + + 2 + + + 1.000000000000000 + + + 0.010000000000000 + + + 1.000000000000000 + + + + + + + ROI Zoom + + + + + + + Qt::Vertical + + + + + + + 0.100000000000000 + + + 2.000000000000000 + + + 0.010000000000000 + + + 1.000000000000000 + + + + + + + 1 + + + 32 + + + + -- cgit v1.2.3