interpolation.h 71.2 KB
Newer Older
1
2
// Copyright (C) 2012  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
3
4
#ifndef DLIB_INTERPOlATIONh_
#define DLIB_INTERPOlATIONh_ 
5
6
7
8
9

#include "interpolation_abstract.h"
#include "../pixel.h"
#include "../matrix.h"
#include "assign_image.h"
10
#include "image_pyramid.h"
11
#include "../simd.h"
12
#include "../image_processing/full_object_detection.h"
13
14
15
16

namespace dlib
{

17
18
19
20
21
22
// ----------------------------------------------------------------------------------------

    template <typename T>
    struct sub_image_proxy
    {
        sub_image_proxy (
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
            T& img,
            rectangle rect
        ) 
        {
            rect = rect.intersect(get_rect(img));
            typedef typename image_traits<T>::pixel_type pixel_type;

            _nr = rect.height();
            _nc = rect.width();
            _width_step = width_step(img);
            _data = (char*)image_data(img) + sizeof(pixel_type)*rect.left() + rect.top()*_width_step;
        }

        void* _data;
        long _width_step;
        long _nr;
        long _nc;
    };

    template <typename T>
    struct const_sub_image_proxy
    {
        const_sub_image_proxy (
            const T& img,
            rectangle rect
        ) 
        {
            rect = rect.intersect(get_rect(img));
            typedef typename image_traits<T>::pixel_type pixel_type;
52

53
54
55
56
57
58
59
60
61
62
            _nr = rect.height();
            _nc = rect.width();
            _width_step = width_step(img);
            _data = (const char*)image_data(img) + sizeof(pixel_type)*rect.left() + rect.top()*_width_step;
        }

        const void* _data;
        long _width_step;
        long _nr;
        long _nc;
63
64
65
66
67
68
69
70
71
72
73
74
    };

    template <typename T>
    struct image_traits<sub_image_proxy<T> >
    {
        typedef typename image_traits<T>::pixel_type pixel_type;
    };
    template <typename T>
    struct image_traits<const sub_image_proxy<T> >
    {
        typedef typename image_traits<T>::pixel_type pixel_type;
    };
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
    template <typename T>
    struct image_traits<const_sub_image_proxy<T> >
    {
        typedef typename image_traits<T>::pixel_type pixel_type;
    };
    template <typename T>
    struct image_traits<const const_sub_image_proxy<T> >
    {
        typedef typename image_traits<T>::pixel_type pixel_type;
    };

    template <typename T>
    inline long num_rows( const sub_image_proxy<T>& img) { return img._nr; }
    template <typename T>
    inline long num_columns( const sub_image_proxy<T>& img) { return img._nc; }
90
91

    template <typename T>
92
    inline long num_rows( const const_sub_image_proxy<T>& img) { return img._nr; }
93
    template <typename T>
94
    inline long num_columns( const const_sub_image_proxy<T>& img) { return img._nc; }
95
96
97
98

    template <typename T>
    inline void* image_data( sub_image_proxy<T>& img) 
    { 
99
        return img._data; 
100
101
102
103
    } 
    template <typename T>
    inline const void* image_data( const sub_image_proxy<T>& img) 
    {
104
105
106
107
108
109
110
        return img._data; 
    }

    template <typename T>
    inline const void* image_data( const const_sub_image_proxy<T>& img) 
    {
        return img._data; 
111
112
113
114
115
    }

    template <typename T>
    inline long width_step(
        const sub_image_proxy<T>& img
116
117
118
119
120
121
    ) { return img._width_step; }

    template <typename T>
    inline long width_step(
        const const_sub_image_proxy<T>& img
    ) { return img._width_step; }
122

123
124
125
126
127
128
129
130
131
132
133
    template <typename T>
    void set_image_size(sub_image_proxy<T>& img, long rows, long cols)
    {
        DLIB_CASSERT(img._nr == rows && img._nc == cols, "A sub_image can't be resized."
            << "\n\t img._nr: "<< img._nr
            << "\n\t img._nc: "<< img._nc
            << "\n\t rows:    "<< rows
            << "\n\t cols:    "<< cols
            );
    }

134
135
136
137
138
139
140
141
142
143
144
145
146
147
    template <
        typename image_type
        >
    sub_image_proxy<image_type> sub_image (
        image_type& img,
        const rectangle& rect
    )
    {
        return sub_image_proxy<image_type>(img,rect);
    }

    template <
        typename image_type
        >
148
    const const_sub_image_proxy<image_type> sub_image (
149
150
151
152
        const image_type& img,
        const rectangle& rect
    )
    {
153
        return const_sub_image_proxy<image_type>(img,rect);
154
155
    }

156
// ----------------------------------------------------------------------------------------
157
158
159
160
161
162
// ----------------------------------------------------------------------------------------

    class interpolate_nearest_neighbor
    {
    public:

163
        template <typename image_view_type, typename pixel_type>
164
        bool operator() (
165
            const image_view_type& img,
166
            const dlib::point& p,
167
            pixel_type& result
168
169
        ) const
        {
170
            COMPILE_TIME_ASSERT(pixel_traits<typename image_view_type::pixel_type>::has_alpha == false);
171

172
173
            if (get_rect(img).contains(p))
            {
174
                assign_pixel(result, img[p.y()][p.x()]);
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
                return true;
            }
            else
            {
                return false;
            }
        }

    };

// ----------------------------------------------------------------------------------------

    class interpolate_bilinear
    {
        template <typename T>
        struct is_rgb_image 
        {
192
            const static bool value = pixel_traits<typename T::pixel_type>::rgb;
193
194
195
196
        };

    public:

197
198
199
        template <typename T, typename image_view_type, typename pixel_type>
        typename disable_if<is_rgb_image<image_view_type>,bool>::type operator() (
            const image_view_type& img,
200
            const dlib::vector<T,2>& p,
201
            pixel_type& result
202
203
        ) const
        {
204
            COMPILE_TIME_ASSERT(pixel_traits<typename image_view_type::pixel_type>::has_alpha == false);
Davis King's avatar
Davis King committed
205

206
            const long left   = static_cast<long>(std::floor(p.x()));
207
208
209
            const long top    = static_cast<long>(std::floor(p.y()));
            const long right  = left+1;
            const long bottom = top+1;
210
211
212


            // if the interpolation goes outside img 
213
            if (!(left >= 0 && top >= 0 && right < img.nc() && bottom < img.nr()))
214
215
                return false;

216
217
            const double lr_frac = p.x() - left;
            const double tb_frac = p.y() - top;
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

            double tl = 0, tr = 0, bl = 0, br = 0;

            assign_pixel(tl, img[top][left]);
            assign_pixel(tr, img[top][right]);
            assign_pixel(bl, img[bottom][left]);
            assign_pixel(br, img[bottom][right]);
            
            double temp = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + 
                              tb_frac*((1-lr_frac)*bl + lr_frac*br);
                            
            assign_pixel(result, temp);
            return true;
        }

233
234
235
        template <typename T, typename image_view_type, typename pixel_type>
        typename enable_if<is_rgb_image<image_view_type>,bool>::type operator() (
            const image_view_type& img,
236
            const dlib::vector<T,2>& p,
237
            pixel_type& result
238
239
        ) const
        {
240
            COMPILE_TIME_ASSERT(pixel_traits<typename image_view_type::pixel_type>::has_alpha == false);
Davis King's avatar
Davis King committed
241

242
            const long left   = static_cast<long>(std::floor(p.x()));
243
244
245
            const long top    = static_cast<long>(std::floor(p.y()));
            const long right  = left+1;
            const long bottom = top+1;
246
247
248


            // if the interpolation goes outside img 
249
            if (!(left >= 0 && top >= 0 && right < img.nc() && bottom < img.nr()))
250
251
                return false;

252
253
            const double lr_frac = p.x() - left;
            const double tb_frac = p.y() - top;
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

            double tl, tr, bl, br;

            tl = img[top][left].red;
            tr = img[top][right].red;
            bl = img[bottom][left].red;
            br = img[bottom][right].red;
            const double red = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + 
                                   tb_frac*((1-lr_frac)*bl + lr_frac*br);

            tl = img[top][left].green;
            tr = img[top][right].green;
            bl = img[bottom][left].green;
            br = img[bottom][right].green;
            const double green = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + 
                                   tb_frac*((1-lr_frac)*bl + lr_frac*br);

            tl = img[top][left].blue;
            tr = img[top][right].blue;
            bl = img[bottom][left].blue;
            br = img[bottom][right].blue;
            const double blue = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + 
                                   tb_frac*((1-lr_frac)*bl + lr_frac*br);
                            
278
279
280
281
282
            rgb_pixel temp;
            assign_pixel(temp.red, red);
            assign_pixel(temp.green, green);
            assign_pixel(temp.blue, blue);
            assign_pixel(result, temp);
283
284
285
286
287
288
289
290
291
292
293
            return true;
        }
    };

// ----------------------------------------------------------------------------------------

