1 # Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
3 @page imageprocessing_features_usecase1 Single Camera Feature Tracking
5 This tutorial shows how dwFeature2DDetector and dwFeature2DTracker modules are typically used. Note that error handling is left out for clarity.
7 #### Initialize feature history array on both CPU and GPU
10 dwFeatureHistoryArray_create(&featureHistoryCPU, maxFeatureCount, maxHistoryCapacity,
11 DW_MEMORY_TYPE_CPU, contextHandle);
13 dwFeatureHistoryArray_create(&featureHistoryGPU, maxFeatureCount, maxHistoryCapacity,
14 DW_MEMORY_TYPE_CUDA, contextHandle);
18 #### Initialize feature array on GPU for detector
21 dwFeatureArray_create(&featuresDetected, maxFeatureCount, DW_MEMORY_TYPE_CUDA, contextHandle);
25 #### Initialize feature detector and tracker parameters with default values
28 dwFeature2DDetectorConfig detectorConfig = {};
29 dwFeature2DDetector_initDefaultParams(&detectorConfig);
31 dwFeature2DTrackerConfig trackerConfig = {};
32 dwFeature2DTracker_initDefaultParams(&trackerConfig);
35 Modify parameters according to input image and detection requirements, create detector.
36 Detector will first split the given WxH input image into cellSize x cellSize cells, detect Harris corner whose score is higher than scoreThreshold. Features with score >= scoreThreshold will be considered as high frequency details (red crosses). Detail features will be added to candidate list first, detector will then sort the rest features with score between (scoreThreshold, detailThreshold) from highest to lowest by cell, select the first numEvenDistributionPerCell features in each cell (blue crosses, here numEvenDistributionPerCell = 5, so there're 5 second-highest-score features in each cell). These second-highest-score features are distributed evenly across cells.
37 
38 Setting scoreThreshold >= detailThreshold means all features are high frequency details, numEvenDistributionPerCell will be invalid. Setting numEvenDistributionPerCell = 0 turns off the even distribution component. Both of the 2 cases will make detected features be clustered in high frequency regions, i.e. only red crosses will be left in above illustration.
39 `contextHandle` is assumed to be previously initialized dwContextHandle_t.
41 detectorConfig.imageWidth = inputImageProps.width;
42 detectorConfig.imageHeight = inputImageProps.height;
43 detectorConfig.scoreThreshold = scoreThreshold;
44 detectorConfig.detailThreshold = detailThreshold;
45 detectorConfig.cellSize = cellSize;
46 detectorConfig.maxFeatureCount = maxFeatureCount;
47 detectorConfig.numEvenDistributionPerCell = numEvenDistributionPerCell;
49 dwFeature2DDetector_initialize(&detector, &detectorConfig, 0, contextHandle);
52 Modify parameters to use PVA Harris feature detector, create detector.
53 The standard Harris corner detection process is applied. First detector computes the spatial gradient, using a separable gradient filter. Gradient filter size is 3x3, 5x5 or 7x7, correspond to gradientSize value 3,5,7. Then Computes a gradients covariance matrix (structure tensor) for each pixel within a block window, the block window size is 3x3, 5x5, or 7x7, correspond to blockSize value 3,5,7. Later Computes a Harris response score using a sensitivity factor, setting harrisK to modify the factor. Last applies a threshold-strength criterion, the threshold value is setted by scoreThreshold, it should be a uint32 value.
54 `contextHandle` is assumed to be previously initialized dwContextHandle_t.
56 detectorConfig.type = DW_FEATURE2D_DETECTOR_TYPE_STD
57 detectorConfig.imageWidth = inputImageProps.width;
58 detectorConfig.imageHeight = inputImageProps.height;
59 detectorConfig.scoreThreshold = scoreThreshold;
60 detectorConfig.blockSize = blockSize;
61 detectorConfig.gradientSize = gradientSize;
62 detectorConfig.maxFeatureCount = maxFeatureCount;
63 detectorConfig.harrisK = harrisK;
64 detectorConfig.processorType = DW_PROCESSOR_TYPE_PVA_0;//or DW_PROCESSOR_TYPE_PVA_1
65 dwFeature2DDetector_initialize(&detector, &detectorConfig, 0, contextHandle);
68 Modify parameters to use Fast9 feature detector, create detector.
69 Fast9 Corner Detector will detect corner whose pixel vaule is larger or smaller than at least 9 continous pixels on the Bresenham circle around it by at least scoreThreshold. And non-maximum suppression can be choosen to apply to filter detected features.
70 `contextHandle` is assumed to be previously initialized dwContextHandle_t.
72 detectorConfig.type = DW_FEATURE2D_DETECTOR_TYPE_FAST9
73 detectorConfig.imageWidth = inputImageProps.width;
74 detectorConfig.imageHeight = inputImageProps.height;
75 detectorConfig.scoreThreshold = scoreThreshold;
76 detectorConfig.maxFeatureCount = maxFeatureCount;
77 detectorConfig.NMSRadius = nmsRadius;
78 detectorConfig.processorType = DW_PROCESSOR_TYPE_GPU;//or DW_PROCESSOR_TYPE_PVA_0, DW_PROCESSOR_TYPE_PVA_1
79 dwFeature2DDetector_initialize(&detector, &detectorConfig, 0, contextHandle);
82 Modify parameters according to input image and tracking requirements, create tracker.
83 `trackerConfig.isInputCompact` must be read from detector.
84 `contextHandle` is assumed to be previously initialized dwContextHandle_t.
86 trackerConfig.imageWidth = inputImageProps.width;
87 trackerConfig.imageHeight = inputImageProps.height;
88 trackerConfig.windowSizeLK = 10;
89 trackerConfig.iterationsLK = 10;
90 trackerConfig.detectorType = detectorConfig.type;
92 dwFeature2DTracker_initialize(&tracker, &trackerConfig, 0, contextHandle);
95 Create 2 image pyramids as detection/tracking input: one is current, one is previous, the 2 pyramids work as a double buffer (or ping-pong buffer). They swap each other after each detection/tracking cycle. So new frame will always be read into pyrmaidCurrent and the previous frame will be stored to pyramidPrevious.
97 `dwPyramid_create` only creates pyramid image and allocates memory, pyramid will be filled in `dwImageFilter_computePyramid`. Top level (level 0) in pyramid is always the same size as original input image.
99 `inputImageProps.width`, `inputImageProps.height` are the same in tracker/detector initialization
101 `imagePxlType` should be got from `dwImage_getPixelType(inputImageProps.format)`.
103 `contextHandle` is assumed to be previously initialized dwContextHandle_t.
106 dwPyramid_create(&pyramidPrevious, levelCount,
107 inputImageProps.width, inputImageProps.height, pxlType, context);
109 dwPyramid_create(&pyramidCurrent, levelCount,
110 inputImageProps.width, inputImageProps.height, pxlType,context);
113 #### Create nccScore array on GPU
115 cudaMalloc(&d_nccScores, maxFeatureCount * sizeof(float32_t));
118 #### Start tracking the loop
123 // ping-pong current/previous pyramid
124 // so new frame will always be built in pyramidCurrent
125 // and old pyramid will be stored in pyramidPrevious
126 std::swap(pyramidCurrent, pyramidPrevious);
128 // CODE: Read image frame code
129 dwImageHandle_t image = ...;
131 // compute pyramid based on the new read frame image
132 dwImageFilter_computePyramid(&pyramidCurrent, image, 0, contextHandle);
135 dwFeatureArray featurePredicted;
136 dwFeature2DTracker_trackFeatures(&featureHistoryGPU, &featurePredicted,
137 d_nccScores, &featuresDetected,
138 &pyramidPrevious, &pyramidCurrent, tracker)
140 // detect new features in the current frame
141 dwFeature2DDetector_detectFromImage(&featuresDetected, pyramidCurrent.levelImages[0],
142 &featurePredicted, d_nccScores, detector)
144 // copy detected results to CPU
145 dwFeatureHistoryArray_copy(&featureHistoryCPU, &featureHistoryGPU, 0);
147 // do some stuff in CPU
148 featureHistoryCPU.locationHistory[...];
149 featureHistoryCPU.statuses[...];
153 #### Finally, free previously allocated memory.
156 cudaFree(d_nccScores);
158 dwPyramid_destroy(pyramidPrevious);
159 dwPyramid_destroy(pyramidCurrent);
161 dwFeatureHistoryArray_destroy(featureHistoryCPU);
162 dwFeatureHistoryArray_destroy(featureHistoryGPU);
163 dwFeatureArray_destroy(featuresDetected);
165 dwFeature2DDetector_release(detector);
166 dwFeature2DTracker_release(tracker);
169 For the full implementation refer to @ref dwx_feature_tracker_sample.