1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//! Various object detection algorithms, such as Haar feature-based cascade
//! classifier for object detection and histogram of oriented gradients (HOG).

use super::core::*;
use super::errors::*;
use super::*;
use failure::Error;
use std::ffi::CString;
use std::os::raw::{c_char, c_double, c_int};
use std::path::Path;
use std::vec::Vec;

enum CCascadeClassifier {}

extern "C" {
    fn cv_cascade_classifier_new() -> *mut CCascadeClassifier;
    fn cv_cascade_classifier_load(cc: *mut CCascadeClassifier, p: *const c_char) -> bool;
    fn cv_cascade_classifier_drop(p: *mut CCascadeClassifier);
    fn cv_cascade_classifier_detect(
        cc: *mut CCascadeClassifier,
        cmat: *mut CMat,
        vec_of_rect: *mut CVec<Rect>,
        scale_factor: c_double,
        min_neighbors: c_int,
        flags: c_int,
        min_size: Size2i,
        max_size: Size2i,
    );
}

/// We can safely send the classifier (a mutable pointer) to a different thread
unsafe impl Send for CascadeClassifier {}

/// An object detect trait.
pub trait ObjectDetect {
    /// Detects the object inside this image and returns a list of detections
    /// with their confidence.
    fn detect(&self, image: &Mat) -> Vec<(Rect, f64)>;
}

/// Cascade classifier class for object detection.
#[derive(Debug)]
pub struct CascadeClassifier {
    inner: *mut CCascadeClassifier,
}

impl ObjectDetect for CascadeClassifier {
    fn detect(&self, image: &Mat) -> Vec<(Rect, f64)> {
        self.detect_multiscale(image)
            .into_iter()
            .map(|r| (r, 0f64))
            .collect::<Vec<_>>()
    }
}

impl CascadeClassifier {
    /// Creates a cascade classifier, uninitialized. Before use, call load.
    pub fn new() -> CascadeClassifier {
        CascadeClassifier {
            inner: unsafe { cv_cascade_classifier_new() },
        }
    }

    /// Creates a cascade classifier using the model specified.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
        let cc = CascadeClassifier::new();
        cc.load(path)?;
        Ok(cc)
    }

    /// Loads the classifier model from a path.
    pub fn load<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        if let Some(p) = path.as_ref().to_str() {
            let s = CString::new(p)?;
            if unsafe { cv_cascade_classifier_load(self.inner, (&s).as_ptr()) } {
                return Ok(());
            }
        }

        Err(CvError::InvalidPath(path.as_ref().to_path_buf()).into())
    }

    /// The default detection uses scale factor 1.1, minNeighbors 3, no min size
    /// or max size.
    pub fn detect_multiscale(&self, mat: &Mat) -> Vec<Rect> {
        self.detect_with_params(mat, 1.1, 3, Size2i::default(), Size2i::default())
    }

    /// Detects the object using parameters specified.
    ///
    /// * `mat` - Matrix of the type CV_8U containing an image where objects are
    ///   detected.
    /// * `scale_factor` - Parameter specifying how much the image size is
    ///   reduced at each image scale.
    /// * `min_neighbors` - Parameter specifying how many neighbors each
    ///   candidate rectangle should have to retain it.
    /// * `min_size` - Minimum possible object size. Objects smaller than that
    ///   are ignored.
    /// * `max_size` - Maximum possible object size. Objects larger than that
    ///   are ignored
    ///
    /// OpenCV has a parameter (`flags`) that's not used at all.
    pub fn detect_with_params(
        &self,
        mat: &Mat,
        scale_factor: f32,
        min_neighbors: c_int,
        min_size: Size2i,
        max_size: Size2i,
    ) -> Vec<Rect> {
        let mut c_result = CVec::<Rect>::default();
        unsafe {
            cv_cascade_classifier_detect(
                self.inner,
                mat.inner,
                &mut c_result,
                scale_factor as c_double,
                min_neighbors,
                0,
                min_size,
                max_size,
            )
        }
        c_result.unpack()
    }
}

impl Drop for CascadeClassifier {
    fn drop(&mut self) {
        unsafe {
            cv_cascade_classifier_drop(self.inner);
        }
    }
}

#[derive(Debug, Clone, Copy)]
/// Opaque type for C/C++ SvmDetector object
pub enum CSvmDetector {}

/// SvmDetector
#[derive(Debug)]
pub struct SvmDetector {
    /// Pointer to the inner data structure
    pub(crate) inner: *mut CSvmDetector,
}

extern "C" {
    fn cv_hog_default_people_detector() -> *mut CSvmDetector;
    fn cv_hog_daimler_people_detector() -> *mut CSvmDetector;
    fn cv_hog_detector_drop(d: *mut CSvmDetector);
}

impl SvmDetector {
    /// The built-in people detector.
    ///
    /// The size of the default people detector is 64x128, that mean that the
    /// people you would want to detect have to be atleast 64x128.
    pub fn default_people_detector() -> SvmDetector {
        SvmDetector {
            inner: unsafe { cv_hog_default_people_detector() },
        }
    }

    /// Returns the Daimler people detector.
    pub fn daimler_people_detector() -> SvmDetector {
        SvmDetector {
            inner: unsafe { cv_hog_daimler_people_detector() },
        }
    }
}

impl Drop for SvmDetector {
    fn drop(&mut self) {
        unsafe {
            cv_hog_detector_drop(self.inner);
        }
    }
}

/// Parameters that controls the behavior of HOG.
#[derive(Debug, Clone, Copy)]
pub struct HogParams {
    /// Detection window size. Align to block size and block stride. The default
    /// is 64x128, trained the same as original paper.
    pub win_size: Size2i,