    class interpolate_quadratic
    {
        template <typename T>
        struct is_rgb_image 
        {
294
            const static bool value = pixel_traits<typename T::pixel_type>::rgb;
295
296
297
298
        };

    public:

299
300
301
        template <typename T, typename image_view_type, typename pixel_type>
        typename disable_if<is_rgb_image<image_view_type>,bool>::type operator() (
            const image_view_type& img,
302
            const dlib::vector<T,2>& p,
303
            pixel_type& result
304
305
        ) const
        {
306
            COMPILE_TIME_ASSERT(pixel_traits<typename image_view_type::pixel_type>::has_alpha == false);
Davis King's avatar
Davis King committed
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
            const point pp(p);

            // if the interpolation goes outside img 
            if (!get_rect(img).contains(grow_rect(pp,1))) 
                return false;

            const long r = pp.y();
            const long c = pp.x();

            const double temp = interpolate(p-pp, 
                                    img[r-1][c-1],
                                    img[r-1][c  ],
                                    img[r-1][c+1],
                                    img[r  ][c-1],
                                    img[r  ][c  ],
                                    img[r  ][c+1],
                                    img[r+1][c-1],
                                    img[r+1][c  ],
                                    img[r+1][c+1]);

            assign_pixel(result, temp);
            return true;
        }

332
333
334
        template <typename T, typename image_view_type, typename pixel_type>
        typename enable_if<is_rgb_image<image_view_type>,bool>::type operator() (
            const image_view_type& img,
335
            const dlib::vector<T,2>& p,
336
            pixel_type& result
337
338
        ) const
        {
339
            COMPILE_TIME_ASSERT(pixel_traits<typename image_view_type::pixel_type>::has_alpha == false);
Davis King's avatar
Davis King committed
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
372
373
374
375
376
377
378
379
380
381
            const point pp(p);

            // if the interpolation goes outside img 
            if (!get_rect(img).contains(grow_rect(pp,1))) 
                return false;

            const long r = pp.y();
            const long c = pp.x();

            const double red = interpolate(p-pp, 
                            img[r-1][c-1].red,
                            img[r-1][c  ].red,
                            img[r-1][c+1].red,
                            img[r  ][c-1].red,
                            img[r  ][c  ].red,
                            img[r  ][c+1].red,
                            img[r+1][c-1].red,
                            img[r+1][c  ].red,
                            img[r+1][c+1].red);
            const double green = interpolate(p-pp, 
                            img[r-1][c-1].green,
                            img[r-1][c  ].green,
                            img[r-1][c+1].green,
                            img[r  ][c-1].green,
                            img[r  ][c  ].green,
                            img[r  ][c+1].green,
                            img[r+1][c-1].green,
                            img[r+1][c  ].green,
                            img[r+1][c+1].green);
            const double blue = interpolate(p-pp, 
                            img[r-1][c-1].blue,
                            img[r-1][c  ].blue,
                            img[r-1][c+1].blue,
                            img[r  ][c-1].blue,
                            img[r  ][c  ].blue,
                            img[r  ][c+1].blue,
                            img[r+1][c-1].blue,
                            img[r+1][c  ].blue,
                            img[r+1][c+1].blue);


382
383
384
385
386
            rgb_pixel temp;
            assign_pixel(temp.red, red);
            assign_pixel(temp.green, green);
            assign_pixel(temp.blue, blue);
            assign_pixel(result, temp);
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458

            return true;
        }

    private:

        /*  tl tm tr
            ml mm mr
            bl bm br
        */
        // The above is the pixel layout in our little 3x3 neighborhood.  interpolate() will 
        // fit a quadratic to these 9 pixels and then use that quadratic to find the interpolated 
        // value at point p.
        inline double interpolate(
            const dlib::vector<double,2>& p,
            double tl, double tm, double tr, 
            double ml, double mm, double mr, 
            double bl, double bm, double br
        ) const
        {
            matrix<double,6,1> w;
            // x
            w(0) = (tr + mr + br - tl - ml - bl)*0.16666666666;
            // y
            w(1) = (bl + bm + br - tl - tm - tr)*0.16666666666;
            // x^2
            w(2) = (tl + tr + ml + mr + bl + br)*0.16666666666 - (tm + mm + bm)*0.333333333;
            // x*y
            w(3) = (tl - tr - bl + br)*0.25;
            // y^2
            w(4) = (tl + tm + tr + bl + bm + br)*0.16666666666 - (ml + mm + mr)*0.333333333;
            // 1 (constant term)
            w(5) = (tm + ml + mr + bm)*0.222222222 - (tl + tr + bl + br)*0.11111111 + (mm)*0.55555556;

            const double x = p.x();
            const double y = p.y();

            matrix<double,6,1> z;
            z = x, y, x*x, x*y, y*y, 1.0;
                            
            return dot(w,z);
        }
    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class black_background
    {
    public:
        template <typename pixel_type>
        void operator() ( pixel_type& p) const { assign_pixel(p, 0); }
    };

    class white_background
    {
    public:
        template <typename pixel_type>
        void operator() ( pixel_type& p) const { assign_pixel(p, 255); }
    };

    class no_background
    {
    public:
        template <typename pixel_type>
        void operator() ( pixel_type& ) const { }
    };

// ----------------------------------------------------------------------------------------

