diff options
author | Michael Welter <michael@welter-4d.de> | 2022-05-17 07:50:55 +0200 |
---|---|---|
committer | Michael Welter <michael@welter-4d.de> | 2022-05-19 20:47:11 +0200 |
commit | f788d94a9ea446c7b687b4a8f3d4cfae41a61296 (patch) | |
tree | 7be48dd4e36cb8ece4f165defa425f16f8f08e38 /tracker-neuralnet | |
parent | 4d6e80a6c585f6929f6bf4bf319932732160ce84 (diff) |
tracker/nn: Add resolution setting and tooltips
Tooltips only for half of the settings or so. When hovering over the
actual input boxes.
Diffstat (limited to 'tracker-neuralnet')
-rw-r--r-- | tracker-neuralnet/ftnoir_tracker_neuralnet.cpp | 123 | ||||
-rw-r--r-- | tracker-neuralnet/ftnoir_tracker_neuralnet.h | 22 | ||||
-rw-r--r-- | tracker-neuralnet/lang/nl_NL.ts | 32 | ||||
-rw-r--r-- | tracker-neuralnet/lang/ru_RU.ts | 32 | ||||
-rw-r--r-- | tracker-neuralnet/lang/stub.ts | 32 | ||||
-rw-r--r-- | tracker-neuralnet/lang/zh_CN.ts | 32 | ||||
-rw-r--r-- | tracker-neuralnet/neuralnet-trackercontrols.ui | 169 |
7 files changed, 369 insertions, 73 deletions
diff --git a/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp b/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp index 00f3f281..f849f4e1 100644 --- a/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp +++ b/tracker-neuralnet/ftnoir_tracker_neuralnet.cpp @@ -16,7 +16,6 @@ #include <opencv2/imgcodecs.hpp> #include "compat/timer.hpp" #include <omp.h> -#include <stdexcept> #ifdef _MSC_VER # pragma warning(disable : 4702) @@ -31,6 +30,8 @@ #include <algorithm> #include <chrono> #include <string> +#include <stdexcept> + // Some demo code for onnx // https://github.com/microsoft/onnxruntime/blob/master/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/C_Api_Sample.cpp @@ -59,6 +60,35 @@ float sigmoid(float x) } +cv::Rect make_crop_rect_for_aspect(const cv::Size &size, int aspect_w, int aspect_h) +{ + auto [w, h] = size; + if ( w*aspect_h > aspect_w*h ) + { + // Image is too wide + const int new_w = (aspect_w*h)/aspect_h; + return cv::Rect((w - new_w)/2, 0, new_w, h); + } + else + { + const int new_h = (aspect_h*w)/aspect_w; + return cv::Rect(0, (h - new_h)/2, w, new_h); + } +} + +cv::Rect make_crop_rect_multiple_of(const cv::Size &size, int multiple) +{ + const int new_w = (size.width / multiple) * multiple; + const int new_h = (size.height / multiple) * multiple; + return cv::Rect( + (size.width-new_w)/2, + (size.height-new_h)/2, + new_w, + new_h + ); +} + + template<class T> cv::Rect_<T> squarize(const cv::Rect_<T> &r) { @@ -752,7 +782,6 @@ module_status neuralnet_tracker::start_tracker(QFrame* videoframe) videoframe->setLayout(&*layout); videoWidget->show(); num_threads = settings.num_threads; - cv::setNumThreads(num_threads); start(); return status_ok(); } @@ -774,8 +803,7 @@ bool neuralnet_tracker::load_and_initialize_model() auto opts = Ort::SessionOptions{}; // Do thread settings here do anything? // There is a warning which says to control number of threads via - // openmp settings. Which is what we do. omp_set_num_threads directly - // before running the inference pass. + // openmp settings. Which is what we do. opts.SetIntraOpNumThreads(num_threads); opts.SetInterOpNumThreads(1); allocator_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); @@ -800,6 +828,8 @@ bool neuralnet_tracker::load_and_initialize_model() bool neuralnet_tracker::open_camera() { + int rint = std::clamp(*settings.resolution, 0, (int)std::size(resolution_choices)-1); + resolution_tuple res = resolution_choices[rint]; int fps = enum_to_fps(settings.force_fps); QMutexLocker l(&camera_mtx); @@ -811,9 +841,11 @@ bool neuralnet_tracker::open_camera() video::impl::camera::info args {}; - args.width = 320; - args.height = 240; - + if (res.width) + { + args.width = res.width; + args.height = res.height; + } if (fps) args.fps = fps; @@ -844,8 +876,34 @@ void neuralnet_tracker::set_intrinsics() } +class GuardedThreadCountSwitch +{ + int old_num_threads_cv = 1; + int old_num_threads_omp = 1; + public: + GuardedThreadCountSwitch(int num_threads) + { + old_num_threads_cv = cv::getNumThreads(); + old_num_threads_omp = omp_get_num_threads(); + omp_set_num_threads(num_threads); + cv::setNumThreads(num_threads); + } + + ~GuardedThreadCountSwitch() + { + omp_set_num_threads(old_num_threads_omp); + cv::setNumThreads(old_num_threads_cv); + } + + GuardedThreadCountSwitch(const GuardedThreadCountSwitch&) = delete; + GuardedThreadCountSwitch& operator=(const GuardedThreadCountSwitch&) = delete; +}; + + void neuralnet_tracker::run() { + GuardedThreadCountSwitch switch_num_threads_to(num_threads); + if (!open_camera()) return; @@ -870,7 +928,8 @@ void neuralnet_tracker::run() continue; } - auto color = cv::Mat(img.height, img.width, CV_8UC(img.channels), (void*)img.data, img.stride); + auto color = prepare_input_image(img); + color.copyTo(frame); switch (img.channels) @@ -890,13 +949,8 @@ 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); @@ -907,6 +961,33 @@ void neuralnet_tracker::run() } +cv::Mat neuralnet_tracker::prepare_input_image(const video::frame& frame) +{ + auto img = cv::Mat(frame.height, frame.width, CV_8UC(frame.channels), (void*)frame.data, frame.stride); + + // Crop if aspect ratio is not 4:3 + if (img.rows*4 != img.cols*3) + { + img = img(make_crop_rect_for_aspect(img.size(), 4, 3)); + } + + img = img(make_crop_rect_multiple_of(img.size(), 4)); + + if (img.cols > 640) + { + cv::pyrDown(img, downsized_original_images_[0]); + img = downsized_original_images_[0]; + } + if (img.cols > 640) + { + cv::pyrDown(img, downsized_original_images_[1]); + img = downsized_original_images_[1]; + } + + return img; +} + + void neuralnet_tracker::update_fps(double dt) { const double alpha = dt/(dt + RC); @@ -965,6 +1046,18 @@ void neuralnet_dialog::make_fps_combobox() } } +void neuralnet_dialog::make_resolution_combobox() +{ + int k=0; + for (const auto [w, h] : resolution_choices) + { + const QString s = (w == 0) + ? tr("Default") + : QString::number(w) + " x " + QString::number(h); + ui.resolution->addItem(s, k++); + } +} + neuralnet_dialog::neuralnet_dialog() : trans_calib(1, 2) @@ -972,7 +1065,7 @@ neuralnet_dialog::neuralnet_dialog() : ui.setupUi(this); make_fps_combobox(); - tie_setting(settings.force_fps, ui.cameraFPS); + make_resolution_combobox(); for (const auto& str : video::camera_names()) ui.cameraName->addItem(str); @@ -987,6 +1080,8 @@ neuralnet_dialog::neuralnet_dialog() : tie_setting(settings.use_mjpeg, ui.use_mjpeg); tie_setting(settings.roi_zoom, ui.roiZoom); tie_setting(settings.num_threads, ui.threadCount); + tie_setting(settings.resolution, ui.resolution); + tie_setting(settings.force_fps, ui.cameraFPS); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); diff --git a/tracker-neuralnet/ftnoir_tracker_neuralnet.h b/tracker-neuralnet/ftnoir_tracker_neuralnet.h index 25f1a0a2..ace16528 100644 --- a/tracker-neuralnet/ftnoir_tracker_neuralnet.h +++ b/tracker-neuralnet/ftnoir_tracker_neuralnet.h @@ -25,6 +25,7 @@ #include <memory> #include <cinttypes> +#include <array> #include <onnxruntime_cxx_api.h> @@ -49,6 +50,23 @@ enum fps_choices fps_MAX = 3 }; +struct resolution_tuple +{ + int width; + int height; +}; + +static const std::array<resolution_tuple, 7> resolution_choices = +{{ + { 320, 240 }, + { 640, 480 }, + { 800, 600 }, + { 1024, 768 }, + { 1280, 720 }, + { 1920, 1080}, + { 0, 0 } +}}; + struct Settings : opts { value<int> offset_fwd { b, "offset-fwd", 200 }, // Millimeters @@ -62,6 +80,7 @@ struct Settings : opts { value<double> roi_zoom{ b, "roi-zoom", 1. }; value<bool> use_mjpeg { b, "use-mjpeg", false }; value<int> num_threads { b, "num-threads", 1 }; + value<int> resolution { b, "force-resolution", 0 }; Settings(); }; @@ -159,6 +178,7 @@ private: bool detect(); bool open_camera(); void set_intrinsics(); + cv::Mat prepare_input_image(const video::frame& frame); bool load_and_initialize_model(); void draw_gizmos( cv::Mat frame, @@ -176,6 +196,7 @@ private: CamIntrinsics intrinsics{}; cv::Mat frame, grayscale; + std::array<cv::Mat,2> downsized_original_images_ = {}; // Image pyramid std::optional<cv::Rect2f> last_localizer_roi; std::optional<cv::Rect2f> last_roi; static constexpr float head_size_mm = 200.f; @@ -202,6 +223,7 @@ public: void unregister_tracker() override; private: void make_fps_combobox(); + void make_resolution_combobox(); Ui::Form ui; Settings settings; diff --git a/tracker-neuralnet/lang/nl_NL.ts b/tracker-neuralnet/lang/nl_NL.ts index 95da8f4c..cb6d1da0 100644 --- a/tracker-neuralnet/lang/nl_NL.ts +++ b/tracker-neuralnet/lang/nl_NL.ts @@ -80,6 +80,38 @@ Don't roll or change position.</source> <source>Thread Count</source> <translation type="unfinished"></translation> </message> + <message> + <source>Resolution</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Field of view. Needed to transform the pose to world coordinates.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Requested video frame rate. Actual setting may not be supported by the camera.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Show the image patch that the pose estimation model sees.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>neuralnet_tracker_ns::neuralnet_dialog</name> diff --git a/tracker-neuralnet/lang/ru_RU.ts b/tracker-neuralnet/lang/ru_RU.ts index a8252299..ed69e9a7 100644 --- a/tracker-neuralnet/lang/ru_RU.ts +++ b/tracker-neuralnet/lang/ru_RU.ts @@ -80,6 +80,38 @@ Don't roll or change position.</source> <source>Thread Count</source> <translation type="unfinished"></translation> </message> + <message> + <source>Resolution</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Field of view. Needed to transform the pose to world coordinates.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Requested video frame rate. Actual setting may not be supported by the camera.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Show the image patch that the pose estimation model sees.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>neuralnet_tracker_ns::neuralnet_dialog</name> diff --git a/tracker-neuralnet/lang/stub.ts b/tracker-neuralnet/lang/stub.ts index 80103fde..db45f47a 100644 --- a/tracker-neuralnet/lang/stub.ts +++ b/tracker-neuralnet/lang/stub.ts @@ -80,6 +80,38 @@ Don't roll or change position.</source> <source>Thread Count</source> <translation type="unfinished"></translation> </message> + <message> + <source>Resolution</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Field of view. Needed to transform the pose to world coordinates.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Requested video frame rate. Actual setting may not be supported by the camera.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Show the image patch that the pose estimation model sees.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>neuralnet_tracker_ns::neuralnet_dialog</name> diff --git a/tracker-neuralnet/lang/zh_CN.ts b/tracker-neuralnet/lang/zh_CN.ts index f55c12fc..d13219f0 100644 --- a/tracker-neuralnet/lang/zh_CN.ts +++ b/tracker-neuralnet/lang/zh_CN.ts @@ -80,6 +80,38 @@ Don't roll or change position.</source> <source>Thread Count</source> <translation type="unfinished"></translation> </message> + <message> + <source>Resolution</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Field of view. Needed to transform the pose to world coordinates.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Requested video frame rate. Actual setting may not be supported by the camera.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Number of threads. Can be used to balance the CPU load between the game and the tracker.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Show the image patch that the pose estimation model sees.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>neuralnet_tracker_ns::neuralnet_dialog</name> diff --git a/tracker-neuralnet/neuralnet-trackercontrols.ui b/tracker-neuralnet/neuralnet-trackercontrols.ui index acbfe909..43b316e9 100644 --- a/tracker-neuralnet/neuralnet-trackercontrols.ui +++ b/tracker-neuralnet/neuralnet-trackercontrols.ui @@ -9,8 +9,8 @@ <rect> <x>0</x> <y>0</y> - <width>721</width> - <height>277</height> + <width>647</width> + <height>305</height> </rect> </property> <property name="windowTitle"> @@ -60,13 +60,10 @@ <string>Camera Configuration</string> </property> <layout class="QGridLayout" name="gridLayout_4"> - <item row="3" column="1"> - <widget class="QComboBox" name="cameraName"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <item row="4" column="0"> + <widget class="QLabel" name="resolution_label"> + <property name="text"> + <string>Resolution</string> </property> </widget> </item> @@ -78,6 +75,9 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="toolTip"> + <string>Field of view. Needed to transform the pose to world coordinates.</string> + </property> <property name="locale"> <locale language="English" country="UnitedStates"/> </property> @@ -96,20 +96,6 @@ </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>MJPEG</string> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_10"> - <property name="text"> - <string>Camera name</string> - </property> - </widget> - </item> <item row="0" column="0"> <widget class="QLabel" name="label_9"> <property name="text"> @@ -125,31 +111,65 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="toolTip"> + <string>Requested video frame rate. Actual setting may not be supported by the camera.</string> + </property> </widget> </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="use_mjpeg"> + <item row="7" column="1"> + <widget class="QPushButton" name="camera_settings"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> - <string/> + <string>Camera settings</string> </property> </widget> </item> <item row="4" column="1"> - <widget class="QPushButton" name="camera_settings"> + <widget class="QComboBox" name="resolution"> + <property name="toolTip"> + <string>The requested resolution for cases where the camera delivers maximum frame rate only for a particular resolution. The image may still be downscaled to the internal resolution.</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QComboBox" name="cameraName"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_10"> <property name="text"> - <string>Camera settings</string> + <string>Camera name</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QCheckBox" name="use_mjpeg"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_11"> + <property name="text"> + <string>MJPEG</string> </property> </widget> </item> @@ -189,6 +209,12 @@ <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_11"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <property name="verticalSpacing"> + <number>0</number> + </property> <item row="1" column="1"> <widget class="QSpinBox" name="ty_spin"> <property name="maximumSize"> @@ -382,21 +408,34 @@ Don't roll or change position.</string> <string>Tuning / Debug</string> </property> <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="7"> - <widget class="Line" name="line_3"> + <item row="0" column="10"> + <widget class="Line" name="line_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> - <item row="0" column="8"> - <widget class="QLabel" name="threadCountLabel"> - <property name="text"> - <string>Thread Count</string> + <item row="0" column="1"> + <widget class="QSpinBox" name="threadCount"> + <property name="toolTip"> + <string>Number of threads. Can be used to balance the CPU load between the game and the tracker.</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>32</number> </property> </widget> </item> - <item row="0" column="2"> + <item row="0" column="4"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + <item row="0" column="8"> <widget class="QLabel" name="roiFilterAlphaLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> @@ -409,14 +448,21 @@ Don't roll or change position.</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="Line" name="line"> + <item row="0" column="11"> + <widget class="QLabel" name="roiZoomLabel"> + <property name="text"> + <string>ROI Zoom</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="Line" name="line_3"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> </widget> </item> - <item row="0" column="0"> + <item row="0" column="3"> <widget class="QCheckBox" name="showNetworkInput"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> @@ -424,12 +470,15 @@ Don't roll or change position.</string> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="toolTip"> + <string>Show the image patch that the pose estimation model sees.</string> + </property> <property name="text"> <string>Show Network Input</string> </property> </widget> </item> - <item row="0" column="3"> + <item row="0" column="9"> <widget class="QDoubleSpinBox" name="roiFilterAlpha"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> @@ -443,6 +492,9 @@ Don't roll or change position.</string> <height>16777215</height> </size> </property> + <property name="toolTip"> + <string>Amount of smoothing of the face region coordinates. Can help stabilize the pose.</string> + </property> <property name="wrapping"> <bool>false</bool> </property> @@ -460,22 +512,18 @@ Don't roll or change position.</string> </property> </widget> </item> - <item row="0" column="5"> - <widget class="QLabel" name="roiZoomLabel"> + <item row="0" column="0"> + <widget class="QLabel" name="threadCountLabel"> <property name="text"> - <string>ROI Zoom</string> - </property> - </widget> - </item> - <item row="0" column="4"> - <widget class="Line" name="line_2"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + <string>Thread Count</string> </property> </widget> </item> - <item row="0" column="6"> + <item row="0" column="12"> <widget class="QDoubleSpinBox" name="roiZoom"> + <property name="toolTip"> + <string>Zoom factor for the face region. Applied before the patch is fed into the pose estimation model. There is a sweet spot near 1.</string> + </property> <property name="minimum"> <double>0.100000000000000</double> </property> @@ -490,15 +538,18 @@ Don't roll or change position.</string> </property> </widget> </item> - <item row="0" column="9"> - <widget class="QSpinBox" name="threadCount"> - <property name="minimum"> - <number>1</number> + <item row="0" column="13"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - <property name="maximum"> - <number>32</number> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> </property> - </widget> + </spacer> </item> </layout> </widget> |