    /// Block size in pixels. Align to cell size. Only (16,16) is supported for
    /// now (at least for GPU).
    pub block_size: Size2i,

    /// Block stride. It must be a multiple of cell size.
    pub block_stride: Size2i,

    /// Cell size. Only (8, 8) is supported for now.
    pub cell_size: Size2i,

    /// Number of bins. Only 9 bins per cell are supported for now.
    pub nbins: c_int,

    /// Gaussian smoothing window parameter. Default -1 for CPU and 4.0 for GPU.
    pub win_sigma: f64,

    /// L2-Hys normalization method shrinkage. Default 0.2.
    pub l2hys_threshold: f64,

    /// Flag to specify whether the gamma correction preprocessing is required
    /// or not. Default false.
    pub gamma_correction: bool,

    /// Maximum number of detection window increases (HOG scales). Default: 64.
    pub nlevels: usize,

    // =======================================================================
    //  Functions from detect function
    // =======================================================================
    /// Threshold for the distance between features and SVM classifying
    /// plane. Usually it is 0 and should be specfied in the detector
    /// coefficients (as the last free coefficient). But if the free coefficient
    /// is omitted (which is allowed), you can specify it manually here.
    pub hit_threshold: f64,

    /// Window stride. It must be a multiple of block stride.
    pub win_stride: Size2i,

    /// Padding
    pub padding: Size2i,

    /// Coefficient of the detection window increase.
    pub scale: f64,

    /// Coefficient to regulate the similarity threshold. When detected, some
    /// objects can be covered by many rectangles. 0 means not to perform
    /// grouping.
    pub group_threshold: c_int,

    /// The useMeanShiftGrouping parameter is a boolean indicating whether or
    /// not mean-shift grouping should be performed to handle potential
    /// overlapping bounding boxes. While this value should not be set and users
    /// should employ non-maxima suppression instead, we support setting it as a
    /// library function.
    pub use_meanshift_grouping: bool,

    /// The `finalThreshold` parameter is mainly used to select the clusters
    /// that have at least `finalThreshold + 1` rectangles. This parameter is
    /// passed when meanShift is enabled; the function rejects the small
    /// clusters containing less than or equal to `finalThreshold` rectangles,
    /// computes the average rectangle size for the rest of the accepted
    /// clusters and adds those to the output rectangle list.
    pub final_threshold: f64,
}

const DEFAULT_WIN_SIGMA: f64 = -1f64;
const DEFAULT_NLEVELS: usize = 64;

impl Default for HogParams {
    fn default() -> HogParams {
        let win_sigma = {
            if cfg!(feature = "cuda") {
                4.0
            } else {
                DEFAULT_WIN_SIGMA
            }
        };

        HogParams {
            win_size: Size2i::new(64, 128),
            block_size: Size2i::new(16, 16),
            block_stride: Size2i::new(8, 8),
            cell_size: Size2i::new(8, 8),
            nbins: 9,

            win_sigma: win_sigma,
            l2hys_threshold: 0.2,
            gamma_correction: false,
            nlevels: DEFAULT_NLEVELS,

            hit_threshold: 0f64,
            win_stride: Size2i::new(8, 8),
            padding: Size2i::default(),
            scale: 1.05,
            group_threshold: 2,

            final_threshold: 2.0,
            use_meanshift_grouping: false,
        }
    }
}

enum CHogDescriptor {}

/// `HogDescriptor` implements Histogram of Oriented Gradients.
#[derive(Debug)]
pub struct HogDescriptor {
    inner: *mut CHogDescriptor,

    /// Hog parameters.
    pub params: HogParams,
}

unsafe impl Send for HogDescriptor {}

extern "C" {
    fn cv_hog_new() -> *mut CHogDescriptor;
    fn cv_hog_drop(hog: *mut CHogDescriptor);
    fn cv_hog_set_svm_detector(hog: *mut CHogDescriptor, svm: *mut CSvmDetector);
    fn cv_hog_detect(
        hog: *mut CHogDescriptor,
        image: *mut CMat,
        objs: *mut CVec<Rect>,
        weights: *mut CVec<c_double>,
        win_stride: Size2i,
        padding: Size2i,
        scale: c_double,
        final_threshold: c_double,
        use_means_shift: bool,
    );
}

impl Default for HogDescriptor {
    fn default() -> HogDescriptor {
        HogDescriptor {
            inner: unsafe { cv_hog_new() },
            params: HogParams::default(),
        }
    }
}

impl ObjectDetect for HogDescriptor {
    fn detect(&self, image: &Mat) -> Vec<(Rect, f64)> {
        let mut detected = CVec::<Rect>::default();
        let mut weights = CVec::<c_double>::default();
        unsafe {
            cv_hog_detect(
                self.inner,
                image.inner,
                &mut detected,
                &mut weights,
                self.params.win_stride,
                self.params.padding,
                self.params.scale,
                self.params.final_threshold,
                self.params.use_meanshift_grouping,
            )
        }

        let results = detected.unpack();
        let weights = weights.unpack();
        results.into_iter().zip(weights).collect::<Vec<_>>()
    }
}

impl HogDescriptor {
    /// Creates a HogDescriptor with provided parameters.
    pub fn with_params(params: HogParams) -> HogDescriptor {
        HogDescriptor {
            inner: unsafe { cv_hog_new() },
            params: params,
        }
    }

    /// Sets the SVM detector.
    pub fn set_svm_detector(&mut self, detector: SvmDetector) {
        unsafe { cv_hog_set_svm_detector(self.inner, detector.inner) }
    }
}

impl Drop for HogDescriptor {
    fn drop(&mut self) {
        unsafe { cv_hog_drop(self.inner) }
    }
}