    template <
459
460
        typename image_type1,
        typename image_type2,
461
462
463
464
465
        typename interpolation_type,
        typename point_mapping_type,
        typename background_type
        >
    void transform_image (
466
467
        const image_type1& in_img,
        image_type2& out_img,
468
469
470
471
472
473
        const interpolation_type& interp,
        const point_mapping_type& map_point,
        const background_type& set_background,
        const rectangle& area
    )
    {
Davis King's avatar
Davis King committed
474
475
476
477
478
479
480
481
482
483
484
        // make sure requires clause is not broken
        DLIB_ASSERT( get_rect(out_img).contains(area) == true &&
                     is_same_object(in_img, out_img) == false ,
            "\t void transform_image()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t get_rect(out_img).contains(area): " << get_rect(out_img).contains(area)
            << "\n\t get_rect(out_img): " << get_rect(out_img)
            << "\n\t area:              " << area
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

485
486
        const_image_view<image_type1> imgv(in_img);
        image_view<image_type2> out_imgv(out_img);
Davis King's avatar
Davis King committed
487

488
489
490
491
        for (long r = area.top(); r <= area.bottom(); ++r)
        {
            for (long c = area.left(); c <= area.right(); ++c)
            {
492
493
                if (!interp(imgv, map_point(dlib::vector<double,2>(c,r)), out_imgv[r][c]))
                    set_background(out_imgv[r][c]);
494
495
496
497
498
499
500
            }
        }
    }

// ----------------------------------------------------------------------------------------

    template <
501
502
        typename image_type1,
        typename image_type2,
503
504
505
506
507
        typename interpolation_type,
        typename point_mapping_type,
        typename background_type
        >
    void transform_image (
508
509
        const image_type1& in_img,
        image_type2& out_img,
510
511
512
513
514
        const interpolation_type& interp,
        const point_mapping_type& map_point,
        const background_type& set_background
    )
    {
Davis King's avatar
Davis King committed
515
516
517
518
519
520
521
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
            "\t void transform_image()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

Davis King's avatar
Davis King committed
522
        transform_image(in_img, out_img, interp, map_point, set_background, get_rect(out_img));
523
524
525
526
527
    }

// ----------------------------------------------------------------------------------------

    template <
528
529
        typename image_type1,
        typename image_type2,
530
531
532
533
        typename interpolation_type,
        typename point_mapping_type
        >
    void transform_image (
534
535
        const image_type1& in_img,
        image_type2& out_img,
536
537
538
539
        const interpolation_type& interp,
        const point_mapping_type& map_point
    )
    {
Davis King's avatar
Davis King committed
540
541
542
543
544
545
546
547
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
            "\t void transform_image()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );


Davis King's avatar
Davis King committed
548
        transform_image(in_img, out_img, interp, map_point, black_background(), get_rect(out_img));
549
550
551
552
553
    }

// ----------------------------------------------------------------------------------------

    template <
554
555
        typename image_type1,
        typename image_type2,
556
557
        typename interpolation_type
        >
558
    point_transform_affine rotate_image (
559
560
        const image_type1& in_img,
        image_type2& out_img,
561
562
563
564
        double angle,
        const interpolation_type& interp
    )
    {
Davis King's avatar
Davis King committed
565
566
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
567
            "\t point_transform_affine rotate_image()"
Davis King's avatar
Davis King committed
568
569
570
571
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

Davis King's avatar
Davis King committed
572
        const rectangle rimg = get_rect(in_img);
573
574
575
576
577
578
579
580


        // figure out bounding box for rotated rectangle
        rectangle rect;
        rect += rotate_point(center(rimg), rimg.tl_corner(), -angle);
        rect += rotate_point(center(rimg), rimg.tr_corner(), -angle);
        rect += rotate_point(center(rimg), rimg.bl_corner(), -angle);
        rect += rotate_point(center(rimg), rimg.br_corner(), -angle);
581
        set_image_size(out_img, rect.height(), rect.width());
582
583
584

        const matrix<double,2,2> R = rotation_matrix(angle);

585
586
        point_transform_affine trans = point_transform_affine(R, -R*dcenter(get_rect(out_img)) + dcenter(rimg));
        transform_image(in_img, out_img, interp, trans);
587
        return inv(trans);
588
589
590
591
592
    }

// ----------------------------------------------------------------------------------------

    template <
593
594
        typename image_type1,
        typename image_type2
595
        >
596
    point_transform_affine rotate_image (
597
598
        const image_type1& in_img,
        image_type2& out_img,
599
600
601
        double angle
    )
    {
Davis King's avatar
Davis King committed
602
603
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
604
            "\t point_transform_affine rotate_image()"
Davis King's avatar
Davis King committed
605
606
607
608
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

609
        return rotate_image(in_img, out_img, angle, interpolate_quadratic());
610
611
    }

Davis King's avatar
Davis King committed
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
// ----------------------------------------------------------------------------------------

    namespace impl
    {
        class helper_resize_image 
        {
        public:
            helper_resize_image(
                double x_scale_,
                double y_scale_
            ):
                x_scale(x_scale_),
                y_scale(y_scale_)
            {}

            dlib::vector<double,2> operator() (
                const dlib::vector<double,2>& p
            ) const
            {
                return dlib::vector<double,2>(p.x()*x_scale, p.y()*y_scale);
            }

        private:
            const double x_scale;
            const double y_scale;
        };
    }

    template <
641
642
        typename image_type1,
        typename image_type2,
Davis King's avatar
Davis King committed
643
644
645
        typename interpolation_type
        >
    void resize_image (
646
647
        const image_type1& in_img,
        image_type2& out_img,
Davis King's avatar
Davis King committed
648
649
650
651
652
653
654
655
656
657
        const interpolation_type& interp
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
            "\t void resize_image()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

658
659
        const double x_scale = (num_columns(in_img)-1)/(double)std::max<long>((num_columns(out_img)-1),1);
        const double y_scale = (num_rows(in_img)-1)/(double)std::max<long>((num_rows(out_img)-1),1);
Davis King's avatar
Davis King committed
660
661
662
663
        transform_image(in_img, out_img, interp, 
                        dlib::impl::helper_resize_image(x_scale,y_scale));
    }

664
665
// ----------------------------------------------------------------------------------------

666
    template <typename image_type>
667
    struct is_rgb_image { const static bool value = pixel_traits<typename image_traits<image_type>::pixel_type>::rgb; };
668
    template <typename image_type>
669
    struct is_grayscale_image { const static bool value = pixel_traits<typename image_traits<image_type>::pixel_type>::grayscale; };
670

671
672
673
674
675
676
    // This is an optimized version of resize_image for the case where bilinear
    // interpolation is used.
    template <
        typename image_type1,
        typename image_type2
        >
677
678
679
    typename disable_if_c<(is_rgb_image<image_type1>::value&&is_rgb_image<image_type2>::value) || 
                          (is_grayscale_image<image_type1>::value&&is_grayscale_image<image_type2>::value)>::type 
    resize_image (
680
681
        const image_type1& in_img_,
        image_type2& out_img_,
682
683
684
        interpolate_bilinear
    )
    {
Davis King's avatar
Davis King committed
685
        // make sure requires clause is not broken
686
        DLIB_ASSERT( is_same_object(in_img_, out_img_) == false ,
Davis King's avatar
Davis King committed
687
688
            "\t void resize_image()"
            << "\n\t Invalid inputs were given to this function."
689
            << "\n\t is_same_object(in_img_, out_img_):  " << is_same_object(in_img_, out_img_)
Davis King's avatar
Davis King committed
690
            );
691
692
693
694

        const_image_view<image_type1> in_img(in_img_);
        image_view<image_type2> out_img(out_img_);

695
696
697
698
699
700
        if (out_img.nr() <= 1 || out_img.nc() <= 1)
        {
            assign_all_pixels(out_img, 0);
            return;
        }

Davis King's avatar
Davis King committed
701

702
703
        typedef typename image_traits<image_type1>::pixel_type T;
        typedef typename image_traits<image_type2>::pixel_type U;
704
705
        const double x_scale = (in_img.nc()-1)/(double)std::max<long>((out_img.nc()-1),1);
        const double y_scale = (in_img.nr()-1)/(double)std::max<long>((out_img.nr()-1),1);
706
        double y = -y_scale;
707
        for (long r = 0; r < out_img.nr(); ++r)
708
709
710
        {
            y += y_scale;
            const long top    = static_cast<long>(std::floor(y));
711
            const long bottom = std::min(top+1, in_img.nr()-1);
712
713
            const double tb_frac = y - top;
            double x = -x_scale;
714
            if (pixel_traits<U>::grayscale)
715
            {
716
                for (long c = 0; c < out_img.nc(); ++c)
717
718
719
                {
                    x += x_scale;
                    const long left   = static_cast<long>(std::floor(x));
720
                    const long right  = std::min(left+1, in_img.nc()-1);
721
722
723
724
                    const double lr_frac = x - left;

                    double tl = 0, tr = 0, bl = 0, br = 0;

725
726
727
728
                    assign_pixel(tl, in_img[top][left]);
                    assign_pixel(tr, in_img[top][right]);
                    assign_pixel(bl, in_img[bottom][left]);
                    assign_pixel(br, in_img[bottom][right]);
729
730
731
732

                    double temp = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + 
                        tb_frac*((1-lr_frac)*bl + lr_frac*br);

733
                    assign_pixel(out_img[r][c], temp);
734
735
736
737
                }
            }
            else
            {
738
                for (long c = 0; c < out_img.nc(); ++c)
739
740
741
                {
                    x += x_scale;
                    const long left   = static_cast<long>(std::floor(x));
742
                    const long right  = std::min(left+1, in_img.nc()-1);
743
744
                    const double lr_frac = x - left;

745
746
747
748
                    const T tl = in_img[top][left];
                    const T tr = in_img[top][right];
                    const T bl = in_img[bottom][left];
                    const T br = in_img[bottom][right];
749

750
751
752
                    T temp;
                    assign_pixel(temp, 0);
                    vector_to_pixel(temp, 
753
754
                        (1-tb_frac)*((1-lr_frac)*pixel_to_vector<double>(tl) + lr_frac*pixel_to_vector<double>(tr)) + 
                            tb_frac*((1-lr_frac)*pixel_to_vector<double>(bl) + lr_frac*pixel_to_vector<double>(br)));
755
                    assign_pixel(out_img[r][c], temp);
756
757
758
759
760
                }
            }
        }
    }

761
762
763
764
765
766
// ----------------------------------------------------------------------------------------

    template <
        typename image_type
        >
    typename enable_if<is_grayscale_image<image_type> >::type resize_image (
767
768
        const image_type& in_img_,
        image_type& out_img_,
769
770
771
772
        interpolate_bilinear
    )
    {
        // make sure requires clause is not broken
773
        DLIB_ASSERT( is_same_object(in_img_, out_img_) == false ,
774
775
            "\t void resize_image()"
            << "\n\t Invalid inputs were given to this function."
776
            << "\n\t is_same_object(in_img_, out_img_):  " << is_same_object(in_img_, out_img_)
777
            );
778
779
780
781

        const_image_view<image_type> in_img(in_img_);
        image_view<image_type> out_img(out_img_);

782
783
784
785
786
787
        if (out_img.nr() <= 1 || out_img.nc() <= 1)
        {
            assign_all_pixels(out_img, 0);
            return;
        }

788
        typedef typename image_traits<image_type>::pixel_type T;
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
        const double x_scale = (in_img.nc()-1)/(double)std::max<long>((out_img.nc()-1),1);
        const double y_scale = (in_img.nr()-1)/(double)std::max<long>((out_img.nr()-1),1);
        double y = -y_scale;
        for (long r = 0; r < out_img.nr(); ++r)
        {
            y += y_scale;
            const long top    = static_cast<long>(std::floor(y));
            const long bottom = std::min(top+1, in_img.nr()-1);
            const double tb_frac = y - top;
            double x = -4*x_scale;

            const simd4f _tb_frac = tb_frac;
            const simd4f _inv_tb_frac = 1-tb_frac;
            const simd4f _x_scale = 4*x_scale;
            simd4f _x(x, x+x_scale, x+2*x_scale, x+3*x_scale);
            long c = 0;
805
            for (;; c+=4)
806
807
            {
                _x += _x_scale;
808
                simd4i left = simd4i(_x);
809

810
                simd4f _lr_frac = _x-left;
811
812
813
814
815
816
817
818
819
820
821
822
823
                simd4f _inv_lr_frac = 1-_lr_frac; 
                simd4i right = left+1;

                simd4f tlf = _inv_tb_frac*_inv_lr_frac;
                simd4f trf = _inv_tb_frac*_lr_frac;
                simd4f blf = _tb_frac*_inv_lr_frac;
                simd4f brf = _tb_frac*_lr_frac;

                int32 fleft[4];
                int32 fright[4];
                left.store(fleft);
                right.store(fright);

824
825
                if (fright[3] >= in_img.nc())
                    break;
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
                simd4f tl(in_img[top][fleft[0]],     in_img[top][fleft[1]],     in_img[top][fleft[2]],     in_img[top][fleft[3]]);
                simd4f tr(in_img[top][fright[0]],    in_img[top][fright[1]],    in_img[top][fright[2]],    in_img[top][fright[3]]);
                simd4f bl(in_img[bottom][fleft[0]],  in_img[bottom][fleft[1]],  in_img[bottom][fleft[2]],  in_img[bottom][fleft[3]]);
                simd4f br(in_img[bottom][fright[0]], in_img[bottom][fright[1]], in_img[bottom][fright[2]], in_img[bottom][fright[3]]);

                simd4i out = simd4i(tlf*tl + trf*tr + blf*bl + brf*br);
                int32 fout[4];
                out.store(fout);

                out_img[r][c]   = static_cast<T>(fout[0]);
                out_img[r][c+1] = static_cast<T>(fout[1]);
                out_img[r][c+2] = static_cast<T>(fout[2]);
                out_img[r][c+3] = static_cast<T>(fout[3]);
            }
            x = -x_scale + c*x_scale;
            for (; c < out_img.nc(); ++c)
            {
                x += x_scale;
                const long left   = static_cast<long>(std::floor(x));
                const long right  = std::min(left+1, in_img.nc()-1);
                const float lr_frac = x - left;

                float tl = 0, tr = 0, bl = 0, br = 0;

                assign_pixel(tl, in_img[top][left]);
                assign_pixel(tr, in_img[top][right]);
                assign_pixel(bl, in_img[bottom][left]);
                assign_pixel(br, in_img[bottom][right]);

                float temp = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + 
                    tb_frac*((1-lr_frac)*bl + lr_frac*br);

                assign_pixel(out_img[r][c], temp);
            }
        }
    }

// ----------------------------------------------------------------------------------------

    template <
        typename image_type
        >
    typename enable_if<is_rgb_image<image_type> >::type resize_image (
869
870
        const image_type& in_img_,
        image_type& out_img_,
871
872
873
874
        interpolate_bilinear
    )
    {
        // make sure requires clause is not broken
875
        DLIB_ASSERT( is_same_object(in_img_, out_img_) == false ,
876
877
            "\t void resize_image()"
            << "\n\t Invalid inputs were given to this function."
878
            << "\n\t is_same_object(in_img_, out_img_):  " << is_same_object(in_img_, out_img_)
879
            );
880
881
882
883

        const_image_view<image_type> in_img(in_img_);
        image_view<image_type> out_img(out_img_);

884
885
886
887
888
889
890
        if (out_img.nr() <= 1 || out_img.nc() <= 1)
        {
            assign_all_pixels(out_img, 0);
            return;
        }


891
        typedef typename image_traits<image_type>::pixel_type T;
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
        const double x_scale = (in_img.nc()-1)/(double)std::max<long>((out_img.nc()-1),1);
        const double y_scale = (in_img.nr()-1)/(double)std::max<long>((out_img.nr()-1),1);
        double y = -y_scale;
        for (long r = 0; r < out_img.nr(); ++r)
        {
            y += y_scale;
            const long top    = static_cast<long>(std::floor(y));
            const long bottom = std::min(top+1, in_img.nr()-1);
            const double tb_frac = y - top;
            double x = -4*x_scale;

            const simd4f _tb_frac = tb_frac;
            const simd4f _inv_tb_frac = 1-tb_frac;
            const simd4f _x_scale = 4*x_scale;
            simd4f _x(x, x+x_scale, x+2*x_scale, x+3*x_scale);
            long c = 0;
908
            for (;; c+=4)
909
910
            {
                _x += _x_scale;
911
912
                simd4i left = simd4i(_x);
                simd4f lr_frac = _x-left;
913
914
915
916
917
918
919
920
921
922
923
924
925
                simd4f _inv_lr_frac = 1-lr_frac; 
                simd4i right = left+1;

                simd4f tlf = _inv_tb_frac*_inv_lr_frac;
                simd4f trf = _inv_tb_frac*lr_frac;
                simd4f blf = _tb_frac*_inv_lr_frac;
                simd4f brf = _tb_frac*lr_frac;

                int32 fleft[4];
                int32 fright[4];
                left.store(fleft);
                right.store(fright);

926
927
                if (fright[3] >= in_img.nc())
                    break;
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
                simd4f tl(in_img[top][fleft[0]].red,     in_img[top][fleft[1]].red,     in_img[top][fleft[2]].red,     in_img[top][fleft[3]].red);
                simd4f tr(in_img[top][fright[0]].red,    in_img[top][fright[1]].red,    in_img[top][fright[2]].red,    in_img[top][fright[3]].red);
                simd4f bl(in_img[bottom][fleft[0]].red,  in_img[bottom][fleft[1]].red,  in_img[bottom][fleft[2]].red,  in_img[bottom][fleft[3]].red);
                simd4f br(in_img[bottom][fright[0]].red, in_img[bottom][fright[1]].red, in_img[bottom][fright[2]].red, in_img[bottom][fright[3]].red);

                simd4i out = simd4i(tlf*tl + trf*tr + blf*bl + brf*br);
                int32 fout[4];
                out.store(fout);

                out_img[r][c].red   = static_cast<unsigned char>(fout[0]);
                out_img[r][c+1].red = static_cast<unsigned char>(fout[1]);
                out_img[r][c+2].red = static_cast<unsigned char>(fout[2]);
                out_img[r][c+3].red = static_cast<unsigned char>(fout[3]);


                tl = simd4f(in_img[top][fleft[0]].green,    in_img[top][fleft[1]].green,    in_img[top][fleft[2]].green,    in_img[top][fleft[3]].green);
                tr = simd4f(in_img[top][fright[0]].green,   in_img[top][fright[1]].green,   in_img[top][fright[2]].green,   in_img[top][fright[3]].green);
                bl = simd4f(in_img[bottom][fleft[0]].green, in_img[bottom][fleft[1]].green, in_img[bottom][fleft[2]].green, in_img[bottom][fleft[3]].green);
                br = simd4f(in_img[bottom][fright[0]].green, in_img[bottom][fright[1]].green, in_img[bottom][fright[2]].green, in_img[bottom][fright[3]].green);
                out = simd4i(tlf*tl + trf*tr + blf*bl + brf*br);
                out.store(fout);
                out_img[r][c].green   = static_cast<unsigned char>(fout[0]);
                out_img[r][c+1].green = static_cast<unsigned char>(fout[1]);
                out_img[r][c+2].green = static_cast<unsigned char>(fout[2]);
                out_img[r][c+3].green = static_cast<unsigned char>(fout[3]);


                tl = simd4f(in_img[top][fleft[0]].blue,     in_img[top][fleft[1]].blue,     in_img[top][fleft[2]].blue,     in_img[top][fleft[3]].blue);
                tr = simd4f(in_img[top][fright[0]].blue,    in_img[top][fright[1]].blue,    in_img[top][fright[2]].blue,    in_img[top][fright[3]].blue);
                bl = simd4f(in_img[bottom][fleft[0]].blue,  in_img[bottom][fleft[1]].blue,  in_img[bottom][fleft[2]].blue,  in_img[bottom][fleft[3]].blue);
                br = simd4f(in_img[bottom][fright[0]].blue, in_img[bottom][fright[1]].blue, in_img[bottom][fright[2]].blue, in_img[bottom][fright[3]].blue);
                out = simd4i(tlf*tl + trf*tr + blf*bl + brf*br);
                out.store(fout);
                out_img[r][c].blue   = static_cast<unsigned char>(fout[0]);
                out_img[r][c+1].blue = static_cast<unsigned char>(fout[1]);
                out_img[r][c+2].blue = static_cast<unsigned char>(fout[2]);
                out_img[r][c+3].blue = static_cast<unsigned char>(fout[3]);
            }
            x = -x_scale + c*x_scale;
            for (; c < out_img.nc(); ++c)
            {
                x += x_scale;
                const long left   = static_cast<long>(std::floor(x));
                const long right  = std::min(left+1, in_img.nc()-1);
                const double lr_frac = x - left;

                const T tl = in_img[top][left];
                const T tr = in_img[top][right];
                const T bl = in_img[bottom][left];
                const T br = in_img[bottom][right];

                T temp;
                assign_pixel(temp, 0);
                vector_to_pixel(temp, 
                    (1-tb_frac)*((1-lr_frac)*pixel_to_vector<double>(tl) + lr_frac*pixel_to_vector<double>(tr)) + 
                    tb_frac*((1-lr_frac)*pixel_to_vector<double>(bl) + lr_frac*pixel_to_vector<double>(br)));
                assign_pixel(out_img[r][c], temp);
            }
        }
    }

Davis King's avatar
Davis King committed
989
990
991
// ----------------------------------------------------------------------------------------

    template <
992
993
        typename image_type1,
        typename image_type2
Davis King's avatar
Davis King committed
994
995
        >
    void resize_image (
996
997
        const image_type1& in_img,
        image_type2& out_img
Davis King's avatar
Davis King committed
998
999
1000
1001
1002
1003
1004
1005
1006
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
            "\t void resize_image()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

1007
        resize_image(in_img, out_img, interpolate_bilinear());
Davis King's avatar
Davis King committed
1008
1009
    }

1010
1011
1012
// ----------------------------------------------------------------------------------------

    template <
1013
1014
        typename image_type1,
        typename image_type2
1015
        >
1016
    point_transform_affine flip_image_left_right (
1017
1018
        const image_type1& in_img,
        image_type2& out_img
1019
1020
    )
    {
Davis King's avatar
Davis King committed
1021
1022
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
1023
            "\t void flip_image_left_right()"
Davis King's avatar
Davis King committed
1024
1025
1026
1027
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

1028
        assign_image(out_img, fliplr(mat(in_img)));
1029
1030
1031
1032
1033
1034
1035
        std::vector<dlib::vector<double,2> > from, to;
        rectangle r = get_rect(in_img);
        from.push_back(r.tl_corner()); to.push_back(r.tr_corner());
        from.push_back(r.bl_corner()); to.push_back(r.br_corner());
        from.push_back(r.tr_corner()); to.push_back(r.tl_corner());
        from.push_back(r.br_corner()); to.push_back(r.bl_corner());
        return find_affine_transform(from,to);
1036
1037
1038
1039
1040
    }

// ----------------------------------------------------------------------------------------

    template <
1041
1042
        typename image_type1,
        typename image_type2
1043
1044
        >
    void flip_image_up_down (
1045
1046
        const image_type1& in_img,
        image_type2& out_img
1047
1048
    )
    {
Davis King's avatar
Davis King committed
1049
1050
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
1051
            "\t void flip_image_up_down()"
Davis King's avatar
Davis King committed
1052
1053
1054
1055
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

1056
        assign_image(out_img, flipud(mat(in_img)));
1057
1058
    }

1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
// ----------------------------------------------------------------------------------------

    namespace impl
    {
        inline rectangle flip_rect_left_right (
            const rectangle& rect,
            const rectangle& window 
        )
        {
            rectangle temp;
            temp.top() = rect.top();
            temp.bottom() = rect.bottom();

            const long left_dist = rect.left()-window.left();

            temp.right() = window.right()-left_dist; 
            temp.left()  = temp.right()-rect.width()+1; 
            return temp;
        }
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099

        inline rectangle tform_object (
            const point_transform_affine& tran,
            const rectangle& rect
        )
        {
            return centered_rect(tran(center(rect)), rect.width(), rect.height());
        }

        inline full_object_detection tform_object(
            const point_transform_affine& tran,
            const full_object_detection& obj
        )
        {
            std::vector<point> parts; 
            parts.reserve(obj.num_parts());
            for (unsigned long i = 0; i < obj.num_parts(); ++i)
            {
                parts.push_back(tran(obj.part(i)));
            }
            return full_object_detection(tform_object(tran,obj.get_rect()), parts);
        }
1100
1101
    }

1102
1103
// ----------------------------------------------------------------------------------------

1104
    template <
1105
        typename image_array_type,
1106
        typename T
1107
1108
        >
    void add_image_left_right_flips (
1109
        image_array_type& images,
1110
        std::vector<std::vector<T> >& objects
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size(),
            "\t void add_image_left_right_flips()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():  " << images.size() 
            << "\n\t objects.size(): " << objects.size() 
            );

1121
        typename image_array_type::value_type temp;
1122
        std::vector<T> rects;
1123
1124
1125
1126

        const unsigned long num = images.size();
        for (unsigned long j = 0; j < num; ++j)
        {
1127
            const point_transform_affine tran = flip_image_left_right(images[j], temp);
1128
1129
1130

            rects.clear();
            for (unsigned long i = 0; i < objects[j].size(); ++i)
1131
                rects.push_back(impl::tform_object(tran, objects[j][i]));
1132
1133
1134

            images.push_back(temp);
            objects.push_back(rects);
1135
1136
1137
1138
1139
1140
        }
    }

// ----------------------------------------------------------------------------------------

