diff --git a/ChangeLog.txt b/ChangeLog.txt index 758f9b1f53..05179dc2b8 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -90,6 +90,7 @@ ViSP 3.x.x (Version in development) . [#1485] Build issue around nlohmann_json usage with VTK 9.2.0 or more recent version used as an embedded 3rdparty by PCL . [#1494] Memory management issue in vpArray2D::resize() + . [#1495] vpMeLine is unable to seek extremities ---------------------------------------------- ViSP 3.6.0 (released September 22, 2023) - Contributors: diff --git a/doc/tutorial/detection_dnn/tutorial-detection-dnn.dox b/doc/tutorial/detection_dnn/tutorial-detection-dnn.dox index c5897fb046..d97c73a9ae 100644 --- a/doc/tutorial/detection_dnn/tutorial-detection-dnn.dox +++ b/doc/tutorial/detection_dnn/tutorial-detection-dnn.dox @@ -46,9 +46,9 @@ Please ensure to install a CuDNN version that is compatible with your version of 3. Then, you need to determine the Compute capability of your GPU either from the [NVidia website](https://developer.nvidia.com/cuda-gpus) or using the [nvidia-smi tool](https://developer.nvidia.com/nvidia-system-management-interface). On a Debian distribution, you would run: - ``` - $ export GPU_CAPABILITIES=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader) - ``` +\code{.sh} +$ export GPU_CAPABILITIES=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader) +\endcode 4. Check if the package already installed on your computer. On a Debian distribution, you would run: ``` $ apt list --installed | grep -i opencv @@ -61,51 +61,50 @@ or using the [nvidia-smi tool](https://developer.nvidia.com/nvidia-system-manage ``` 5. Install OpenCV dependencies. On a Debian distribution, you would run: - ``` - ## libx11-dev is a recommended ViSP 3rd parties - # If you installed another version of CUDA, please install the version of CuDNN which is compatible with your version - $ sudo apt update - $ sudo apt install libgtk-3-dev \ - cmake \ - git \ - pip \ - cmake-curses-gui \ - locate \ - libx11-dev - ``` +\code{.sh} +# libx11-dev is a recommended ViSP 3rd parties +# If you installed another version of CUDA, please install the version of CuDNN which is compatible with your version +$ sudo apt update +$ sudo apt install libgtk-3-dev \ + cmake \ + git \ + pip \ + cmake-curses-gui \ + locate \ + libx11-dev +\endcode 6. Get the sources. The \b vpDetectorDNNOpenCV has been tested with **OpenCV 4.7**. First, get the OpenCV_contrib sources, that contain the Cuda DNN module. On a Debian distribution, you would run: - ``` - $ cd ${HOME}/visp_ws/3rdparty/ - $ git clone --branch 4.7.0 https://github.com/opencv/opencv_contrib - $ git clone --branch 4.7.0 https://github.com/opencv/opencv - ``` +\code{.sh} +$ cd ${HOME}/visp_ws/3rdparty/ +$ git clone --branch 4.7.0 https://github.com/opencv/opencv_contrib +$ git clone --branch 4.7.0 https://github.com/opencv/opencv +\endcode 7. Compile OpenCV and install it from source. On a Debian distribution, you would run: - ``` - $ mkdir -p ${HOME}/visp_ws/3rdparty/opencv/build &&\ - cd ${HOME}/visp_ws/3rdparty/opencv/build - $ cmake .. \ - -DCMAKE_BUILD_TYPE=RELEASE \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_INSTALL_LIBDIR=lib \ - -DWITH_CUDA=ON \ - -DWITH_CUDNN=ON \ - -DOPENCV_DNN_CUDA=ON \ - -DENABLE_FAST_MATH=1 \ - -DCUDA_FAST_MATH=1 \ - -DCUDA_ARCH_BIN=${GPU_CAPABILITIES} \ - -DWITH_CUBLAS=1 \ - -DOPENCV_EXTRA_MODULES_PATH=${HOME}/visp_ws/3rdparty/opencv_contrib/modules \ - -DBUILD_PERF_TESTS=Off \ - -DBUILD_TESTS=Off \ - -DBUILD_EXAMPLES=Off \ - -DBUILD_opencv_apps=Off \ - -DBUILD_opencv_java_bindings_generator=Off \ - -DBUILD_opencv_js=Off - ``` +\code{.sh} +$ mkdir -p ${HOME}/visp_ws/3rdparty/opencv/build && cd ${HOME}/visp_ws/3rdparty/opencv/build +$ cmake .. \ + -DCMAKE_BUILD_TYPE=RELEASE \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DWITH_CUDA=ON \ + -DWITH_CUDNN=ON \ + -DOPENCV_DNN_CUDA=ON \ + -DENABLE_FAST_MATH=1 \ + -DCUDA_FAST_MATH=1 \ + -DCUDA_ARCH_BIN=${GPU_CAPABILITIES} \ + -DWITH_CUBLAS=1 \ + -DOPENCV_EXTRA_MODULES_PATH=${HOME}/visp_ws/3rdparty/opencv_contrib/modules \ + -DBUILD_PERF_TESTS=Off \ + -DBUILD_TESTS=Off \ + -DBUILD_EXAMPLES=Off \ + -DBUILD_opencv_apps=Off \ + -DBUILD_opencv_java_bindings_generator=Off \ + -DBUILD_opencv_js=Off +\endcode 8. Compile and install OpenCV. On a Debian distribution, you would run: ``` @@ -230,22 +229,22 @@ or for a non-sorted vector with: The default behavior is to detect human faces, but you can input another model to detect the objects you want. To see which are the options, run: -``` +\code{.sh} $ cd $VISP_WS/visp-build/tutorial/detection/dnn $ ./tutorial-dnn-object-detection-live --help -``` +\endcode \subsection dnn_usecase_face_detection Face detection The default behavior is to detect human faces using a model provided by OpenCV and learned over a ResNet 10 network. If you have a laptop, simply run: -``` +\code{.sh} $ cd $VISP_WS/visp-build/tutorial/detection/dnn $ ./tutorial-dnn-object-detection-live -``` +\endcode The previous command is similar to the next one: -``` +\code{.sh} $ CONFIG=opencv_face_detector.pbtxt \ MODEL=opencv_face_detector_uint8.pb \ LABELS=class.txt \ @@ -255,7 +254,7 @@ $ CONFIG=opencv_face_detector.pbtxt \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --confThresh 0.35 --filterThresh -0.25 --scale 1 -``` +\endcode \subsection dnn_models_coco COCO dataset objects detection @@ -271,7 +270,7 @@ the weights (`frozen_inference_graph.pb`) [there](http://download.tensorflow.org and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/master/tutorial/detection/dnn/coco_classes.txt). To run the tutorial with the Faster-RCNN network, please run the following commands: -``` +\code{.sh} $ cd $VISP_WS/visp-build/tutorial/detection/dnn $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=${DNN_PATH}/Faster-RCNN/cfg/config.pbtxt \ @@ -283,13 +282,13 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --confThresh 0.35 --filterThresh -0.25 --scale 1 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_faster-rcnn.json -``` +\endcode If you want to train your own Faster-RCNN model, please refer to this [tutorial](https://debuggercafe.com/how-to-train-faster-rcnn-resnet50-fpn-v2-on-custom-dataset/). @@ -303,7 +302,7 @@ and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/ The parameters to use with this network were found [there](https://github.com/opencv/opencv/blob/0052d46b8e33c7bfe0e1450e4bff28b88f455570/samples/dnn/models.yml#L68). To run the tutorial with the `Mobilenet V1` network, please run the following commands: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=${DNN_PATH}/MobileNet-SSD/cfg/ssd_mobilenet_v1_coco_2017_11_17.pbtxt \ MODEL=${DNN_PATH}/MobileNet-SSD/weights/frozen_inference_graph.pb \ @@ -314,13 +313,13 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --filterThresh -0.25 --scale 1 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_ssd-mobilenet_v1.json -``` +\endcode If you would rather use the v3 of Mobilenet-SSD, please download the config file (`ssd_mobilenet_v3_large_coco_2020_01_14.pbtxt`) [here](https://gist.github.com/dkurt/54a8e8b51beb3bd3f770b79e56927bd7), @@ -328,7 +327,7 @@ the weights (`frozen_inference_graph.pb`) [there](http://download.tensorflow.org and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/master/tutorial/detection/dnn/coco_classes.txt). Then, to run the tutorial with the `Mobilenet V3` network, please run the following commands: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=${DNN_PATH}/MobileNet-SSD/cfg/ssd_mobilenet_v3_large_coco_2020_01_14.pbtxt \ MODEL=${DNN_PATH}/MobileNet-SSD/weights/frozen_inference_graph.pb \ @@ -339,13 +338,13 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0.0019 0.0019 0.0019 \ --filterThresh -0.25 --scale 0.00389 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_ssd-mobilenet_v3.json -``` +\endcode If you want to train your own MobileNet SSD model, please refer to this [tutorial](https://www.forecr.io/blogs/ai-algorithms/how-to-train-ssd-mobilenet-model-for-object-detection-using-pytorch) @@ -358,7 +357,7 @@ the weights (`yolov3.weights`) [there](https://pjreddie.com/media/files/yolov3.w and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/master/tutorial/detection/dnn/coco_classes.txt). To run the tutorial program `tutorial-dnn-object-detection-live.cpp`, use the following commands: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=${DNN_PATH}/yolov3/cfg/yolov3.cfg \ MODEL=${DNN_PATH}/yolov3/weights/yolov3.weights \ @@ -369,13 +368,13 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --filterThresh -0.25 --scale 0.0039 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_yolov3.json -``` +\endcode If you want to train your own YoloV3 model, please refer to the [official documentation](https://github.com/ultralytics/yolov3). @@ -386,7 +385,7 @@ the weights (`yolov4-tiny.weights`) [there](https://github.com/AlexeyAB/darknet/ and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/master/tutorial/detection/dnn/coco_classes.txt). To run the tutorial program `tutorial-dnn-object-detection-live.cpp`, use the following commands: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=${DNN_PATH}/yolov4/cfg/yolov4-tiny.cfg \ MODEL=${DNN_PATH}/yolov4/weights/yolov4-tiny.weights \ @@ -397,13 +396,13 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --filterThresh -0.25 --scale 0.0039 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_yolov4.json -``` +\endcode If you want to train your own YoloV4 model, please refer to the [official documentation](https://github.com/AlexeyAB/darknet#how-to-train-to-detect-your-custom-objects). @@ -415,7 +414,7 @@ and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/ \note You do not need a config file when using a network saved in ONNX format. To run the tutorial program `tutorial-dnn-object-detection-live.cpp`, use the following commands: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=none \ MODEL=${DNN_PATH}/yolov5/weights/yolov5n.onnx \ @@ -426,13 +425,13 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --filterThresh -0.25 --scale 0.0039 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_yolov5.json -``` +\endcode If you want to train your own YoloV5 model, please refer to the [official documentation](https://docs.ultralytics.com/yolov5/tutorials/train_custom_data/#13-prepare-dataset-for-yolov5). @@ -444,12 +443,12 @@ in the Pytorch format from [here](https://github.com/WongKinYiu/yolov7/releases/ Then, convert it in ONNX format using the `export.py` script that you can find on the [YoloV7 repo](https://github.com/WongKinYiu/yolov7) with the following arguments: -``` +\code{.sh} $ python3 export.py --weights ../weights/yolov7-tiny.pt --grid --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 --max-wh 640 -``` +\endcode Finally, use the following commands to run the tutorial program: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=none \ MODEL=${DNN_PATH}/yolov7/weights/yolov7-tiny.onnx \ @@ -460,15 +459,15 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --filterThresh -0.25 --scale 0.0039 -``` +\endcode \note You do not need a config file when using a network saved in ONNX format. Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_yolov7.json -``` +\endcode If you want to train your own YoloV7 model, please refer to the [official documentation](https://github.com/WongKinYiu/yolov7#transfer-learning). If your dataset is rather small (only hundreds of pictures), you may want to consider to base your training on @@ -485,7 +484,7 @@ You can find the weights (`yolov8s.onnx`) in ONNX format and the labels (`coco_classes.txt`) [here](https://github.com/lagadic/visp/blob/master/tutorial/detection/dnn/coco_classes.txt). Please use the following commands to run the tutorial program: -``` +\code{.sh} $ DNN_PATH=/path/to/my/dnn/folder \ CONFIG=none \ MODEL=${DNN_PATH}/yolov8/weights/yolov8s.onnx \ @@ -496,18 +495,54 @@ $ DNN_PATH=/path/to/my/dnn/folder \ $ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ --filterThresh -0.25 --scale 0.0039 -``` +\endcode Alternatively, if you have installed the NLOHMANN JSON library and you are using the weights quoted above, you can use the following command line: -``` +\code{.sh} $ ./tutorial-dnn-object-detection-live --input-json ./default_yolov8.json -``` +\endcode \note You do not need a config file when using a network saved in ONNX format. If you want to train your own YoloV8 model, please refer to the [official documentation](https://docs.ultralytics.com/modes/train/). +\subsubsection dnn_supported_yolov11 Yolo v11 + +Please follow the [official documentation](https://docs.ultralytics.com/quickstart/#install-ultralytics) +to install Ultralytics tools in order to be able to train or export a model. The installation using Docker has been tested for +the sake of this tutorial. + +You can get the pre-trained YoloV11 models [here](https://docs.ultralytics.com/models/yolo11/#performance-metrics). For +this tutorial, we tested the [YOLO11s](https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11s.pt) +pre-trained model. + +To export a model stored in Pytorch format into an ONNX format, you can use the Ultralytics tool: +\code{.sh} +$ sudo docker run -it --ipc=host --gpus all ultralytics/ultralytics:latest +root@8efe0fdbe196:/ultralytics# yolo export model=/path/to/yolo11s.pt format=onnx imgsz=640 opset=12 +\endcode + +\note The `opset` option permits to set the version of ONNX to use to export the model. If you use OpenCV 4.10.0 this +option does not seem to be required. + +\note It seems that OpenCV 4.7.0 is not compatible with Yolo v11. To upgrade OpenCV please follow the instructions in +the section \ref dnn_model_upgrade_opencv below. + +Please use the following commands to run the tutorial program: +\code{.sh} +$ DNN_PATH=/path/to/my/dnn/folder \ + CONFIG=none \ + MODEL=${DNN_PATH}/yolov11/weights/yolov11s.onnx \ + LABELS=${DNN_PATH}/yolov11/cfg/coco_classes.txt \ + TYPE=yolov11 \ + FRAMEWORK=onnx \ + WIDTH=640 HEIGHT=640 +$ ./tutorial-dnn-object-detection-live --model $MODEL --labels $LABELS --config $CONFIG --type $TYPE \ + --framework $FRAMEWORK --width $WIDTH --height $HEIGHT --nmsThresh 0.5 --mean 0 0 0 \ + --filterThresh -0.25 --scale 0.0039 +\endcode + \section dnn_model_other Other dnn models \subsection dnn_model_other_zoo OpenCV model zoo @@ -534,11 +569,11 @@ that does not match the one expected by the DNN). \subsection dnn_error_unimplemented YoloV3: transpose weights is not functionNotImplementedError -``` +\code{.sh} terminate called after throwing an instance of 'cv::Exception' what(): OpenCV(4.7.0) error: (-213:The function/feature is not implemented) Transpose the weights (except for convolutional) is not implemented in function 'ReadDarknetFromWeightsStream' -``` +\endcode Following the proposition found [here](https://github.com/opencv/opencv/issues/15502#issuecomment-531755462) to download once again the weights from [here](https://pjreddie.com/media/files/yolov3.weights) permitted to solve this error. @@ -546,16 +581,82 @@ again the weights from [here](https://pjreddie.com/media/files/yolov3.weights) p \subsection dnn_error_nonmaxsuppr YoloV7: can't create NonMaxSuppression layer When using a YoloV7 model exported in `onnx` format, one can face the following error: -``` +\code{.sh} [ERROR:0@0.335] global onnx_importer.cpp:1054 handleNode DNN/ONNX: ERROR during processing node with 5 inputs and 1 outputs: [NonMaxSuppression]onnx_node!/end2end/NonMaxSuppression) from domain='ai.onnx' terminate called after throwing an instance of 'cv::Exception' what(): OpenCV(4.7.0) opencv/modules/dnn/src/onnx/onnx_importer.cpp:1073: error: (-2:Unspecified error) in function 'handleNode' Node [NonMaxSuppression@ai.onnx]onnx_node!/end2end/NonMaxSuppression) parse error: OpenCV(4.7.0) opencv/modules/dnn/src/net_impl.hpp:108: error: (-2:Unspecified error) Can't create layer "onnx_node!/end2end/NonMaxSuppression" of type "NonMaxSuppression" in function 'getLayerInstance' Aborted (core dumped) -``` +\endcode You may have been missing the onnxsim library or forgotten to remove the `--end2end` option during the export of the network. +\subsection dnn_error_yolov11 Yolo v11: Known issues + +\subsubsection dnn_model_onnx_mismatch ONNX version mismatch + +You may face the following error: +\code{.sh} +what(): OpenCV(4.7.0) /root/3rdparty/opencv/modules/dnn/src/onnx/onnx_importer.cpp:1073: error: (-2:Unspecified error) in function 'handleNode' +> Node [Split@ai.onnx]:(onnx_node!/model.10/m/m.0/attn/Split) parse error: OpenCV(4.7.0) /root/3rdparty/opencv/modules/dnn/src/layers/slice_layer.cpp:274: error: (-215:Assertion failed) splits > 0 && inpShape[axis_rw] % splits == 0 in function 'getMemoryShapes' +\endcode +It is because the version of ONNX used to export the model does not match the one that OpenCV uses. +Please be sure that you used the `opset` option in the export command as stated in \ref dnn_supported_yolov11 section, such as follow: +\code{.sh} +$ yolo export model=/path/to/yolo11s.pt format=onnx imgsz=640 opset=12 +\endcode +\note The `opset` option does not seem to be needed with OpenCV 4.10.0 . + +\subsubsection dnn_model_opencv_deprecated OpenCV version too old + +You may also face the following error when trying to run the tutorial with a Yolo v11 model: +\code{.sh} +terminate called after throwing an instance of 'cv::Exception' + what(): OpenCV(4.7.0) /root/3rdparty/opencv/modules/dnn/src/net_impl_fuse.cpp:252: error: (-215:Assertion failed) biasLayerData->outputBlobs.size() == 1 in function 'fuseLayers' +\endcode +It is because the OpenCV version that you use is too old. Please update OpenCV following the instructions presented in +the \ref dnn_model_upgrade_opencv below. + +\subsubsection dnn_model_upgrade_opencv Fix by upgrading OpenCV from source + +We suppose that OpenCV has been installed from source as described in the section \ref build_opencv_with_cuda +above. + +To upgrade OpenCV to version 4.10.0, please follow the steps below: + +\code{.sh} +$ cd $VISP_WS/3rdparty/opencv +$ git fetch +$ git checkout 4.10.0 +$ cd build +$ cmake .. \ + -DCMAKE_BUILD_TYPE=RELEASE \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DWITH_CUDA=ON \ + -DWITH_CUDNN=ON \ + -DOPENCV_DNN_CUDA=ON \ + -DENABLE_FAST_MATH=1 \ + -DCUDA_FAST_MATH=1 \ + -DCUDA_ARCH_BIN=${GPU_CAPABILITIES} \ + -DWITH_CUBLAS=1 \ + -DOPENCV_EXTRA_MODULES_PATH=${HOME}/visp_ws/3rdparty/opencv_contrib/modules \ + -DBUILD_PERF_TESTS=Off \ + -DBUILD_TESTS=Off \ + -DBUILD_EXAMPLES=Off \ + -DBUILD_opencv_apps=Off \ + -DBUILD_opencv_java_bindings_generator=Off \ + -DBUILD_opencv_js=Off +$ make -j$(nproc) +$ sudo make install +\endcode +Once OpenCV is build and installed, you need to rebuild ViSP: +\code{.sh} +$ cd $VISP_WS/visp-build +$ cmake ../visp +$ make -j$(nproc) +\endcode + \section dnn_next Next tutorial You may continue following \ref tutorial-detection-tensorrt. diff --git a/example/tracking/trackMeCircle.cpp b/example/tracking/trackMeCircle.cpp index 3beca9c4f2..8050802c87 100644 --- a/example/tracking/trackMeCircle.cpp +++ b/example/tracking/trackMeCircle.cpp @@ -56,13 +56,12 @@ #include #include +#include #include #include #include #include #include - -#include #include #include diff --git a/example/tracking/trackMeEllipse.cpp b/example/tracking/trackMeEllipse.cpp index 36f25fded1..14c2f752b0 100644 --- a/example/tracking/trackMeEllipse.cpp +++ b/example/tracking/trackMeEllipse.cpp @@ -1,7 +1,6 @@ -/**************************************************************************** - * +/* * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * Copyright (C) 2005 - 2024 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +29,7 @@ * * Description: * Tracking of an ellipse. - * -*****************************************************************************/ + */ /*! \file trackMeEllipse.cpp @@ -316,8 +314,8 @@ int main(int argc, const char **argv) // it size is not defined yet, it will be defined when the image is // read on the disk vpImage I; - vpDisplay *display = nullptr; + vpVideoReader g; try { // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH @@ -336,8 +334,9 @@ int main(int argc, const char **argv) } // Get the option values - if (!opt_ipath.empty()) + if (!opt_ipath.empty()) { ipath = opt_ipath; + } // Compare ipath and env_ipath. If they differ, we take into account // the input path coming from the command line option @@ -374,7 +373,6 @@ int main(int argc, const char **argv) thickness += 1; } - vpVideoReader g; if (opt_ppath.empty()) { // Set the path location of the image sequence #if VISP_HAVE_DATASET_VERSION >= 0x030600 @@ -398,7 +396,7 @@ int main(int argc, const char **argv) g.open(I); if (opt_display) { - // We open a window using either X11, GTK or GDI. + // We open a window using either X11, GTK, GDI or OpenCV #if defined(VISP_HAVE_X11) display = new vpDisplayX; #elif defined(VISP_HAVE_GTK) diff --git a/example/tracking/trackMeLine.cpp b/example/tracking/trackMeLine.cpp index 5bc03dda0d..8fb9be47c2 100644 --- a/example/tracking/trackMeLine.cpp +++ b/example/tracking/trackMeLine.cpp @@ -1,7 +1,6 @@ -/**************************************************************************** - * +/* * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * Copyright (C) 2005 - 2024 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +29,7 @@ * * Description: * Tracking of a line. - * -*****************************************************************************/ + */ /*! \file trackMeLine.cpp @@ -59,20 +57,18 @@ #include #include #include +#include #include #include #include #include #include - +#include +#include #include - #include #include -#include -#include - // List of allowed command line options #define GETOPTARGS "cdf:hi:l:p:s:" @@ -222,25 +218,23 @@ bool getOptions(int argc, const char **argv, std::string &ipath, std::string &pp int main(int argc, const char **argv) { #if defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV) - try { - std::string env_ipath; - std::string opt_ipath; - std::string ipath; - std::string opt_ppath; - std::string dirname; - std::string filename; - unsigned int opt_first = 1; - unsigned int opt_last = 30; - unsigned int opt_step = 1; - bool opt_click_allowed = true; - bool opt_display = true; - -#if VISP_HAVE_DATASET_VERSION >= 0x030600 - std::string ext("png"); -#else - std::string ext("pgm"); -#endif + std::string env_ipath; + std::string opt_ipath; + std::string ipath; + std::string opt_ppath; + std::string videoname; + unsigned int opt_first = 1; + unsigned int opt_last = 30; + unsigned int opt_step = 1; + bool opt_click_allowed = true; + bool opt_display = true; + unsigned int thickness = 1; + + vpImage I; + vpDisplay *display = nullptr; + vpVideoReader g; + try { // Get the visp-images-data package path or VISP_INPUT_IMAGE_PATH // environment variable value env_ipath = vpIoTools::getViSPImagesDataPath(); @@ -256,8 +250,9 @@ int main(int argc, const char **argv) } // Get the option values - if (!opt_ipath.empty()) + if (!opt_ipath.empty()) { ipath = opt_ipath; + } // Compare ipath and env_ipath. If they differ, we take into account // the input path coming from the command line option @@ -284,76 +279,42 @@ int main(int argc, const char **argv) return EXIT_FAILURE; } - // Declare an image, this is a gray level image (unsigned char) - // it size is not defined yet, it will be defined when the image will - // read on the disk - vpImage I; - - unsigned iter = opt_first; - std::ostringstream s; - char cfilename[FILENAME_MAX]; - + vpVideoReader g; if (opt_ppath.empty()) { - - // Warning : - // The image sequence is not provided with the ViSP package - // therefore the program will return an error : - // !! couldn't read file visp-images/mire-2/image.0001.png - // - // ViSP dataset is available on the visp www site - // https://visp.inria.fr/download/. - - // Set the path location of the image sequence - dirname = vpIoTools::createFilePath(ipath, "line"); - - // Build the name of the image file - s.setf(std::ios::right, std::ios::adjustfield); - s << "image." << std::setw(4) << std::setfill('0') << iter << "." << ext; - filename = vpIoTools::createFilePath(dirname, s.str()); + // Set the path location of the image sequence +#if VISP_HAVE_DATASET_VERSION >= 0x030600 + videoname = vpIoTools::createFilePath(ipath, "line/image.%04d.png"); +#else + videoname = vpIoTools::createFilePath(ipath, "line/image.%04d.pgm"); +#endif + g.setFileName(videoname); } else { - snprintf(cfilename, FILENAME_MAX, opt_ppath.c_str(), iter); - filename = cfilename; + g.setFileName(opt_ppath); } - // Read the image named "filename", and put the bitmap into the image structure I. - // I is initialized to the correct size - // - // vpImageIo::read() may throw various exception if, for example, - // the file does not exist, or if the memory cannot be allocated - try { - vpCTRACE << "Load: " << filename << std::endl; - - vpImageIo::read(I, filename); + if (opt_first > 0) { + g.setFirstFrameIndex(opt_first); } - catch (...) { - // If an exception is thrown by vpImageIo::read() it will result in the end of the program. - std::cerr << std::endl << "ERROR:" << std::endl; - std::cerr << " Cannot read " << filename << std::endl; - if (opt_ppath.empty()) { - std::cerr << " Check your -i " << ipath << " option " << std::endl - << " or VISP_INPUT_IMAGE_PATH environment variable." << std::endl; - } - else { - std::cerr << " Check your -p " << opt_ppath << " option " << std::endl; - } - return EXIT_FAILURE; + if (opt_last > 0) { + g.setLastFrameIndex(opt_last); } + g.setFrameStep(opt_step); + g.open(I); - // We open a window using either X11, GTK or GDI. + if (opt_display) { + // We open a window using either X11, GTK, GDI or OpenCV #if defined(VISP_HAVE_X11) - vpDisplayX display; + display = new vpDisplayX; #elif defined(VISP_HAVE_GTK) - vpDisplayGTK display; + display = new vpDisplayGTK; #elif defined(VISP_HAVE_GDI) - vpDisplayGDI display; + display = new vpDisplayGDI; #elif defined(HAVE_OPENCV_HIGHGUI) - vpDisplayOpenCV display; + display = new vpDisplayOpenCV; #endif - - if (opt_display) { // Display size is automatically defined by the image (I) size - display.init(I, 100, 100, "Display..."); + display->init(I, 10, 10, "Current image"); // Display the image // The image class has a member that specify a pointer toward // the display that has been initialized in the display declaration @@ -363,7 +324,7 @@ int main(int argc, const char **argv) vpDisplay::flush(I); } - vpMeLine L1; + vpMeLine me_line; vpMe me; me.setRange(15); @@ -371,24 +332,38 @@ int main(int argc, const char **argv) me.setLikelihoodThresholdType(vpMe::NORMALIZED_THRESHOLD); me.setThreshold(20); - L1.setMe(&me); - L1.setDisplay(vpMeSite::RANGE_RESULT); + me_line.setMe(&me); + me_line.setDisplay(vpMeSite::RANGE_RESULT); + + std::cout << "Video settings" << std::endl; + std::cout << " Name : " << g.getFrameName() << std::endl; + std::cout << " First image: " << g.getFirstFrameIndex() << std::endl; + std::cout << " Last image : " << g.getLastFrameIndex() << std::endl; + std::cout << " Step : " << g.getFrameStep() << std::endl; + std::cout << " Image size : " << I.getWidth() << " x " << I.getHeight() << std::endl; + + std::cout << "Moving-edges settings" << std::endl; + std::cout << " Sample step : " << me_line.getMe()->getSampleStep() << std::endl; + std::cout << " Range : " << me_line.getMe()->getRange() << std::endl; + std::cout << " Threshold type: " << (me_line.getMe()->getLikelihoodThresholdType() == vpMe::NORMALIZED_THRESHOLD ? "normalized" : "old threshold (to be avoided)") << std::endl; + std::cout << " Threshold : " << me_line.getMe()->getThreshold() << std::endl; if (opt_display && opt_click_allowed) - L1.initTracking(I); + me_line.initTracking(I); else { vpImagePoint ip1, ip2; ip1.set_i(96); ip1.set_j(191); ip2.set_i(122); ip2.set_j(211); - L1.initTracking(I, ip1, ip2); + me_line.initTracking(I, ip1, ip2); } - if (opt_display) - L1.display(I, vpColor::green); + if (opt_display) { + me_line.display(I, vpColor::green); + } - L1.track(I); + me_line.track(I); if (opt_display && opt_click_allowed) { std::cout << "A click to continue..." << std::endl; vpDisplay::getClick(I); @@ -398,42 +373,41 @@ int main(int argc, const char **argv) vpFeatureLine l; vpCameraParameters cam; - vpImage Ic; - while (iter < opt_last) { - std::cout << "----------------------------------------------------------" << std::endl; - // set the new image name - s.str(""); - s << "image." << std::setw(4) << std::setfill('0') << iter << "." << ext; - filename = vpIoTools::createFilePath(dirname, s.str()); - // read the image - vpImageIo::read(I, filename); + + bool quit = false; + while (!g.end() && !quit) { + g.acquire(I); + std::cout << "Process image " << g.getFrameIndex() << std::endl; if (opt_display) { // Display the image vpDisplay::display(I); + if (opt_click_allowed) { + vpDisplay::displayText(I, 40, 10, "Click to exit...", vpColor::red); + } } - std::cout << "Tracking on image: " << filename << std::endl; - L1.track(I); + me_line.track(I); - vpTRACE("L1 : %f %f", L1.getRho(), vpMath::deg(L1.getTheta())); - vpFeatureBuilder::create(l, cam, L1); - vpTRACE("L1 : %f %f", l.getRho(), vpMath::deg(l.getTheta())); + vpTRACE("me_line : %f %f", me_line.getRho(), vpMath::deg(me_line.getTheta())); + vpFeatureBuilder::create(l, cam, me_line); + vpTRACE("me_line : %f %f", l.getRho(), vpMath::deg(l.getTheta())); if (opt_display) { - L1.display(I, vpColor::green); + me_line.display(I, vpColor::green, thickness); vpDisplay::flush(I); - if (opt_click_allowed) { - std::cout << "A click to continue..." << std::endl; - vpDisplay::getClick(I); + } + if (opt_display && opt_click_allowed) { + if (vpDisplay::getClick(I, false)) { + quit = true; } } - - iter += opt_step; } - if (opt_display && opt_click_allowed) { - std::cout << "A click to exit..." << std::endl; + if (opt_display && opt_click_allowed && !quit) { vpDisplay::getClick(I); } + if (display) { + delete display; + } return EXIT_SUCCESS; } catch (const vpException &e) { diff --git a/modules/core/src/image/vpImageConvert_opencv.cpp b/modules/core/src/image/vpImageConvert_opencv.cpp index 12b7eda56f..b3b380674e 100644 --- a/modules/core/src/image/vpImageConvert_opencv.cpp +++ b/modules/core/src/image/vpImageConvert_opencv.cpp @@ -283,9 +283,9 @@ void vpImageConvert::convert(const cv::Mat &src, vpImage &dest, b } /*! - * Converts cv::Mat CV_32FC1 format to ViSP vpImage. + * Converts cv::Mat CV_32FC1 / CV_16SC1 format to ViSP vpImage. * - * \param[in] src : OpenCV image in CV_32FC1 format. + * \param[in] src : OpenCV image in CV_32FC1 / CV_16SC1 format. * \param[out] dest : ViSP image in float format. * \param[in] flip : When true during conversion flip image vertically. */ @@ -296,7 +296,7 @@ void vpImageConvert::convert(const cv::Mat &src, vpImage &dest, bool flip unsigned int destCols = dest.getCols(); if (src.type() == CV_32FC1) { - for (unsigned int i = 0; i < destRows; ++i) + for (unsigned int i = 0; i < destRows; ++i) { for (unsigned int j = 0; j < destCols; ++j) { if (flip) { dest[dest.getRows() - i - 1][j] = src.at(static_cast(i), static_cast(j)); @@ -305,6 +305,19 @@ void vpImageConvert::convert(const cv::Mat &src, vpImage &dest, bool flip dest[i][j] = src.at(static_cast(i), static_cast(j)); } } + } + } + else if (src.type() == CV_16SC1) { + for (unsigned int i = 0; i < destRows; ++i) { + for (unsigned int j = 0; j < destCols; ++j) { + if (flip) { + dest[dest.getRows() - i - 1][j] = src.at(static_cast(i), static_cast(j)); + } + else { + dest[i][j] = src.at(static_cast(i), static_cast(j)); + } + } + } } else { throw vpException(vpException::badValue, "cv::Mat type is not supported!"); diff --git a/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h b/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h index 9c0e0e362f..f39a773a9e 100644 --- a/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h +++ b/modules/detection/include/visp3/detection/vpDetectorDNNOpenCV.h @@ -1,6 +1,6 @@ /* * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * Copyright (C) 2005 - 2024 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +30,9 @@ * Description: * DNN object detection using OpenCV DNN module. */ -#ifndef _vpDetectorDNN_h_ -#define _vpDetectorDNN_h_ + +#ifndef VP_DETECTOR_DNN_OPENCV_H +#define VP_DETECTOR_DNN_OPENCV_H #include @@ -74,6 +75,7 @@ BEGIN_VISP_NAMESPACE * - Yolo v5, see usage to detect objects belonging to the COCO dataset using \ref dnn_supported_yolov5 network * - Yolo v7, see usage to detect objects belonging to the COCO dataset using \ref dnn_supported_yolov7 network * - Yolo v8, see usage to detect objects belonging to the COCO dataset using \ref dnn_supported_yolov8 network + * - Yolo v11, see usage to detect objects belonging to the COCO dataset using \ref dnn_supported_yolov11 network * * This class can be initialized from a JSON file if ViSP has been compiled with NLOHMANN JSON (see \ref soft_tool_json to see how to do it). * Examples of such JSON files can be found in the tutorial folder. @@ -98,8 +100,9 @@ class VISP_EXPORT vpDetectorDNNOpenCV YOLO_V4 = 5, /*!< The \b vpDetectorDNNOpenCV object will use the parsing method corresponding to a YoloV4 DNN. See \b vpDetectorDNNOpenCV::postProcess_YoloV3_V4 for more information.*/ YOLO_V5 = 6, /*!< The \b vpDetectorDNNOpenCV object will use the parsing method corresponding to a YoloV5 DNN. See \b vpDetectorDNNOpenCV::postProcess_YoloV5_V7 for more information.*/ YOLO_V7 = 7, /*!< The \b vpDetectorDNNOpenCV object will use the parsing method corresponding to a YoloV7 DNN. See \b vpDetectorDNNOpenCV::postProcess_YoloV5_V7 for more information.*/ - YOLO_V8 = 8, /*!< The \b vpDetectorDNNOpenCV object will use the parsing method corresponding to a YoloV8 DNN. See \b vpDetectorDNNOpenCV::postProcess_YoloV8 for more information.*/ - COUNT = 9 /*!< The number of parsing method that come along with the \b vpDetectorDNNOpenCV class.*/ + YOLO_V8 = 8, /*!< The \b vpDetectorDNNOpenCV object will use the parsing method corresponding to a YoloV8 DNN. See \b vpDetectorDNNOpenCV::postProcess_YoloV8_V11 for more information.*/ + YOLO_V11 = 9, /*!< The \b vpDetectorDNNOpenCV object will use the parsing method corresponding to a YoloV11 DNN. See \b vpDetectorDNNOpenCV::postProcess_YoloV8_V11 for more information.*/ + COUNT = 10 /*!< The number of parsing method that come along with the \b vpDetectorDNNOpenCV class.*/ } DNNResultsParsingType; typedef struct DetectionCandidates @@ -560,7 +563,7 @@ class VISP_EXPORT vpDetectorDNNOpenCV void postProcess_YoloV5_V7(DetectionCandidates &proposals, std::vector &dnnRes, const NetConfig &netConfig); - void postProcess_YoloV8(DetectionCandidates &proposals, std::vector &dnnRes, const NetConfig &netConfig); + void postProcess_YoloV8_V11(DetectionCandidates &proposals, std::vector &dnnRes, const NetConfig &netConfig); void postProcess_FasterRCNN(DetectionCandidates &proposals, std::vector &dnnRes, const NetConfig &netConfig); diff --git a/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp b/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp index d3846019e1..39b6bdbb35 100644 --- a/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp +++ b/modules/detection/src/dnn/vpDetectorDNNOpenCV.cpp @@ -1,7 +1,6 @@ -/**************************************************************************** - * +/* * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2023 by Inria. All rights reserved. + * Copyright (C) 2005 - 2024 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +29,8 @@ * * Description: * DNN object detection using OpenCV DNN module. - * -*****************************************************************************/ + */ + #include // Check if std:c++17 or higher @@ -50,7 +49,7 @@ BEGIN_VISP_NAMESPACE * * \return std::string The list of the supported parsing methods / types of DNNs. */ -std::string vpDetectorDNNOpenCV::getAvailableDnnResultsParsingTypes() + std::string vpDetectorDNNOpenCV::getAvailableDnnResultsParsingTypes() { std::string list = "["; for (unsigned int i = 0; i < vpDetectorDNNOpenCV::COUNT - 1; i++) { @@ -88,6 +87,9 @@ std::string vpDetectorDNNOpenCV::dnnResultsParsingTypeToString(const DNNResultsP case YOLO_V8: name = "yolov8"; break; + case YOLO_V11: + name = "yolov11"; + break; case FASTER_RCNN: name = "faster-rcnn"; break; @@ -499,7 +501,8 @@ void vpDetectorDNNOpenCV::postProcess(DetectionCandidates &proposals) postProcess_YoloV5_V7(proposals, m_dnnRes, m_netConfig); break; case YOLO_V8: - postProcess_YoloV8(proposals, m_dnnRes, m_netConfig); + case YOLO_V11: + postProcess_YoloV8_V11(proposals, m_dnnRes, m_netConfig); break; case FASTER_RCNN: postProcess_FasterRCNN(proposals, m_dnnRes, m_netConfig); @@ -815,7 +818,7 @@ void vpDetectorDNNOpenCV::postProcess_YoloV5_V7(DetectionCandidates &proposals, \param dnnRes: raw results of the \b vpDetectorDNNOpenCV::detect step. \param netConfig: the configuration of the network, to know for instance the DNN input size. */ -void vpDetectorDNNOpenCV::postProcess_YoloV8(DetectionCandidates &proposals, std::vector &dnnRes, const NetConfig &netConfig) +void vpDetectorDNNOpenCV::postProcess_YoloV8_V11(DetectionCandidates &proposals, std::vector &dnnRes, const NetConfig &netConfig) { // Code adapted from here: https://github.com/JustasBart/yolov8_CPP_Inference_OpenCV_ONNX/blob/minimalistic/inference.cpp // Compute the ratio between the original size of the image and the network size to translate network coordinates into @@ -1146,7 +1149,7 @@ void vpDetectorDNNOpenCV::setPreferableTarget(const int &targetId) { m_net.setPr void vpDetectorDNNOpenCV::setScaleFactor(const double &scaleFactor) { m_netConfig.m_scaleFactor = scaleFactor; - if ((m_netConfig.m_parsingMethodType == YOLO_V7 || m_netConfig.m_parsingMethodType == YOLO_V8) && m_netConfig.m_scaleFactor != 1 / 255.) { + if ((m_netConfig.m_parsingMethodType == YOLO_V7 || m_netConfig.m_parsingMethodType == YOLO_V8 || m_netConfig.m_parsingMethodType == YOLO_V11) && m_netConfig.m_scaleFactor != 1 / 255.) { std::cout << "[vpDetectorDNNOpenCV::setParsingMethod] WARNING: scale factor should be 1/255. to normalize pixels value." << std::endl; } } @@ -1169,7 +1172,7 @@ void vpDetectorDNNOpenCV::setParsingMethod(const DNNResultsParsingType &typePars { m_netConfig.m_parsingMethodType = typeParsingMethod; m_parsingMethod = parsingMethod; - if ((m_netConfig.m_parsingMethodType == YOLO_V7 || m_netConfig.m_parsingMethodType == YOLO_V8) && m_netConfig.m_scaleFactor != 1 / 255.) { + if ((m_netConfig.m_parsingMethodType == YOLO_V7 || m_netConfig.m_parsingMethodType == YOLO_V8 || m_netConfig.m_parsingMethodType == YOLO_V11) && m_netConfig.m_scaleFactor != 1 / 255.) { m_netConfig.m_scaleFactor = 1 / 255.; std::cout << "[vpDetectorDNNOpenCV::setParsingMethod] NB: scale factor changed to 1/255. to normalize pixels value." << std::endl; } diff --git a/modules/tracker/mbt/src/edge/vpMbtMeLine.cpp b/modules/tracker/mbt/src/edge/vpMbtMeLine.cpp index a433359e48..bf99e47adf 100644 --- a/modules/tracker/mbt/src/edge/vpMbtMeLine.cpp +++ b/modules/tracker/mbt/src/edge/vpMbtMeLine.cpp @@ -171,29 +171,30 @@ void vpMbtMeLine::sample(const vpImage &I, bool doNoTrack) vpImagePoint iP; iP.set_i(is); iP.set_j(js); - unsigned int is_uint = static_cast(is); - unsigned int js_uint = static_cast(js); // If point is in the image, add to the sample list - if ((!outOfImage(iP, (int)(m_me->getRange() + m_me->getMaskSize() + 1), nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint) - && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { - vpMeSite pix; - pix.init(iP.get_i(), iP.get_j(), m_delta, 0, m_sign); - pix.setDisplay(m_selectDisplay); - pix.setState(vpMeSite::NO_SUPPRESSION); - const double marginRatio = m_me->getThresholdMarginRatio(); - double convolution = pix.convolution(I, m_me); - double contrastThreshold = fabs(convolution) * marginRatio; - pix.setContrastThreshold(contrastThreshold, *m_me); - - if (!doNoTrack) { - pix.track(I, m_me, false); - } + if (!outOfImage(iP, (int)(m_me->getRange() + m_me->getMaskSize() + 1), nbrows, nbcols)) { + unsigned int is_uint = static_cast(is); + unsigned int js_uint = static_cast(js); + if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { + vpMeSite pix; + pix.init(iP.get_i(), iP.get_j(), m_delta, 0, m_sign); + pix.setDisplay(m_selectDisplay); + pix.setState(vpMeSite::NO_SUPPRESSION); + const double marginRatio = m_me->getThresholdMarginRatio(); + double convolution = pix.convolution(I, m_me); + double contrastThreshold = fabs(convolution) * marginRatio; + pix.setContrastThreshold(contrastThreshold, *m_me); + + if (!doNoTrack) { + pix.track(I, m_me, false); + } - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, iP, 2, vpColor::blue); - } + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, iP, 2, vpColor::blue); + } - m_meList.push_back(pix); + m_meList.push_back(pix); + } } is += stepi; js += stepj; diff --git a/modules/tracker/me/src/moving-edges/vpMeEllipse.cpp b/modules/tracker/me/src/moving-edges/vpMeEllipse.cpp index fffd6e8f13..dc143cadcb 100644 --- a/modules/tracker/me/src/moving-edges/vpMeEllipse.cpp +++ b/modules/tracker/me/src/moving-edges/vpMeEllipse.cpp @@ -311,25 +311,26 @@ void vpMeEllipse::sample(const vpImage &I, bool doNotTrack) computePointOnEllipse(ang, iP); // If point is in the image, add to the sample list // Check done in (i,j) frame) - unsigned int is_uint = static_cast(iP.get_i()); - unsigned int js_uint = static_cast(iP.get_j()); - if ((!outOfImage(iP, 0, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint) - && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { - const unsigned int crossSize = 5; - vpDisplay::displayCross(I, iP, crossSize, vpColor::red); - - double theta = computeTheta(iP); - vpMeSite pix; - // (i,j) frame used for vpMeSite - pix.init(iP.get_i(), iP.get_j(), theta); - pix.setDisplay(m_selectDisplay); - pix.setState(vpMeSite::NO_SUPPRESSION); - const double marginRatio = m_me->getThresholdMarginRatio(); - double convolution = pix.convolution(I, m_me); - double contrastThreshold = fabs(convolution) * marginRatio; - pix.setContrastThreshold(contrastThreshold, *m_me); - m_meList.push_back(pix); - m_angleList.push_back(ang); + if (!outOfImage(iP, 0, nbrows, nbcols)) { + unsigned int is_uint = static_cast(iP.get_i()); + unsigned int js_uint = static_cast(iP.get_j()); + if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { + const unsigned int crossSize = 5; + vpDisplay::displayCross(I, iP, crossSize, vpColor::red); + + double theta = computeTheta(iP); + vpMeSite pix; + // (i,j) frame used for vpMeSite + pix.init(iP.get_i(), iP.get_j(), theta); + pix.setDisplay(m_selectDisplay); + pix.setState(vpMeSite::NO_SUPPRESSION); + const double marginRatio = m_me->getThresholdMarginRatio(); + double convolution = pix.convolution(I, m_me); + double contrastThreshold = fabs(convolution) * marginRatio; + pix.setContrastThreshold(contrastThreshold, *m_me); + m_meList.push_back(pix); + m_angleList.push_back(ang); + } } ang += incr; } @@ -375,9 +376,63 @@ unsigned int vpMeEllipse::plugHoles(const vpImage &I) while (ang < (nextang - incr)) { vpImagePoint iP; computePointOnEllipse(ang, iP); + if (!outOfImage(iP, 0, nbrows, nbcols)) { + unsigned int is_uint = static_cast(iP.get_i()); + unsigned int js_uint = static_cast(iP.get_j()); + if (inRoiMask(m_mask, is_uint, js_uint)) { + double theta = computeTheta(iP); + vpMeSite pix; + pix.init(iP.get_i(), iP.get_j(), theta); + pix.setDisplay(m_selectDisplay); + pix.setState(vpMeSite::NO_SUPPRESSION); + double convolution = pix.convolution(I, m_me); + double contrastThreshold = fabs(convolution) * marginRatio; + pix.setContrastThreshold(contrastThreshold, *m_me); + pix.track(I, m_me, false); + if (pix.getState() == vpMeSite::NO_SUPPRESSION) { // good point + ++nb_pts_added; + iP.set_ij(pix.get_ifloat(), pix.get_jfloat()); + double new_ang = computeAngleOnEllipse(iP); + if ((new_ang - ang) > M_PI) { + new_ang -= 2.0 * M_PI; + } + else if ((ang - new_ang) > M_PI) { + new_ang += 2.0 * M_PI; + } + m_meList.insert(meList, pix); + m_angleList.insert(angleList, new_ang); + } + } + } + ang += incr; + } + } + ang = nextang; + ++angleList; + ++meList; + } + + // Add points in case two neighboring points are too far away + angleList = m_angleList.begin(); + ang = *angleList; + meList = m_meList.begin(); + vpMeSite pix1 = *meList; + ++angleList; + ++meList; + while (meList != m_meList.end()) { + double nextang = *angleList; + vpMeSite pix2 = *meList; + double dist = sqrt(((pix1.get_ifloat() - pix2.get_ifloat()) * (pix1.get_ifloat() - pix2.get_ifloat())) + + ((pix1.get_jfloat() - pix2.get_jfloat()) * (pix1.get_jfloat() - pix2.get_jfloat()))); + // Only one point is added if two neighboring points are too far away + if (dist > (2.0 * m_me->getSampleStep())) { + ang = (nextang + ang) / 2.0; // point added at mid angle + vpImagePoint iP; + computePointOnEllipse(ang, iP); + if (!outOfImage(iP, 0, nbrows, nbcols)) { unsigned int is_uint = static_cast(iP.get_i()); unsigned int js_uint = static_cast(iP.get_j()); - if ((!outOfImage(iP, 0, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)) { + if (inRoiMask(m_mask, is_uint, js_uint)) { double theta = computeTheta(iP); vpMeSite pix; pix.init(iP.get_i(), iP.get_j(), theta); @@ -401,44 +456,41 @@ unsigned int vpMeEllipse::plugHoles(const vpImage &I) m_angleList.insert(angleList, new_ang); } } - ang += incr; } } ang = nextang; + pix1 = pix2; ++angleList; ++meList; } - // Add points in case two neighboring points are too far away - angleList = m_angleList.begin(); - ang = *angleList; + // Try to fill the first extremity: from alpha_min - incr to alpha1 + incr/2 meList = m_meList.begin(); - vpMeSite pix1 = *meList; - ++angleList; - ++meList; - while (meList != m_meList.end()) { - double nextang = *angleList; - vpMeSite pix2 = *meList; - double dist = sqrt(((pix1.get_ifloat() - pix2.get_ifloat()) * (pix1.get_ifloat() - pix2.get_ifloat())) - + ((pix1.get_jfloat() - pix2.get_jfloat()) * (pix1.get_jfloat() - pix2.get_jfloat()))); - // Only one point is added if two neighboring points are too far away - if (dist > (2.0 * m_me->getSampleStep())) { - ang = (nextang + ang) / 2.0; // point added at mid angle - vpImagePoint iP; - computePointOnEllipse(ang, iP); + pix1 = *meList; + unsigned int nbpts = 0; + // Add - incr/2.0 to avoid being too close to 0 + if ((m_alphamin - m_alpha1 - (incr / 2.0)) > 0.0) { + nbpts = static_cast(floor((m_alphamin - m_alpha1 - (incr / 2.0)) / incr)); + } + ang = m_alphamin - incr; + for (unsigned int i = 0; i < nbpts; ++i) { + vpImagePoint iP; + computePointOnEllipse(ang, iP); + if (!outOfImage(iP, 0, nbrows, nbcols)) { unsigned int is_uint = static_cast(iP.get_i()); unsigned int js_uint = static_cast(iP.get_j()); - if ((!outOfImage(iP, 0, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)) { + if (inRoiMask(m_mask, is_uint, js_uint)) { double theta = computeTheta(iP); vpMeSite pix; pix.init(iP.get_i(), iP.get_j(), theta); pix.setDisplay(m_selectDisplay); pix.setState(vpMeSite::NO_SUPPRESSION); + // --comment: pix dot setContrastThreshold of pix1 dot getContrastThreshold() comma *m_me double convolution = pix.convolution(I, m_me); double contrastThreshold = fabs(convolution) * marginRatio; pix.setContrastThreshold(contrastThreshold, *m_me); pix.track(I, m_me, false); - if (pix.getState() == vpMeSite::NO_SUPPRESSION) { // good point + if (pix.getState() == vpMeSite::NO_SUPPRESSION) { ++nb_pts_added; iP.set_ij(pix.get_ifloat(), pix.get_jfloat()); double new_ang = computeAngleOnEllipse(iP); @@ -448,56 +500,11 @@ unsigned int vpMeEllipse::plugHoles(const vpImage &I) else if ((ang - new_ang) > M_PI) { new_ang += 2.0 * M_PI; } - m_meList.insert(meList, pix); - m_angleList.insert(angleList, new_ang); + m_meList.push_front(pix); + m_angleList.push_front(new_ang); } } } - ang = nextang; - pix1 = pix2; - ++angleList; - ++meList; - } - - // Try to fill the first extremity: from alpha_min - incr to alpha1 + incr/2 - meList = m_meList.begin(); - pix1 = *meList; - unsigned int nbpts = 0; - // Add - incr/2.0 to avoid being too close to 0 - if ((m_alphamin - m_alpha1 - (incr / 2.0)) > 0.0) { - nbpts = static_cast(floor((m_alphamin - m_alpha1 - (incr / 2.0)) / incr)); - } - ang = m_alphamin - incr; - for (unsigned int i = 0; i < nbpts; ++i) { - vpImagePoint iP; - computePointOnEllipse(ang, iP); - unsigned int is_uint = static_cast(iP.get_i()); - unsigned int js_uint = static_cast(iP.get_j()); - if ((!outOfImage(iP, 0, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)) { - double theta = computeTheta(iP); - vpMeSite pix; - pix.init(iP.get_i(), iP.get_j(), theta); - pix.setDisplay(m_selectDisplay); - pix.setState(vpMeSite::NO_SUPPRESSION); - // --comment: pix dot setContrastThreshold of pix1 dot getContrastThreshold() comma *m_me - double convolution = pix.convolution(I, m_me); - double contrastThreshold = fabs(convolution) * marginRatio; - pix.setContrastThreshold(contrastThreshold, *m_me); - pix.track(I, m_me, false); - if (pix.getState() == vpMeSite::NO_SUPPRESSION) { - ++nb_pts_added; - iP.set_ij(pix.get_ifloat(), pix.get_jfloat()); - double new_ang = computeAngleOnEllipse(iP); - if ((new_ang - ang) > M_PI) { - new_ang -= 2.0 * M_PI; - } - else if ((ang - new_ang) > M_PI) { - new_ang += 2.0 * M_PI; - } - m_meList.push_front(pix); - m_angleList.push_front(new_ang); - } - } ang -= incr; } @@ -512,30 +519,32 @@ unsigned int vpMeEllipse::plugHoles(const vpImage &I) for (unsigned int i = 0; i < nbpts; ++i) { vpImagePoint iP; computePointOnEllipse(ang, iP); - unsigned int is_uint = static_cast(iP.get_i()); - unsigned int js_uint = static_cast(iP.get_j()); - if ((!outOfImage(iP, 0, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)) { - double theta = computeTheta(iP); - vpMeSite pix; - pix.init(iP.get_i(), iP.get_j(), theta); - pix.setDisplay(m_selectDisplay); - pix.setState(vpMeSite::NO_SUPPRESSION); - double convolution = pix.convolution(I, m_me); - double contrastThreshold = fabs(convolution) * marginRatio; - pix.setContrastThreshold(contrastThreshold, *m_me); - pix.track(I, m_me, false); - if (pix.getState() == vpMeSite::NO_SUPPRESSION) { - ++nb_pts_added; - iP.set_ij(pix.get_ifloat(), pix.get_jfloat()); - double new_ang = computeAngleOnEllipse(iP); - if ((new_ang - ang) > M_PI) { - new_ang -= 2.0 * M_PI; - } - else if ((ang - new_ang) > M_PI) { - new_ang += 2.0 * M_PI; + if (!outOfImage(iP, 0, nbrows, nbcols)) { + unsigned int is_uint = static_cast(iP.get_i()); + unsigned int js_uint = static_cast(iP.get_j()); + if (inRoiMask(m_mask, is_uint, js_uint)) { + double theta = computeTheta(iP); + vpMeSite pix; + pix.init(iP.get_i(), iP.get_j(), theta); + pix.setDisplay(m_selectDisplay); + pix.setState(vpMeSite::NO_SUPPRESSION); + double convolution = pix.convolution(I, m_me); + double contrastThreshold = fabs(convolution) * marginRatio; + pix.setContrastThreshold(contrastThreshold, *m_me); + pix.track(I, m_me, false); + if (pix.getState() == vpMeSite::NO_SUPPRESSION) { + ++nb_pts_added; + iP.set_ij(pix.get_ifloat(), pix.get_jfloat()); + double new_ang = computeAngleOnEllipse(iP); + if ((new_ang - ang) > M_PI) { + new_ang -= 2.0 * M_PI; + } + else if ((ang - new_ang) > M_PI) { + new_ang += 2.0 * M_PI; + } + m_meList.push_back(pix); + m_angleList.push_back(new_ang); } - m_meList.push_back(pix); - m_angleList.push_back(new_ang); } } ang += incr; diff --git a/modules/tracker/me/src/moving-edges/vpMeLine.cpp b/modules/tracker/me/src/moving-edges/vpMeLine.cpp index 43aafd0b3d..b585f37bae 100644 --- a/modules/tracker/me/src/moving-edges/vpMeLine.cpp +++ b/modules/tracker/me/src/moving-edges/vpMeLine.cpp @@ -518,13 +518,17 @@ void vpMeLine::seekExtremities(const vpImage &I) double sample_step = (double)m_me->getSampleStep(); vpMeSite P; + P.init((int)m_PExt[0].m_ifloat, (int)m_PExt[0].m_jfloat, m_delta_1, 0, m_sign); P.setDisplay(m_selectDisplay); + const double marginRatio = m_me->getThresholdMarginRatio(); + double convolution = P.convolution(I, m_me); + double contrastThreshold = fabs(convolution) * marginRatio; + P.setContrastThreshold(contrastThreshold, *m_me); unsigned int memory_range = m_me->getRange(); m_me->setRange(1); - for (int i = 0; i < 3; i++) { P.m_ifloat = P.m_ifloat + di * sample_step; P.m_i = static_cast(P.m_ifloat); @@ -535,28 +539,32 @@ void vpMeLine::seekExtremities(const vpImage &I) iP.set_i(P.m_ifloat); iP.set_j(P.m_jfloat); - unsigned int is_uint = static_cast(P.m_ifloat); - unsigned int js_uint = static_cast(P.m_jfloat); - if ((!outOfImage(iP, 5, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint) - && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { - P.track(I, m_me, false); - - if (P.getState() == vpMeSite::NO_SUPPRESSION) { - m_meList.push_back(P); - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, iP, 5, vpColor::green); + // First test to ensure that iP coordinates are > 0 before casting to unsigned int + if (!outOfImage(iP, 5, nbrows, nbcols)) { + unsigned int is_uint = static_cast(P.m_ifloat); + unsigned int js_uint = static_cast(P.m_jfloat); + if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { + P.track(I, m_me, false); + if (P.getState() == vpMeSite::NO_SUPPRESSION) { + m_meList.push_back(P); + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, iP, 5, vpColor::green); + } } - } - else { - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, iP, 10, vpColor::blue); + else { + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, iP, 10, vpColor::blue); + } } } } } - P.init((int)m_PExt[1].m_ifloat, (int)m_PExt[1].m_jfloat, m_delta_1, 0, m_sign); P.setDisplay(m_selectDisplay); + convolution = P.convolution(I, m_me); + contrastThreshold = fabs(convolution) * marginRatio; + P.setContrastThreshold(contrastThreshold, *m_me); + for (int i = 0; i < 3; i++) { P.m_ifloat = P.m_ifloat - di * sample_step; P.m_i = static_cast(P.m_ifloat); @@ -567,21 +575,22 @@ void vpMeLine::seekExtremities(const vpImage &I) iP.set_i(P.m_ifloat); iP.set_j(P.m_jfloat); - unsigned int is_uint = static_cast(P.m_ifloat); - unsigned int js_uint = static_cast(P.m_jfloat); - if ((!outOfImage(iP, 5, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint) - && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { - P.track(I, m_me, false); - - if (P.getState() == vpMeSite::NO_SUPPRESSION) { - m_meList.push_back(P); - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, iP, 5, vpColor::green); + // First test to ensure that iP coordinates are > 0 before casting to unsigned int + if (!outOfImage(iP, 5, nbrows, nbcols)) { + unsigned int is_uint = static_cast(P.m_ifloat); + unsigned int js_uint = static_cast(P.m_jfloat); + if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) { + P.track(I, m_me, false); + if (P.getState() == vpMeSite::NO_SUPPRESSION) { + m_meList.push_back(P); + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, iP, 5, vpColor::green); + } } - } - else { - if (vpDEBUG_ENABLE(3)) { - vpDisplay::displayCross(I, iP, 10, vpColor::blue); + else { + if (vpDEBUG_ENABLE(3)) { + vpDisplay::displayCross(I, iP, 10, vpColor::blue); + } } } }