    template <
1141
        typename image_array_type,
1142
1143
        typename T,
        typename U
1144
1145
        >
    void add_image_left_right_flips (
1146
        image_array_type& images,
1147
1148
        std::vector<std::vector<T> >& objects,
        std::vector<std::vector<U> >& objects2
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size() &&
                     images.size() == objects2.size(),
            "\t void add_image_left_right_flips()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            << "\n\t objects2.size(): " << objects2.size() 
            );

1161
        typename image_array_type::value_type temp;
1162
1163
        std::vector<T> rects;
        std::vector<U> rects2;
1164
1165
1166
1167

        const unsigned long num = images.size();
        for (unsigned long j = 0; j < num; ++j)
        {
1168
            const point_transform_affine tran = flip_image_left_right(images[j], temp);
1169
1170
1171
1172
            images.push_back(temp);

            rects.clear();
            for (unsigned long i = 0; i < objects[j].size(); ++i)
1173
                rects.push_back(impl::tform_object(tran, objects[j][i]));
1174
1175
            objects.push_back(rects);

1176
            rects2.clear();
1177
            for (unsigned long i = 0; i < objects2[j].size(); ++i)
1178
1179
                rects2.push_back(impl::tform_object(tran, objects2[j][i]));
            objects2.push_back(rects2);
1180
1181
1182
        }
    }

1183
1184
// ----------------------------------------------------------------------------------------

1185
    template <typename image_array_type>
1186
    void flip_image_dataset_left_right (
1187
        image_array_type& images, 
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
        std::vector<std::vector<rectangle> >& objects
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size(),
            "\t void flip_image_dataset_left_right()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            );

1199
        typename image_array_type::value_type temp;
1200
1201
1202
        for (unsigned long i = 0; i < images.size(); ++i)
        {
            flip_image_left_right(images[i], temp); 
1203
            swap(temp,images[i]);
1204
1205
1206
1207
1208
1209
1210
1211
1212
            for (unsigned long j = 0; j < objects[i].size(); ++j)
            {
                objects[i][j] = impl::flip_rect_left_right(objects[i][j], get_rect(images[i]));
            }
        }
    }

// ----------------------------------------------------------------------------------------

1213
    template <typename image_array_type>
1214
    void flip_image_dataset_left_right (
1215
        image_array_type& images, 
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
        std::vector<std::vector<rectangle> >& objects,
        std::vector<std::vector<rectangle> >& objects2
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size() &&
                     images.size() == objects2.size(),
            "\t void flip_image_dataset_left_right()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            << "\n\t objects2.size(): " << objects2.size() 
            );

1230
        typename image_array_type::value_type temp;
1231
1232
1233
        for (unsigned long i = 0; i < images.size(); ++i)
        {
            flip_image_left_right(images[i], temp); 
1234
            swap(temp, images[i]);
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
            for (unsigned long j = 0; j < objects[i].size(); ++j)
            {
                objects[i][j] = impl::flip_rect_left_right(objects[i][j], get_rect(images[i]));
            }
            for (unsigned long j = 0; j < objects2[i].size(); ++j)
            {
                objects2[i][j] = impl::flip_rect_left_right(objects2[i][j], get_rect(images[i]));
            }
        }
    }

// ----------------------------------------------------------------------------------------

    template <
        typename pyramid_type,
1250
        typename image_array_type
1251
1252
        >
    void upsample_image_dataset (
1253
        image_array_type& images,
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
        std::vector<std::vector<rectangle> >& objects
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size(),
            "\t void upsample_image_dataset()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            );

1265
        typename image_array_type::value_type temp;
1266
1267
1268
1269
        pyramid_type pyr;
        for (unsigned long i = 0; i < images.size(); ++i)
        {
            pyramid_up(images[i], temp, pyr);
1270
            swap(temp, images[i]);
1271
1272
1273
1274
1275
1276
1277
1278
1279
            for (unsigned long j = 0; j < objects[i].size(); ++j)
            {
                objects[i][j] = pyr.rect_up(objects[i][j]);
            }
        }
    }

    template <
        typename pyramid_type,
1280
        typename image_array_type
1281
1282
        >
    void upsample_image_dataset (
1283
        image_array_type& images,
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
        std::vector<std::vector<rectangle> >& objects,
        std::vector<std::vector<rectangle> >& objects2 
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size() &&
                     images.size() == objects2.size(),
            "\t void upsample_image_dataset()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            << "\n\t objects2.size(): " << objects2.size() 
            );

1298
        typename image_array_type::value_type temp;
1299
1300
1301
1302
        pyramid_type pyr;
        for (unsigned long i = 0; i < images.size(); ++i)
        {
            pyramid_up(images[i], temp, pyr);
1303
            swap(temp, images[i]);
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
            for (unsigned long j = 0; j < objects[i].size(); ++j)
            {
                objects[i][j] = pyr.rect_up(objects[i][j]);
            }
            for (unsigned long j = 0; j < objects2[i].size(); ++j)
            {
                objects2[i][j] = pyr.rect_up(objects2[i][j]);
            }
        }
    }

// ----------------------------------------------------------------------------------------

1317
    template <typename image_array_type>
1318
1319
    void rotate_image_dataset (
        double angle,
1320
        image_array_type& images,
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
        std::vector<std::vector<rectangle> >& objects
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size(),
            "\t void rotate_image_dataset()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            );

1332
        typename image_array_type::value_type temp;
1333
1334
        for (unsigned long i = 0; i < images.size(); ++i)
        {
1335
            const point_transform_affine tran = rotate_image(images[i], temp, angle);
1336
            swap(temp, images[i]);
1337
1338
1339
1340
1341
1342
1343
1344
            for (unsigned long j = 0; j < objects[i].size(); ++j)
            {
                const rectangle rect = objects[i][j];
                objects[i][j] = centered_rect(tran(center(rect)), rect.width(), rect.height());
            }
        }
    }

1345
    template <typename image_array_type>
1346
1347
    void rotate_image_dataset (
        double angle,
1348
        image_array_type& images,
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
        std::vector<std::vector<rectangle> >& objects,
        std::vector<std::vector<rectangle> >& objects2
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( images.size() == objects.size() &&
                     images.size() == objects2.size(),
            "\t void rotate_image_dataset()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t images.size():   " << images.size() 
            << "\n\t objects.size():  " << objects.size() 
            << "\n\t objects2.size(): " << objects2.size() 
            );

1363
        typename image_array_type::value_type temp;
1364
1365
        for (unsigned long i = 0; i < images.size(); ++i)
        {
1366
            const point_transform_affine tran = rotate_image(images[i], temp, angle);
1367
            swap(temp, images[i]);
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
            for (unsigned long j = 0; j < objects[i].size(); ++j)
            {
                const rectangle rect = objects[i][j];
                objects[i][j] = centered_rect(tran(center(rect)), rect.width(), rect.height());
            }
            for (unsigned long j = 0; j < objects2[i].size(); ++j)
            {
                const rectangle rect = objects2[i][j];
                objects2[i][j] = centered_rect(tran(center(rect)), rect.width(), rect.height());
            }
        }
    }

1381
1382
1383
// ----------------------------------------------------------------------------------------

    template <
1384
        typename image_array_type, 
1385
1386
1387
1388
1389
1390
        typename EXP, 
        typename T, 
        typename U
        >
    void add_image_rotations (
        const matrix_exp<EXP>& angles,
1391
        image_array_type& images,
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
        std::vector<std::vector<T> >& objects,
        std::vector<std::vector<U> >& objects2
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( is_vector(angles) && angles.size() > 0 && 
                     images.size() == objects.size() &&
                     images.size() == objects2.size(),
            "\t void add_image_rotations()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_vector(angles): " << is_vector(angles) 
            << "\n\t angles.size():     " << angles.size() 
            << "\n\t images.size():     " << images.size() 
            << "\n\t objects.size():    " << objects.size() 
            << "\n\t objects2.size():   " << objects2.size() 
            );

1409
        image_array_type new_images;
1410
1411
1412
1413
1414
1415
1416
        std::vector<std::vector<T> > new_objects;
        std::vector<std::vector<U> > new_objects2;

        using namespace impl; 

        std::vector<T> objtemp;
        std::vector<U> objtemp2;
1417
        typename image_array_type::value_type temp;
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
        for (long i = 0; i < angles.size(); ++i)
        {
            for (unsigned long j = 0; j < images.size(); ++j)
            {
                const point_transform_affine tran = rotate_image(images[j], temp, angles(i));
                new_images.push_back(temp);

                objtemp.clear();
                for (unsigned long k = 0; k < objects[j].size(); ++k)
                    objtemp.push_back(tform_object(tran, objects[j][k]));
                new_objects.push_back(objtemp);

                objtemp2.clear();
                for (unsigned long k = 0; k < objects2[j].size(); ++k)
                    objtemp2.push_back(tform_object(tran, objects2[j][k]));
                new_objects2.push_back(objtemp2);
            }
        }

        new_images.swap(images);
        new_objects.swap(objects);
        new_objects2.swap(objects2);
    }

// ----------------------------------------------------------------------------------------

    template <
1445
        typename image_array_type, 
1446
1447
1448
1449
1450
        typename EXP,
        typename T
        >
    void add_image_rotations (
        const matrix_exp<EXP>& angles,
1451
        image_array_type& images,
1452
1453
1454
1455
1456
1457
1458
        std::vector<std::vector<T> >& objects
    )
    {
        std::vector<std::vector<T> > objects2(objects.size());
        add_image_rotations(angles, images, objects, objects2);
    }

1459
// ----------------------------------------------------------------------------------------
Davis King's avatar
Davis King committed
1460
1461
1462
// ----------------------------------------------------------------------------------------

    template <
1463
1464
        typename image_type1,
        typename image_type2,
Davis King's avatar
Davis King committed
1465
1466
1467
1468
        typename pyramid_type,
        typename interpolation_type
        >
    void pyramid_up (
1469
1470
        const image_type1& in_img,
        image_type2& out_img,
Davis King's avatar
Davis King committed
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
        const pyramid_type& pyr,
        const interpolation_type& interp
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
            "\t void pyramid_up()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

1482
        if (image_size(in_img) == 0)
Davis King's avatar
Davis King committed
1483
        {
1484
            set_image_size(out_img, 0, 0);
Davis King's avatar
Davis King committed
1485
1486
1487
1488
            return;
        }

        rectangle rect = get_rect(in_img);
1489
        rectangle uprect = pyr.rect_up(rect);
1490
1491
        if (uprect.is_empty())
        {
1492
            set_image_size(out_img, 0, 0);
1493
1494
            return;
        }
1495
        set_image_size(out_img, uprect.bottom()+1, uprect.right()+1);
Davis King's avatar
Davis King committed
1496

1497
        resize_image(in_img, out_img, interp);
Davis King's avatar
Davis King committed
1498
1499
1500
1501
1502
    }

// ----------------------------------------------------------------------------------------

    template <
1503
1504
        typename image_type1,
        typename image_type2,
Davis King's avatar
Davis King committed
1505
1506
1507
        typename pyramid_type
        >
    void pyramid_up (
1508
1509
        const image_type1& in_img,
        image_type2& out_img,
1510
        const pyramid_type& pyr
Davis King's avatar
Davis King committed
1511
1512
1513
1514
1515
1516
1517
1518
1519
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( is_same_object(in_img, out_img) == false ,
            "\t void pyramid_up()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t is_same_object(in_img, out_img):  " << is_same_object(in_img, out_img)
            );

1520
        pyramid_up(in_img, out_img, pyr, interpolate_bilinear());
Davis King's avatar
Davis King committed
1521
1522
    }

1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
// ----------------------------------------------------------------------------------------

    template <
        typename image_type,
        typename pyramid_type
        >
    void pyramid_up (
        image_type& img,
        const pyramid_type& pyr
    )
    {
        image_type temp;
        pyramid_up(img, temp, pyr);
1536
        swap(temp, img);
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
    }

// ----------------------------------------------------------------------------------------

    template <
        typename image_type
        >
    void pyramid_up (
        image_type& img
    )
    {
        pyramid_down<2> pyr;
        pyramid_up(img, pyr);
    }

Davis King's avatar
Davis King committed
1552
1553
1554
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
    struct chip_dims
    {
        chip_dims (
            unsigned long rows_,
            unsigned long cols_
        ) : rows(rows_), cols(cols_) { }

        unsigned long rows;
        unsigned long cols;
    };

Davis King's avatar
Davis King committed
1566
1567
    struct chip_details
    {
1568
        chip_details() : angle(0), rows(0), cols(0) {}
1569
        chip_details(const rectangle& rect_) : rect(rect_),angle(0), rows(rect_.height()), cols(rect_.width()) {}
1570
1571
        chip_details(const drectangle& rect_) : rect(rect_),angle(0), 
          rows((unsigned long)(rect_.height()+0.5)), cols((unsigned long)(rect_.width()+0.5)) {}
1572
        chip_details(const drectangle& rect_, unsigned long size) : rect(rect_),angle(0) 
1573
        { compute_dims_from_size(size); }
1574
        chip_details(const drectangle& rect_, unsigned long size, double angle_) : rect(rect_),angle(angle_) 
1575
1576
        { compute_dims_from_size(size); }

1577
        chip_details(const drectangle& rect_, const chip_dims& dims) : 
1578
            rect(rect_),angle(0),rows(dims.rows), cols(dims.cols) {}
1579
        chip_details(const drectangle& rect_, const chip_dims& dims, double angle_) : 
1580
            rect(rect_),angle(angle_),rows(dims.rows), cols(dims.cols) {}
Davis King's avatar
Davis King committed
1581

1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
        template <typename T>
        chip_details(
            const std::vector<dlib::vector<T,2> >& chip_points,
            const std::vector<dlib::vector<T,2> >& img_points,
            const chip_dims& dims
        ) : 
            rows(dims.rows), cols(dims.cols) 
        {
            DLIB_CASSERT( chip_points.size() == img_points.size() && chip_points.size() >= 2,
                "\t chip_details::chip_details(chip_points,img_points,dims)"
                << "\n\t Invalid inputs were given to this function."
                << "\n\t chip_points.size(): " << chip_points.size() 
                << "\n\t img_points.size():  " << img_points.size() 
            );

            const point_transform_affine tform = find_similarity_transform(chip_points,img_points);
            dlib::vector<double,2> p(1,0);
            p = tform.get_m()*p;

            // There are only 3 things happening in a similarity transform.  There is a
            // rescaling, a rotation, and a translation.  So here we pick out the scale and
            // rotation parameters.
            angle = std::atan2(p.y(),p.x());
            // Note that the translation and scale part are represented by the extraction
            // rectangle.  So here we build the appropriate rectangle.
            const double scale = length(p); 
1608
1609
1610
            rect = centered_drect(tform(point(dims.cols,dims.rows)/2.0), 
                                  dims.cols*scale, 
                                  dims.rows*scale);
1611
1612
1613
        }


1614
        drectangle rect;
Davis King's avatar
Davis King committed
1615
        double angle;
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
        unsigned long rows; 
        unsigned long cols;

        inline unsigned long size() const 
        {
            return rows*cols;
        }

    private:
        void compute_dims_from_size (
            unsigned long size
        ) 
        {
            const double relative_size = std::sqrt(size/(double)rect.area());
            rows = static_cast<unsigned long>(rect.height()*relative_size + 0.5);
            cols  = static_cast<unsigned long>(size/(double)rows + 0.5);
1632
1633
            rows = std::max(1ul,rows);
            cols = std::max(1ul,cols);
1634
        }
Davis King's avatar
Davis King committed
1635
1636
    };

Davis King's avatar
Davis King committed
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
// ----------------------------------------------------------------------------------------

    inline point_transform_affine get_mapping_to_chip (
        const chip_details& details
    )
    {
        std::vector<dlib::vector<double,2> > from, to;
        point p1(0,0);
        point p2(details.cols-1,0);
        point p3(details.cols-1, details.rows-1);
        to.push_back(p1);  
        from.push_back(rotate_point<double>(center(details.rect),details.rect.tl_corner(),details.angle));
        to.push_back(p2);  
        from.push_back(rotate_point<double>(center(details.rect),details.rect.tr_corner(),details.angle));
        to.push_back(p3);  
        from.push_back(rotate_point<double>(center(details.rect),details.rect.br_corner(),details.angle));
1653
        return find_affine_transform(from, to);
Davis King's avatar
Davis King committed
1654
1655
    }

Davis King's avatar
Davis King committed
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
// ----------------------------------------------------------------------------------------

    inline full_object_detection map_det_to_chip(
        const full_object_detection& det,
        const chip_details& details
    )
    {
        point_transform_affine tform = get_mapping_to_chip(details);
        full_object_detection res(det);
        // map the parts
        for (unsigned long l = 0; l < det.num_parts(); ++l)
            res.part(l) = tform(det.part(l));
        // map the main rectangle
        rectangle rect;
        rect += tform(det.get_rect().tl_corner());
        rect += tform(det.get_rect().tr_corner());
        rect += tform(det.get_rect().bl_corner());
        rect += tform(det.get_rect().br_corner());
        res.get_rect() = rect;
        return res;
    }

1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
// ----------------------------------------------------------------------------------------

    namespace impl
    {
        template <
            typename image_type1,
            typename image_type2
            >
        void basic_extract_image_chip (
            const image_type1& img,
            const rectangle& location,
            image_type2& chip
        )
        /*!
            ensures
                - This function doesn't do any scaling or rotating. It just pulls out the
                  chip in the given rectangle.  This also means the output image has the
                  same dimensions as the location rectangle.
        !*/
        {
            const_image_view<image_type1> vimg(img);
            image_view<image_type2> vchip(chip);

            vchip.set_size(location.height(), location.width());

            // location might go outside img so clip it
            rectangle area = location.intersect(get_rect(img));

            // find the part of the chip that corresponds to area in img.
            rectangle chip_area = translate_rect(area, -location.tl_corner());

            zero_border_pixels(chip, chip_area);
            // now pull out the contents of area/chip_area.
            for (long r = chip_area.top(), rr = area.top(); r <= chip_area.bottom(); ++r,++rr)
            {
                for (long c = chip_area.left(), cc = area.left(); c <= chip_area.right(); ++c,++cc)
                {
                    assign_pixel(vchip[r][c], vimg[rr][cc]);
                }
            }
        }
    }

Davis King's avatar
Davis King committed
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
// ----------------------------------------------------------------------------------------

    template <
        typename image_type1,
        typename image_type2
        >
    void extract_image_chips (
        const image_type1& img,
        const std::vector<chip_details>& chip_locations,
        dlib::array<image_type2>& chips
    )
    {
        // make sure requires clause is not broken
1734
#ifdef ENABLE_ASSERTS
Davis King's avatar
Davis King committed
1735
1736
        for (unsigned long i = 0; i < chip_locations.size(); ++i)
        {
1737
            DLIB_CASSERT(chip_locations[i].size() != 0 &&
Davis King's avatar
Davis King committed
1738
1739
1740
                         chip_locations[i].rect.is_empty() == false,
            "\t void extract_image_chips()"
            << "\n\t Invalid inputs were given to this function."
1741
            << "\n\t chip_locations["<<i<<"].size():            " << chip_locations[i].size()
1742
1743
            << "\n\t chip_locations["<<i<<"].rect.is_empty(): " << chip_locations[i].rect.is_empty()
            );
Davis King's avatar
Davis King committed
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
        }
#endif 

        pyramid_down<2> pyr;
        long max_depth = 0;
        // If the chip is supposed to be much smaller than the source subwindow then you
        // can't just extract it using bilinear interpolation since at a high enough
        // downsampling amount it would effectively turn into nearest neighbor
        // interpolation.  So we use an image pyramid to make sure the interpolation is
        // fast but also high quality.  The first thing we do is figure out how deep the
        // image pyramid needs to be.
1755
        rectangle bounding_box;
Davis King's avatar
Davis King committed
1756
1757
1758
        for (unsigned long i = 0; i < chip_locations.size(); ++i)
        {
            long depth = 0;
1759
            double grow = 2;
1760
            drectangle rect = pyr.rect_down(chip_locations[i].rect);
1761
            while (rect.area() > chip_locations[i].size())
Davis King's avatar
Davis King committed
1762
1763
1764
            {
                rect = pyr.rect_down(rect);
                ++depth;
1765
1766
1767
                // We drop the image size by a factor of 2 each iteration and then assume a
                // border of 2 pixels is needed to avoid any border effects of the crop.
                grow = grow*2 + 2;
Davis King's avatar
Davis King committed
1768
            }
1769
1770
1771
1772
1773
1774
1775
            drectangle rot_rect;
            const vector<double,2> cent = center(chip_locations[i].rect);
            rot_rect += rotate_point<double>(cent,chip_locations[i].rect.tl_corner(),chip_locations[i].angle);
            rot_rect += rotate_point<double>(cent,chip_locations[i].rect.tr_corner(),chip_locations[i].angle);
            rot_rect += rotate_point<double>(cent,chip_locations[i].rect.bl_corner(),chip_locations[i].angle);
            rot_rect += rotate_point<double>(cent,chip_locations[i].rect.br_corner(),chip_locations[i].angle);
            bounding_box += grow_rect(rot_rect, grow).intersect(get_rect(img));
Davis King's avatar
Davis King committed
1776
1777
            max_depth = std::max(depth,max_depth);
        }
1778
1779
        //std::cout << "max_depth: " << max_depth << std::endl;
        //std::cout << "crop amount: " << bounding_box.area()/(double)get_rect(img).area() << std::endl;
Davis King's avatar
Davis King committed
1780
1781

        // now make an image pyramid
1782
        dlib::array<array2d<typename image_traits<image_type1>::pixel_type> > levels(max_depth);
Davis King's avatar
Davis King committed
1783
        if (levels.size() != 0)
1784
            pyr(sub_image(img,bounding_box),levels[0]);
Davis King's avatar
Davis King committed
1785
1786
1787
1788
1789
1790
1791
1792
1793
        for (unsigned long i = 1; i < levels.size(); ++i)
            pyr(levels[i-1],levels[i]);

        std::vector<dlib::vector<double,2> > from, to;

        // now pull out the chips
        chips.resize(chip_locations.size());
        for (unsigned long i = 0; i < chips.size(); ++i)
        {
1794
1795
1796
1797
1798
            // If the chip doesn't have any rotation or scaling then use the basic version
            // of chip extraction that just does a fast copy.
            if (chip_locations[i].angle == 0 && 
                chip_locations[i].rows == chip_locations[i].rect.height() &&
                chip_locations[i].cols == chip_locations[i].rect.width())
Davis King's avatar
Davis King committed
1799
            {
1800
                impl::basic_extract_image_chip(img, chip_locations[i].rect, chips[i]);
Davis King's avatar
Davis King committed
1801
1802
            }
            else
1803
1804
1805
1806
1807
            {
                set_image_size(chips[i], chip_locations[i].rows, chip_locations[i].cols);

                // figure out which level in the pyramid to use to extract the chip
                int level = -1;
1808
                drectangle rect = translate_rect(chip_locations[i].rect, -bounding_box.tl_corner());
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
                while (pyr.rect_down(rect).area() > chip_locations[i].size())
                {
                    ++level;
                    rect = pyr.rect_down(rect);
                }

                // find the appropriate transformation that maps from the chip to the input
                // image
                from.clear();
                to.clear();
                from.push_back(get_rect(chips[i]).tl_corner());  to.push_back(rotate_point<double>(center(rect),rect.tl_corner(),chip_locations[i].angle));
                from.push_back(get_rect(chips[i]).tr_corner());  to.push_back(rotate_point<double>(center(rect),rect.tr_corner(),chip_locations[i].angle));
                from.push_back(get_rect(chips[i]).bl_corner());  to.push_back(rotate_point<double>(center(rect),rect.bl_corner(),chip_locations[i].angle));
1822
                point_transform_affine trns = find_affine_transform(from,to);
1823
1824
1825

                // now extract the actual chip
                if (level == -1)
1826
                    transform_image(sub_image(img,bounding_box),chips[i],interpolate_bilinear(),trns);
1827
1828
1829
                else
                    transform_image(levels[level],chips[i],interpolate_bilinear(),trns);
            }
Davis King's avatar
Davis King committed
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
        }
    }

// ----------------------------------------------------------------------------------------

    template <
        typename image_type1,
        typename image_type2
        >
    void extract_image_chip (
        const image_type1& img,
        const chip_details& location,
        image_type2& chip
    )
    {
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
        // If the chip doesn't have any rotation or scaling then use the basic version of
        // chip extraction that just does a fast copy.
        if (location.angle == 0 && 
            location.rows == location.rect.height() &&
            location.cols == location.rect.width())
        {
            impl::basic_extract_image_chip(img, location.rect, chip);
        }
        else
        {
            std::vector<chip_details> chip_locations(1,location);
            dlib::array<image_type2> chips;
            extract_image_chips(img, chip_locations, chips);
            swap(chips[0], chip);
        }
Davis King's avatar
Davis King committed
1860
1861
    }

Davis King's avatar
Davis King committed
1862
1863
1864
1865
// ----------------------------------------------------------------------------------------

    inline chip_details get_face_chip_details (
        const full_object_detection& det,
1866
        const unsigned long size = 200,
Davis King's avatar
Davis King committed
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
        const double padding = 0.2
    )
    {
        DLIB_CASSERT(det.num_parts() == 68,
            "\t chip_details get_face_chip_details()"
            << "\n\t You must give a detection with exactly 68 parts in it."
            << "\n\t det.num_parts(): " << det.num_parts()
        );
        DLIB_CASSERT(padding >= 0 && size > 0,
            "\t chip_details get_face_chip_details()"
            << "\n\t Invalid inputs were given to this function."
            << "\n\t padding: " << padding 
            << "\n\t size:    " << size 
            );

        // Average positions of face points 17-67
        const double mean_face_shape_x[] = {
            0.000213256, 0.0752622, 0.18113, 0.29077, 0.393397, 0.586856, 0.689483, 0.799124,
            0.904991, 0.98004, 0.490127, 0.490127, 0.490127, 0.490127, 0.36688, 0.426036,
            0.490127, 0.554217, 0.613373, 0.121737, 0.187122, 0.265825, 0.334606, 0.260918,
            0.182743, 0.645647, 0.714428, 0.793132, 0.858516, 0.79751, 0.719335, 0.254149,
            0.340985, 0.428858, 0.490127, 0.551395, 0.639268, 0.726104, 0.642159, 0.556721,
            0.490127, 0.423532, 0.338094, 0.290379, 0.428096, 0.490127, 0.552157, 0.689874,
            0.553364, 0.490127, 0.42689
        };
        const double mean_face_shape_y[] = {
            0.106454, 0.038915, 0.0187482, 0.0344891, 0.0773906, 0.0773906, 0.0344891,
            0.0187482, 0.038915, 0.106454, 0.203352, 0.307009, 0.409805, 0.515625, 0.587326,
            0.609345, 0.628106, 0.609345, 0.587326, 0.216423, 0.178758, 0.179852, 0.231733,
            0.245099, 0.244077, 0.231733, 0.179852, 0.178758, 0.216423, 0.244077, 0.245099,
            0.780233, 0.745405, 0.727388, 0.742578, 0.727388, 0.745405, 0.780233, 0.864805,
            0.902192, 0.909281, 0.902192, 0.864805, 0.784792, 0.778746, 0.785343, 0.778746,
            0.784792, 0.824182, 0.831803, 0.824182
        };

        COMPILE_TIME_ASSERT(sizeof(mean_face_shape_x)/sizeof(double) == 68-17);

        std::vector<dlib::vector<double,2> > from_points, to_points;
        for (unsigned long i = 17; i < det.num_parts(); ++i)
        {
1907
1908
1909
1910
1911
1912
1913
            // Ignore the lower lip
            if ((55 <= i && i <= 59) || (65 <= i && i <= 67))
                continue;
            // Ignore the eyebrows 
            if (17 <= i && i <= 26)
                continue;

Davis King's avatar
Davis King committed
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
            dlib::vector<double,2> p;
            p.x() = (padding+mean_face_shape_x[i-17])/(2*padding+1);
            p.y() = (padding+mean_face_shape_y[i-17])/(2*padding+1);
            from_points.push_back(p*size);
            to_points.push_back(det.part(i));
        }

        return chip_details(from_points, to_points, chip_dims(size,size));
    }

1924
1925
1926
1927
// ----------------------------------------------------------------------------------------

    inline std::vector<chip_details> get_face_chip_details (
        const std::vector<full_object_detection>& dets,
1928
        const unsigned long size = 200,
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
        const double padding = 0.2
    )
    {
        std::vector<chip_details> res;
        res.reserve(dets.size());
        for (unsigned long i = 0; i < dets.size(); ++i)
            res.push_back(get_face_chip_details(dets[i], size, padding));
        return res;
    }

1939
1940
1941
1942
// ----------------------------------------------------------------------------------------

}

1943
#endif // DLIB_INTERPOlATIONh_
1944