1
#ifndef __MOTION_SEARCHER_H__
2
#define __MOTION_SEARCHER_H__
4
// This file (C) 2004-2009 Steven Boswell. All rights reserved.
5
// Released to the public under the GNU General Public License v2.
6
// See the file COPYING for more information.
10
#include "mjpeg_types.h"
11
#include "TemplateLib.hh"
13
#include "DoublyLinkedList.hh"
14
#include "ReferenceFrame.hh"
15
#include "SetRegion2D.hh"
16
#include "BitmapRegion2D.hh"
19
// HACK: for development error messages.
24
// Define this to print region unions/subtractions.
26
// #define PRINTREGIONMATH
27
#endif // DEBUG_REGION2D
31
// Define this to print details of the search-border's progress.
33
// #define PRINT_SEARCHBORDER
34
#endif // DEBUG_REGION2D
38
// Define this to prevent reference-frame pixels from being used more
40
#define USE_REFERENCEFRAMEPIXELS_ONCE
44
// Define this to throttle the output of the pixel-sorter, using only
45
// those matches with the lowest sum-of-absolute-differences.
46
#define THROTTLE_PIXELSORTER_WITH_SAD
50
// Define this to implement set-regions with vectors.
51
// Don't define this to implement set-regions with skip-lists.
52
#define SET_REGION_IMPLEMENTED_WITH_VECTOR
56
// Define this to implement the SearchBorder's border-extent-boundary-sets
58
// Don't define this to implement the SearchBorder's
59
// border-extent-boundary-sets with skip-lists.
61
// (Even though this option is specific to the internals of SearchBorder,
62
// it's defined here, since all of our other configuration switches are
64
#define BORDEREXTENTBOUNDARYSET_IMPLEMENTED_WITH_VECTOR
68
// Define this to use bitmap regions to implement zero-motion
70
#define ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
74
// Define this to use bitmap regions to implement match-throttle
76
#define MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
80
// Define this to use bitmap regions to implement pruning flood-fill.
81
//#define PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
85
// Define this to use a bitmap region to implement the
86
// used-reference-pixel region. Don't define it to use a set-based
87
// region to implement the used-reference-pixel region.
88
#define USED_REFERENCE_PIXELS_REGION_IS_BITMAP
92
// We'll be using this variant of the search-border.
93
// (SearchBorder uses SET_REGION_IMPLEMENTED_WITH_VECTOR to configure.)
94
#include "SearchBorder.hh"
96
// We'll be using this variant of the search-window.
98
#define OPTIONALLY_SORT_PIXEL_GROUPS
99
#endif // EXPAND_REGIONS
100
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
101
#define CALCULATE_SAD
102
#endif // THROTTLE_PIXELSORTER_WITH_SAD
103
#include "SearchWindow.hh"
105
#undef OPTIONALLY_SORT_PIXEL_GROUPS
109
// The generic motion-searcher class. It's parameterized by the size of
110
// elements in the pixels, the dimension of the pixels, the numeric type
111
// to use in tolerance calculations, the numeric type to use for pixel
112
// indices, a numeric type big enough to hold the product of the largest
113
// expected frame width/height, the width/height of pixel groups to
114
// operate on, a numeric type big enough to hold pixel-dimension *
115
// pixel-group-width * pixel-group-height bits and serve as an array
116
// index, and the types of pixels, reference pixels, and reference
117
// frames to operate on. When constructed, it's configured with the
118
// number of frames over which to accumulate pixel values, the search
119
// radius (in separate x and y directions), the error tolerances, and
120
// throttle values for the number of matches and the size of matches.
122
// Pixel values are tracked over several frames. The idea is, if the
123
// motion searcher can prove that a particular pixel in several frames
124
// is really the same pixel, it can average together all the pixel's
125
// values to calculate a more accurate value for it. Therefore, output
126
// is delayed by the number of frames specified in the constructor, to
127
// give the motion-searcher the slack to do this.
129
// The motion-searcher works on groups of pixels. It iterates through a
130
// frame, looking for groups of pixels within the search radius that
131
// match the current group (within the error tolerance). It sorts the
132
// found pixel-groups by sum-of-absolute-differences, keeps the best
133
// match-count-throttle matches, and flood-fills each one, storing the
134
// results in the search-border. Future pixel-sorter matches are
135
// checked against this set, to filter out duplicate matches (and the
136
// associated flood-fills).
138
// Since this process can generate a lot of information, two throttles
139
// are used -- one on the number of matches, and one on the size of the
140
// largest match. If either is exceeded, the largest matches found are
141
// applied to the frame before searching finishes, until neither throttle
142
// is exceeded. This decreases the amount of work to finish the search,
143
// since some of the frame has been resolved already. If the throttle
144
// values are too low, quality may be impacted, but if the throttle
145
// values are too high, performance may be impacted.
147
// Similarly, if there are no matches for the current pixel-group, then
148
// it is considered new information, and new reference pixels are
149
// allocated for the group and applied to the new frame before the
150
// search is finished. However, these pixels may be overwritten later
151
// by regions that are applied to the frame. This attempts to improve
152
// the efficiency of processing frames that don't match the previous
153
// frame, in a way that doesn't impact quality.
155
// Once the entire frame has been searched, the found regions are
156
// applied to the frame, from largest match to smallest, and always
157
// flood-filled first to get rid of any resolved pixels. If such a region
158
// is no longer the largest region, it's put back into the pool, and the
159
// largest remaining region is evaluated instead.
161
// Any areas of the frame not resolved by this method are new
162
// information, and new reference pixels are allocated for them.
163
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
164
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
166
class PIXEL = Pixel<PIXEL_NUM,DIM,PIXEL_TOL>,
168
= ReferencePixel<PIXEL_TOL,PIXEL_NUM,DIM,PIXEL>,
170
= ReferenceFrame<REFERENCEPIXEL,PIXELINDEX,FRAMESIZE> >
174
typedef PIXEL Pixel_t;
177
typedef REFERENCEPIXEL ReferencePixel_t;
178
// Our reference pixel type.
180
typedef REFERENCEFRAME ReferenceFrame_t;
181
// Our reference frame type.
183
typedef PIXEL_NUM PixelValue_t;
184
// The numeric type to use in pixel values, in each dimension
187
typedef PIXEL_TOL Tolerance_t;
188
// The numeric type to use in tolerance calculations.
191
// Default constructor.
193
virtual ~MotionSearcher();
196
void Init (Status_t &a_reStatus, int a_nFrames,
197
PIXELINDEX a_tnWidth, PIXELINDEX a_tnHeight,
198
PIXELINDEX a_tnSearchRadiusX, PIXELINDEX a_tnSearchRadiusY,
199
PixelValue_t a_nZeroTolerance, PixelValue_t a_nTolerance,
200
FRAMESIZE a_nMatchCountThrottle,
201
FRAMESIZE a_nMatchSizeThrottle);
202
// Initializer. Provide the number of frames over which to
203
// accumulate pixel data, the dimensions of the frames, the
204
// search radius, the error tolerances, and the match throttles.
206
const ReferenceFrame_t *GetFrameReadyForOutput (void);
207
// If a frame is ready to be output, return it, otherwise return
209
// Call this once before each call to AddFrame(), to ensure that
210
// AddFrame() has the space to accept another frame. Note that
211
// this implies the data in the returned frame will be
212
// invalidated by AddFrame().
214
void AddFrame (Status_t &a_reStatus, const Pixel_t *a_pPixels);
215
// Add another frame to be analyzed into the system.
216
// The digested version will eventually be returned by either
217
// GetFrameReadyForOutput() or GetRemainingFrames().
219
const ReferenceFrame_t *GetRemainingFrames (void);
220
// Once there is no more input, call this repeatedly to get the
221
// details of the remaining frames, until it returns NULL.
224
// Purge ourselves of temporary structures that aren't normally
225
// freed at the end of a frame. (Most are.)
226
// Should be called every once in a while (e.g. every 10 frames).
230
// The number of reference frames we use.
232
PIXELINDEX m_tnWidth;
233
PIXELINDEX m_tnHeight;
234
FRAMESIZE m_tnPixels;
235
// The dimensions of each reference frame.
237
PIXELINDEX m_tnSearchRadiusX, m_tnSearchRadiusY;
238
// The search radius, i.e. how far from the current pixel
239
// group we look when searching for possible moved instances of
242
Tolerance_t m_tnTolerance, m_tnTwiceTolerance;
243
// The error tolerance, i.e. the largest difference we're
244
// willing to tolerate between pixels before considering them
245
// to be the same pixel. Also, twice the tolerance.
247
Tolerance_t m_tnZeroTolerance;
248
// The error tolerance for the zero-motion pass.
250
FRAMESIZE m_nMatchCountThrottle;
251
// How many matches we're willing to have for the current
252
// pixel group before we just flood-fill the largest one and
255
FRAMESIZE m_nMatchSizeThrottle;
256
// The size (measured in number of pixels) that the largest
257
// region in the area of the current pixel-group can be before
258
// we just flood-fill the largest one and apply it now.
260
PixelAllocator<REFERENCEPIXEL,FRAMESIZE> m_oPixelPool;
261
// Our source for new reference pixels.
263
ReferenceFrame_t **m_ppFrames;
264
// Our reference frames; an array of pointers to frames.
266
int m_nFirstFrame, m_nLastFrame;
267
// The range of frames that contain useful info.
268
// When both equal 0, no frames contain useful info.
269
// When m_nFirstFrame is 0 and m_nLastFrame is m_nFrames,
270
// it's time for GetFrameReadyForOutput() to emit a frame.
271
// When m_nFirstFrame is greater than zero but less than
272
// m_nLastFrame, it means our client is calling
273
// GetRemainingFrames().
275
ReferenceFrame_t *m_pNewFrame;
276
// The reference-frame representation of the new frame.
277
// Points to one of the instances in m_ppFrames[].
279
ReferenceFrame_t *m_pReferenceFrame;
280
// The reference frame, against which the new frame is
282
// Points to one of the instances in m_ppFrames[].
284
const Pixel_t *m_pNewFramePixels;
285
// The pixels of the new frame (i.e. the raw version).
287
PIXELINDEX m_tnX, m_tnY;
288
// The index of the current pixel group. Actually the index
289
// of the top-left pixel in the current pixel group. This
290
// gets moved in a zigzag pattern, back and forth across the
291
// frame and then down, until the end of the frame is reached.
293
PIXELINDEX m_tnStepX;
294
// Whether we're zigging or zagging.
296
typedef Region2D<PIXELINDEX,FRAMESIZE> BaseRegion_t;
297
// The base class for all our region types.
299
#ifdef SET_REGION_IMPLEMENTED_WITH_VECTOR
300
typedef Vector<typename BaseRegion_t::Extent,
301
typename BaseRegion_t::Extent,
302
Ident<typename BaseRegion_t::Extent,
303
typename BaseRegion_t::Extent>,
304
Less<typename BaseRegion_t::Extent> >
306
#else // SET_REGION_IMPLEMENTED_WITH_VECTOR
307
typedef SkipList<typename BaseRegion_t::Extent,
308
typename BaseRegion_t::Extent,
309
Ident<typename BaseRegion_t::Extent,
310
typename BaseRegion_t::Extent>,
311
Less<typename BaseRegion_t::Extent> >
313
#endif // SET_REGION_IMPLEMENTED_WITH_VECTOR
314
// The container class that implements our set-based region.
316
typedef SetRegion2D<PIXELINDEX,FRAMESIZE,RegionImp_t> Region_t;
317
typedef typename Region_t::Allocator RegionAllocator_t;
318
// How we use SetRegion2D<>.
320
typedef BitmapRegion2D<PIXELINDEX,FRAMESIZE> BitmapRegion_t;
321
// How we use BitmapRegion2D<>.
323
typedef SearchBorder<PIXELINDEX,FRAMESIZE> BaseSearchBorder_t;
324
// The base class for the type of search-border we'll be using.
326
typedef typename BaseSearchBorder_t::MovedRegion MovedRegion;
327
// A moved region of pixels that has been detected.
329
RegionAllocator_t m_oRegionAllocator;
330
// Used by all our set-regions to allocate space for their extents.
332
typedef Set<MovedRegion *,
333
typename MovedRegion::SortBySizeThenMotionVectorLength>
335
typedef typename MovedRegionSet::Allocator MovedRegionAllocator_t;
336
MovedRegionAllocator_t m_oMovedRegionSetAllocator;
337
MovedRegionSet m_setRegions;
338
// All moving areas detected so far.
339
// Sorted by decreasing size, then increasing motion vector
340
// length, i.e. the order in which they should be applied to
341
// the reference-frame version of the new frame.
343
#ifdef USED_REFERENCE_PIXELS_REGION_IS_BITMAP
344
BitmapRegion_t m_oUsedReferencePixels;
345
#else // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
346
Region_t m_oUsedReferencePixels;
347
#endif // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
348
// The region describing all parts of the reference frame that
349
// have been found in the new frame.
351
#ifdef USED_REFERENCE_PIXELS_REGION_IS_BITMAP
352
BitmapRegion_t m_oTestedZeroMotionPixels;
353
#else // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
354
Region_t m_oTestedZeroMotionPixels;
355
#endif // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
356
// The region describing all parts of the reference frame that
357
// have been tested in the zero-motion pass.
359
MovedRegion m_oMatchThrottleRegion;
360
// The region that's applied to the frame before motion
361
// detection is finished. Allocated here to avoid lots of
362
// creation & destruction.
364
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
365
BitmapRegion_t m_oFloodFillRegion;
366
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
367
// The region that does the flood-filling for the
368
// match-throttle-region.
370
void ApplyRegionToNewFrame (Status_t &a_reStatus,
371
const MovedRegion &a_rRegion);
372
// Apply this region to the new frame.
374
typedef SearchWindow<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
375
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
376
REFERENCEFRAME> SearchWindow_t;
377
// The type of search-window we'll be using.
379
SearchWindow_t m_oSearchWindow;
380
// The search window. It contains all the cells needed to
381
// analyze the image.
383
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
385
// A pixel group that matches the current pixel-group, with the
386
// given sum-of-absolute-differences.
387
class MatchedPixelGroup
391
// The sum-of-absolute-differences.
393
const typename SearchWindow_t::PixelGroup *m_pGroup;
397
// Default constructor.
399
MatchedPixelGroup (Tolerance_t a_tnSAD,
400
const typename SearchWindow_t::PixelGroup *a_pGroup);
401
// Initializing constructor.
403
~MatchedPixelGroup();
406
// A comparison class, suitable for Set<>.
410
inline bool operator() (const MatchedPixelGroup &a_rLeft,
411
const MatchedPixelGroup &a_rRight) const;
415
typedef Set<MatchedPixelGroup,
416
typename MatchedPixelGroup::SortBySAD>
417
MatchedPixelGroupSet;
418
typedef typename MatchedPixelGroupSet::Allocator MPGS_Allocator_t;
419
// The type for a set of matched pixel-groups.
421
MPGS_Allocator_t m_oMatchAllocator;
422
MatchedPixelGroupSet m_setMatches;
423
// All the matches for the current pixel-group that we
426
#endif // THROTTLE_PIXELSORTER_WITH_SAD
428
// The search-border, specialized to put completed regions into
430
class SearchBorder_t : public BaseSearchBorder_t
433
typedef BaseSearchBorder_t BaseClass;
434
// Keep track of who our base class is.
437
SearchBorder_t (MovedRegionSet &a_rsetRegions,
438
typename MovedRegion::Allocator &a_rAlloc
439
= MovedRegion::Extents::Imp::sm_oNodeAllocator);
440
// Constructor. Provide a reference to the set where
441
// completed regions will be stored.
443
virtual void OnCompletedRegion (Status_t &a_reStatus,
444
typename SearchBorder_t::MovedRegion *a_pRegion);
445
// Tell BaseSearchBorder_t how to hand us a completed region.
448
MovedRegionSet &m_rsetRegions;
449
// A reference to our list of completed regions.
452
SearchBorder_t m_oSearchBorder;
453
// The search border, i.e. all regions on the border between
454
// the searched area and the not-yet-searched area, the regions
455
// still under construction.
457
FRAMESIZE SearchBorder_AddNewRegion (Status_t &a_reStatus,
458
PIXELINDEX a_tnMotionX, PIXELINDEX a_tnMotionY);
459
// Add a new region, with the given motion vector, to the
461
// Flood-fills its area before adding.
462
// Returns the size of the added region.
464
FRAMESIZE SearchBorder_MatchThrottle (Status_t &a_reStatus,
465
FRAMESIZE a_nMatchCount,
466
PIXELINDEX &a_rtnMotionX, PIXELINDEX &a_rtnMotionY);
467
// Get the best region that matched the current pixel-group
468
// (which is usually the largest active-region). Expand it as far
469
// as it can go, i.e. flood-fill in its area. If it's no longer
470
// the largest region, put it back and try again. Otherwise, apply
471
// it to the new frame now.
473
// Pass the number of pixel-group matches as an argument. If a
474
// best-region candidate is found to have no unresolved pixels,
475
// it's no longer considered a pixel-group match, and that may lead
476
// to the discovery that the match-count-throttle is no longer
479
// Returns the number of points flood-filled, and backpatches the
480
// motion vector associated with the best region.
481
// May return zero if analysis reveals that the match-throttles
482
// weren't really exceeded.
484
// A class that helps implement the zero-motion flood-fill.
485
class ZeroMotionFloodFillControl
486
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
487
: public BitmapRegion_t::FloodFillControl
488
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
489
: public Region_t::FloodFillControl
490
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
493
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
494
typedef typename BitmapRegion_t::FloodFillControl BaseClass;
495
// Keep track of who our base class is.
496
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
497
typedef typename Region_t::FloodFillControl BaseClass;
498
// Keep track of who our base class is.
499
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
501
MotionSearcher *m_pMotionSearcher;
502
// The motion-searcher we're working for.
505
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
506
ZeroMotionFloodFillControl();
507
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
508
ZeroMotionFloodFillControl
509
(typename BaseClass::Allocator &a_rAllocator
510
= Region_t::Extents::Imp::sm_oNodeAllocator);
511
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
512
// Default constructor. Must be followed by Init().
514
void Init (Status_t &a_reStatus,
515
MotionSearcher *a_pMotionSearcher);
518
// Redefined FloodFillControl methods.
520
bool ShouldUseExtent (typename
521
ZeroMotionFloodFillControl::BaseClass::Extent &a_rExtent);
522
// Return true if the flood-fill should examine the given
523
// extent. May modify the extent.
525
bool IsPointInRegion (PIXELINDEX a_tnX, PIXELINDEX a_tnY);
526
// Returns true if the given point should be included in the
530
friend class ZeroMotionFloodFillControl;
531
// Allow the zero-motion flood-fill-control class direct access.
533
ZeroMotionFloodFillControl m_oZeroMotionFloodFillControl;
534
// Used to implement flood-filling the result of the
535
// zero-motion search.
537
// A class that helps implement the match-throttle flood-fill.
538
class MatchThrottleFloodFillControl
539
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
540
: public BitmapRegion_t::FloodFillControl
541
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
542
: public Region_t::FloodFillControl
543
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
546
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
547
typedef typename BitmapRegion_t::FloodFillControl BaseClass;
548
// Keep track of who our base class is.
549
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
550
typedef typename Region_t::FloodFillControl BaseClass;
551
// Keep track of who our base class is.
552
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
554
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
555
MatchThrottleFloodFillControl();
556
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
557
MatchThrottleFloodFillControl
558
(typename BaseClass::Allocator &a_rAllocator
559
= Region_t::Extents::Imp::sm_oNodeAllocator);
560
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
561
// Default constructor. Must be followed by Init().
563
void Init (Status_t &a_reStatus,
564
MotionSearcher *a_pMotionSearcher);
567
void SetupForFloodFill (PIXELINDEX a_tnMotionX,
568
PIXELINDEX a_tnMotionY);
569
// Set up to to a flood-fill. Provide the motion vector.
570
// Call this before doing a flood-fill with this control
573
// Redefined FloodFillControl methods.
575
bool ShouldUseExtent (typename Region_t::Extent &a_rExtent);
576
// Return true if the flood-fill should examine the given
577
// extent. May modify the extent.
579
bool IsPointInRegion (PIXELINDEX a_tnX, PIXELINDEX a_tnY);
580
// Returns true if the given point should be included in the
584
MotionSearcher *m_pMotionSearcher;
585
// The motion-searcher we're working for.
587
PIXELINDEX m_tnMotionX, m_tnMotionY;
588
// The motion vector to be used for this flood-fill.
591
friend class MatchThrottleFloodFillControl;
592
// Allow the match-throttle flood-fill-control class direct access.
594
MatchThrottleFloodFillControl m_oMatchThrottleFloodFillControl;
595
// Used to implement flood-filling a region before
596
// motion-searching is done.
598
// A class that helps implement the pruning flood-fill, i.e. the one
599
// that removes resolved pixels from a candidate moved-region.
600
class PruningFloodFillControl
601
#ifdef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
602
: public BitmapRegion_t::FloodFillControl
603
#else // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
604
: public Region_t::FloodFillControl
605
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
608
#ifdef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
609
typedef typename BitmapRegion_t::FloodFillControl BaseClass;
610
// Keep track of who our base class is.
611
#else // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
612
typedef typename Region_t::FloodFillControl BaseClass;
613
// Keep track of who our base class is.
614
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
616
#ifdef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
617
PruningFloodFillControl();
618
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
619
PruningFloodFillControl
620
(typename BaseClass::Allocator &a_rAllocator
621
= Region_t::Extents::Imp::sm_oNodeAllocator);
622
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
623
// Default constructor. Must be followed by Init().
625
void Init (Status_t &a_reStatus,
626
MotionSearcher *a_pMotionSearcher);
629
void SetupForFloodFill (PIXELINDEX a_tnMotionX,
630
PIXELINDEX a_tnMotionY);
631
// Set up to to a flood-fill. Provide the motion vector.
632
// Call this before doing a flood-fill with this control
635
// Redefined FloodFillControl methods.
637
bool ShouldUseExtent (typename Region_t::Extent &a_rExtent);
638
// Return true if the flood-fill should examine the given
639
// extent. May modify the extent.
641
bool IsPointInRegion (PIXELINDEX a_tnX, PIXELINDEX a_tnY);
642
// Returns true if the given point should be included in the
646
MotionSearcher *m_pMotionSearcher;
647
// The motion-searcher we're working for.
649
PIXELINDEX m_tnMotionX, m_tnMotionY;
650
// The motion vector to be used for this flood-fill.
653
friend class PruningFloodFillControl;
654
// Allow the zero-motion flood-fill-control class direct access.
656
PruningFloodFillControl m_oPruningFloodFillControl;
657
// Used to implement flood-filling a region to remove any
663
// Default constructor.
664
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
665
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
666
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
667
class REFERENCEFRAME>
668
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
669
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
670
REFERENCEFRAME>::MotionSearcher()
671
: m_oRegionAllocator (1048576),
672
m_oMovedRegionSetAllocator (262144),
673
m_setRegions (typename MovedRegion::SortBySizeThenMotionVectorLength(),
674
m_oMovedRegionSetAllocator),
675
m_oMatchThrottleRegion (m_oRegionAllocator),
676
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
677
m_oMatchAllocator (65536),
678
m_setMatches (typename MatchedPixelGroup::SortBySAD(),
680
#endif // THROTTLE_PIXELSORTER_WITH_SAD
681
m_oSearchBorder (m_setRegions, m_oRegionAllocator)
682
#ifndef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
683
, m_oZeroMotionFloodFillControl (m_oRegionAllocator)
684
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
685
#ifndef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
686
, m_oMatchThrottleFloodFillControl (m_oRegionAllocator)
687
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
688
#ifndef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
689
, m_oPruningFloodFillControl (m_oRegionAllocator)
690
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
695
m_nFirstFrame = m_nLastFrame = 0;
696
m_tnWidth = m_tnHeight = PIXELINDEX (0);
697
m_tnPixels = FRAMESIZE (0);
699
// No information on the sort of search to do yet.
700
m_tnSearchRadiusX = m_tnSearchRadiusY = PIXELINDEX (0);
701
m_tnZeroTolerance = m_tnTolerance = m_tnTwiceTolerance
703
m_nMatchCountThrottle = 0;
704
m_nMatchSizeThrottle = 0;
706
// No active search yet.
707
m_tnX = m_tnY = m_tnStepX = PIXELINDEX (0);
709
m_pReferenceFrame = NULL;
710
m_pNewFramePixels = NULL;
716
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
717
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
718
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
719
class REFERENCEFRAME>
720
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
721
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
722
REFERENCEFRAME>::~MotionSearcher()
724
// Free up any remaining moved regions. (Testing for a non-zero
725
// size is defined to be safe even if the set hasn't been
726
// initialized, i.e. if we get destroyed before our Init() has been
728
if (m_setRegions.Size() > 0)
730
typename MovedRegionSet::Iterator itHere;
731
// The location of the next region to destroy.
733
// Loop through the moved-regions set, remove each item,
735
while (itHere = m_setRegions.Begin(),
736
itHere != m_setRegions.End())
738
// Get the moved-region to destroy.
739
MovedRegion *pRegion = *itHere;
741
// Remove it from the set.
742
m_setRegions.Erase (itHere);
744
// Destroy the region.
745
m_oSearchBorder.DeleteRegion (pRegion);
749
// Destroy the reference frames.
750
for (int i = 0; i < m_nFrames; i++)
752
m_ppFrames[i]->Reset();
753
delete m_ppFrames[i];
761
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
762
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
763
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
764
class REFERENCEFRAME>
766
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
767
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>::Init
768
(Status_t &a_reStatus, int a_nFrames, PIXELINDEX a_tnWidth,
769
PIXELINDEX a_tnHeight, PIXELINDEX a_tnSearchRadiusX,
770
PIXELINDEX a_tnSearchRadiusY, PixelValue_t a_tnZeroTolerance,
771
PixelValue_t a_tnTolerance, FRAMESIZE a_nMatchCountThrottle,
772
FRAMESIZE a_nMatchSizeThrottle)
775
// Used to loop through things.
777
// The number of pixels in each frame.
779
// Make sure they didn't start us off with an error.
780
assert (a_reStatus == g_kNoError);
782
// Make sure they gave us a reasonable number of frames.
783
// (We need at least one for the new frame and one to
784
// compare against the new frame. We prefer more.)
785
assert (a_nFrames >= 2);
787
// Make sure the width & height are reasonable.
788
assert (a_tnWidth > PIXELINDEX (0));
789
assert (a_tnHeight > PIXELINDEX (0));
791
// Make sure the search radius is reasonable.
792
assert (a_tnSearchRadiusX > PIXELINDEX (0)
793
&& a_tnSearchRadiusY > PIXELINDEX (0)
794
&& a_tnSearchRadiusX <= a_tnWidth
795
&& a_tnSearchRadiusY <= a_tnHeight);
797
// Make sure the match throttles are reasonable.
798
assert (a_nMatchCountThrottle >= 0
799
&& a_nMatchCountThrottle
800
<= a_tnSearchRadiusX * a_tnSearchRadiusY);
801
assert (a_nMatchSizeThrottle > 0);
803
// Calculate the number of pixels in each frame.
804
tnPixels = FRAMESIZE (a_tnWidth) * FRAMESIZE (a_tnHeight);
806
// Initialize our pixel pool.
807
m_oPixelPool.Initialize (a_reStatus, tnPixels * FRAMESIZE (a_nFrames));
808
if (a_reStatus != g_kNoError)
811
// Allocate space for our pointers to frames.
812
m_ppFrames = new ReferenceFrame_t * [a_nFrames];
813
if (m_ppFrames == NULL)
815
a_reStatus = g_kOutOfMemory;
818
// (Initialize each one to NULL, in case we run out of memory while
819
// trying to allocate frames -- if we don't do this, we'll end up
820
// trying to delete garbage pointers.)
821
for (i = 0; i < a_nFrames; ++i)
822
m_ppFrames[i] = NULL;
823
// (Save this parameter now, to make it possible to destroy an
824
// incompletely-initialized object.)
825
m_nFrames = a_nFrames;
827
// Allocate our reference frames.
828
for (i = 0; i < a_nFrames; ++i)
830
// Allocate the next reference frame.
831
m_ppFrames[i] = new ReferenceFrame_t (a_reStatus, a_tnWidth,
833
if (m_ppFrames[i] == NULL)
835
a_reStatus = g_kOutOfMemory;
838
if (a_reStatus != g_kNoError)
842
// Initialize the search-window.
843
m_oSearchWindow.Init (a_reStatus, a_tnWidth, a_tnHeight,
844
a_tnSearchRadiusX, a_tnSearchRadiusY, a_tnTolerance);
845
if (a_reStatus != g_kNoError)
848
// Initialize our set of matches. (We'll use this to sort the
849
// incoming matches by how closely it matches the current
850
// pixel-group, and we'll throw away fuzzier matches.)
851
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
852
typename MatchedPixelGroupSet::InitParams oInitSetMatches (rand(),
853
true /* allocate internal nodes from allocator */);
854
m_setMatches.Init (a_reStatus, true, oInitSetMatches);
855
if (a_reStatus != g_kNoError)
857
#endif // THROTTLE_PIXELSORTER_WITH_SAD
859
// Initialize our moved-regions set.
860
m_setRegions.Init (a_reStatus, true);
861
if (a_reStatus != g_kNoError)
864
// Initialize the moved-region extents allocator.
865
#ifdef SET_REGION_IMPLEMENTED_WITH_VECTOR
866
m_oRegionAllocator.Init (a_reStatus);
867
if (a_reStatus != g_kNoError)
869
#endif // SET_REGION_IMPLEMENTED_WITH_VECTOR
871
// Initialize our used reference-pixels container.
872
#ifdef USED_REFERENCE_PIXELS_REGION_IS_BITMAP
873
m_oUsedReferencePixels.Init (a_reStatus, a_tnWidth, a_tnHeight);
874
#else // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
875
m_oUsedReferencePixels.Init (a_reStatus, m_oRegionAllocator);
876
#endif // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
877
if (a_reStatus != g_kNoError)
879
#if defined (USED_REFERENCE_PIXELS_REGION_IS_BITMAP)
880
#if defined (DEBUG_REGION2D)
881
// HACK: too expensive, test region class elsewhere.
882
m_oUsedReferencePixels.SetDebug (false);
883
#endif // DEBUG_REGION2D
884
#endif // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
886
// Initialize our tested zero-motion-pixels container.
887
#ifdef USED_REFERENCE_PIXELS_REGION_IS_BITMAP
888
m_oTestedZeroMotionPixels.Init (a_reStatus, a_tnWidth, a_tnHeight);
889
#else // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
890
m_oTestedZeroMotionPixels.Init (a_reStatus, m_oRegionAllocator);
891
#endif // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
892
if (a_reStatus != g_kNoError)
894
#if defined (USED_REFERENCE_PIXELS_REGION_IS_BITMAP)
895
#if defined (DEBUG_REGION2D)
896
// HACK: too expensive, test region class elsewhere.
897
m_oTestedZeroMotionPixels.SetDebug (false);
898
#endif // DEBUG_REGION2D
899
#endif // USED_REFERENCE_PIXELS_REGION_IS_BITMAP
901
// Initialize our match-throttle region.
902
m_oMatchThrottleRegion.Init (a_reStatus);
903
if (a_reStatus != g_kNoError)
906
// Initialize the region that does the flood-filling for the
907
// match-throttle-region.
908
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
909
m_oFloodFillRegion.Init (a_reStatus, a_tnWidth, a_tnHeight);
910
if (a_reStatus != g_kNoError)
912
#if defined (DEBUG_REGION2D)
913
// HACK: too expensive, test region class elsewhere.
914
m_oFloodFillRegion.SetDebug (false);
915
#endif // DEBUG_REGION2D
916
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
918
// Initialize the search-border.
919
// Note that the search-border is not given the value of
920
// the externally-set match-size-throttle. That was done
921
// back when throttling happened at the end of a pixel-group
922
// search if any of the resulting regions exceeded the
923
// match-size-throttle. Now, those large regions are kept
924
// in the search-border until all the new conditions for
925
// throttling are met.
926
m_oSearchBorder.Init (a_reStatus, a_tnWidth, a_tnHeight,
927
a_tnSearchRadiusX, a_tnSearchRadiusY, PGW, PGH,
928
/* a_nMatchSizeThrottle */ a_tnWidth * a_tnHeight);
929
if (a_reStatus != g_kNoError)
932
// Finally, store our parameters. (Convert the tolerance value to
933
// the format used internally.)
934
//m_nFrames = a_nFrames; (Stored above)
935
m_tnWidth = a_tnWidth;
936
m_tnHeight = a_tnHeight;
937
m_tnPixels = tnPixels;
938
m_tnSearchRadiusX = a_tnSearchRadiusX;
939
m_tnSearchRadiusY = a_tnSearchRadiusY;
940
m_tnZeroTolerance = Pixel_t::MakeTolerance (a_tnZeroTolerance);
941
m_tnTolerance = Pixel_t::MakeTolerance (a_tnTolerance);
942
m_tnTwiceTolerance = Pixel_t::MakeTolerance (PixelValue_t (2)
944
m_nMatchCountThrottle = a_nMatchCountThrottle;
945
m_nMatchSizeThrottle = a_nMatchSizeThrottle;
947
// Initialize our flood-fill controllers. (This happens after we
948
// store our parameters, because these methods may need those
950
m_oZeroMotionFloodFillControl.Init (a_reStatus, this);
951
if (a_reStatus != g_kNoError)
953
m_oMatchThrottleFloodFillControl.Init (a_reStatus, this);
954
if (a_reStatus != g_kNoError)
956
m_oPruningFloodFillControl.Init (a_reStatus, this);
957
if (a_reStatus != g_kNoError)
963
// If a frame is ready to be output, return it, otherwise return
965
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
966
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
967
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
968
class REFERENCEFRAME>
969
const typename MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,
970
FRAMESIZE, PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
971
REFERENCEFRAME>::ReferenceFrame_t *
972
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
973
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
974
REFERENCEFRAME>::GetFrameReadyForOutput (void)
976
ReferenceFrame_t *pFrame;
977
// The frame to return to the caller.
979
// Used to loop through things.
981
// If we have space for the new frame in our reference frames, then
982
// it's not time to emit a frame yet. (We delay emitting frames for
983
// as long as possible, in order to calculate the most accurate
985
if (m_nFirstFrame != 0 || m_nLastFrame != m_nFrames)
988
// Get the frame to return to the caller.
989
pFrame = m_ppFrames[0];
991
// Shift the remaining frames down.
992
for (i = 1; i < m_nFrames; ++i)
993
m_ppFrames[i - 1] = m_ppFrames[i];
995
// Our caller will read the data in the frame. By the time the
996
// caller calls AddFrame(), we'll need to use this frame again.
997
// So put it at the end of the list.
999
m_ppFrames[m_nLastFrame] = pFrame;
1001
// Finally, return the frame to our caller.
1007
// HACK: developer debugging output.
1008
extern "C" { extern int frame, verbose; };
1010
// Add another frame to be analyzed into the system.
1011
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
1012
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
1013
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
1014
class REFERENCEFRAME>
1016
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
1017
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>::AddFrame
1018
(Status_t &a_reStatus, const Pixel_t *a_pPixels)
1021
// Used to loop through pixels.
1022
FRAMESIZE tnNotMovedZeroMotionPixels, tnNotMovedThrottledPixels,
1023
tnNotMovedPixels, tnMovedThrottledPixels, tnMovedPixels,
1024
tnNoMatchNewPixels, tnNewPixels;
1025
// Statistics on the result of our analysis -- the number of
1026
// pixels that didn't move, the number that moved, the number
1027
// found by flood-filling, and the number of new pixels.
1029
// Make sure they didn't start us off with an error.
1030
assert (a_reStatus == g_kNoError);
1032
// Make sure we can accept a new frame.
1033
assert (m_nFirstFrame == 0 && m_nLastFrame < m_nFrames);
1035
// Get the reference frame that will become the new frame.
1036
m_pNewFrame = m_ppFrames[m_nLastFrame];
1037
m_pNewFramePixels = a_pPixels;
1039
// Reset the new frame, so that it doesn't refer to any pixels.
1040
// (This frame was previously returned by GetFrameReadyForOutput(),
1041
// so it wasn't safe to reset it until now.)
1042
m_pNewFrame->Reset();
1044
// Reset our statistics.
1045
tnNotMovedZeroMotionPixels = tnNotMovedThrottledPixels
1046
= tnNotMovedPixels = tnMovedThrottledPixels = tnMovedPixels
1047
= tnNoMatchNewPixels = tnNewPixels = FRAMESIZE (0);
1049
// If there is a previous frame, do motion-detection against it.
1050
if (m_nFirstFrame != m_nLastFrame)
1052
PIXELINDEX tnLastX, tnLastY;
1053
// Used to zigzag through the frame.
1055
// Get the reference frame, i.e. the one that we'll do
1056
// motion-detection against. (For now, that's the previous
1057
// frame. Eventually, we'd like to do motion-detection against
1058
// several previous frames, but not yet.)
1059
m_pReferenceFrame = m_ppFrames[m_nLastFrame - 1];
1061
// Prepare to search within this frame.
1062
m_oSearchWindow.StartFrame (m_pReferenceFrame);
1063
m_oSearchBorder.StartFrame (a_reStatus);
1064
if (a_reStatus != g_kNoError)
1067
// Start by processing parts of the image that aren't moving.
1068
// Loop through pixel-group-sized chunks of the image, find
1069
// pixel-groups within the specified tolerance, and flood-fill
1070
// them. If the resulting region exceeds the match-size-throttle,
1071
// apply it now. Either way, remember the region that was found
1072
// (so that the region doesn't have to be found again).
1074
// Skip if they specified a zero for the tolerance.
1075
m_oUsedReferencePixels.Clear();
1076
m_oTestedZeroMotionPixels.Clear();
1077
if (m_tnZeroTolerance != 0)
1082
PIXELINDEX tnPixelX, tnPixelY;
1083
// Used to loop through pixels in the pixel-group.
1088
ReferencePixel_t *pPrevPixel;
1089
// The pixel from the previous frame.
1090
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1091
typename BitmapRegion_t::ConstIterator itExtent;
1092
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1093
// Used to convert between region types.
1095
// Loop through the pixels to compare, see if they all
1096
// match within the tolerance.
1097
for (tnPixelY = m_tnY;
1098
tnPixelY < m_tnY + PGH;
1101
for (tnPixelX = m_tnX;
1102
tnPixelX < m_tnX + PGW;
1105
// If a pixel in this pixel-group has already
1106
// been tested in this pass, skip it.
1107
if (m_oTestedZeroMotionPixels.DoesContainPoint
1108
(tnPixelY, tnPixelX))
1111
// Get the two pixels to compare.
1112
pPrevPixel = m_pReferenceFrame->GetPixel
1113
(tnPixelX, tnPixelY);
1114
assert (pPrevPixel != NULL);
1115
const Pixel_t &rPrevPixel
1116
= pPrevPixel->GetValue();
1117
const Pixel_t &rNewPixel
1118
= a_pPixels[tnPixelY * m_tnWidth
1122
if (!rPrevPixel.IsWithinTolerance (rNewPixel,
1131
// These pixels are within the zero-motion tolerance.
1132
// Set up a region describing the current pixel-group.
1133
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1134
m_oFloodFillRegion.Clear();
1135
for (tnPixelY = m_tnY;
1136
tnPixelY < m_tnY + PGH;
1139
m_oFloodFillRegion.Merge (a_reStatus, tnPixelY,
1140
m_tnX, m_tnX + PGW);
1141
if (a_reStatus != g_kNoError)
1144
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1145
m_oMatchThrottleRegion.Clear();
1146
for (tnPixelY = m_tnY;
1147
tnPixelY < m_tnY + PGH;
1150
m_oMatchThrottleRegion.Merge (a_reStatus, tnPixelY,
1151
m_tnX, m_tnX + PGW);
1152
if (a_reStatus != g_kNoError)
1155
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1157
// Set its motion vector.
1158
m_oMatchThrottleRegion.SetMotionVector (0, 0);
1160
// Set its location. (Needed only for debugging
1163
m_oMatchThrottleRegion.m_tnX = m_tnX;
1164
m_oMatchThrottleRegion.m_tnY = m_tnY;
1167
// Flood-fill this match, so as to get its full extent.
1168
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1169
m_oFloodFillRegion.FloodFill (a_reStatus,
1170
m_oZeroMotionFloodFillControl, false, true);
1171
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1172
m_oMatchThrottleRegion.FloodFill (a_reStatus,
1173
m_oZeroMotionFloodFillControl, false, true);
1174
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1175
if (a_reStatus != g_kNoError)
1178
// Now copy the results of the flood-fill to the
1179
// match-throttle region.
1180
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1181
m_oMatchThrottleRegion.Clear();
1182
for (itExtent = m_oFloodFillRegion.Begin();
1183
itExtent != m_oFloodFillRegion.End();
1186
// Get the current extent.
1187
const typename BitmapRegion_t::Extent &rExtent
1190
// Copy it to the match-throttle region.
1191
m_oMatchThrottleRegion.Union
1192
(a_reStatus, rExtent.m_tnY,
1193
rExtent.m_tnXStart, rExtent.m_tnXEnd);
1194
if (a_reStatus != g_kNoError)
1197
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1199
// All of these reference pixels have been tested.
1200
// There's no need to test them again.
1202
typename MovedRegion::ConstIterator itExtent;
1203
for (itExtent = m_oMatchThrottleRegion.Begin();
1204
itExtent != m_oMatchThrottleRegion.End();
1207
// Get the current extent.
1208
const typename MovedRegion::Extent &rExtent
1211
// Add it to our running total of tested
1212
// zero-motion reference-pixels.
1213
m_oTestedZeroMotionPixels.Union
1214
(a_reStatus, rExtent.m_tnY,
1215
rExtent.m_tnXStart, rExtent.m_tnXEnd);
1216
if (a_reStatus != g_kNoError)
1221
// If this match is larger than the
1222
// match-size-throttle, apply it now.
1223
if (m_oMatchThrottleRegion.NumberOfPoints()
1224
>= m_nMatchSizeThrottle)
1226
// Apply this region to the new frame.
1227
ApplyRegionToNewFrame (a_reStatus,
1228
m_oMatchThrottleRegion);
1229
if (a_reStatus != g_kNoError)
1232
// That's more resolved pixels.
1233
tnNotMovedZeroMotionPixels
1234
+= m_oMatchThrottleRegion.NumberOfPoints();
1236
// Clean up after ourselves.
1237
m_oMatchThrottleRegion.Clear();
1240
// Otherwise, just dispose of it.
1241
// m_oTestedZeroMotionPixels will prevent us from
1242
// finding it again.
1244
m_oMatchThrottleRegion.Clear();
1247
// Now move X forward, but in a way that handles
1248
// frames whose dimensions are not even multiples of
1249
// the pixel-group dimension.
1250
if (m_tnX + PGW == m_tnWidth)
1253
if (m_tnX > m_tnWidth - PGW)
1254
m_tnX = m_tnWidth - PGW;
1257
// Now move Y forward, but in a way that handles
1258
// frames whose dimensions are not even multiples of
1259
// the pixel-group dimension.
1260
if (m_tnY + PGH == m_tnHeight)
1263
if (m_tnY > m_tnHeight - PGH)
1264
m_tnY = m_tnHeight - PGH;
1267
// Clean up after ourselves.
1268
m_oTestedZeroMotionPixels.Clear();
1271
// Now do the motion-compensated denoising. Start in the
1272
// upper-left corner of the frame, and zigzag down the frame
1273
// (i.e. move all the way right, then down one line, then all
1274
// the way left, then down one line, etc.). Look for matches
1275
// for the current pixel-group in the reference frame, and
1276
// build regions of such matches.
1278
// (Skip it if they turned motion-detection off.)
1279
if (m_tnTolerance > 0)
1281
// Start searching in the upper-left corner, and prepare to
1284
tnLastX = m_tnWidth - PGW;
1285
tnLastY = m_tnHeight - PGH;
1288
// Loop until the entire frame has been searched.
1291
typename SearchWindow_t::PixelGroup oCurrentGroup;
1292
// The current pixel-group.
1293
typename SearchWindow_t::PixelSorterIterator itMatch;
1294
// Used to search for matches for the current
1296
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
1297
typename MatchedPixelGroupSet::ConstIterator
1299
// One of the best matches found.
1301
// The sum-of-absolute-differences between the
1302
// current pixel group and the match.
1303
#endif // THROTTLE_PIXELSORTER_WITH_SAD
1304
const typename SearchWindow_t::PixelGroup *pMatch;
1305
// A pixel-group that matches the current pixel,
1306
// within the configured tolerance.
1307
FRAMESIZE tnMatches;
1308
// The number of matches found.
1310
// Create the current pixel-group. If any of its pixels
1311
// have been resolved, skip it.
1314
// Used to loop through the current
1315
// pixel-group's pixels.
1317
for (y = 0; y < PGH; ++y)
1319
for (x = 0; x < PGW; ++x)
1321
PIXELINDEX tnPixelX, tnPixelY;
1322
// The index of the current pixel.
1324
// Calculate the index of the current pixel.
1325
tnPixelX = m_tnX + x;
1326
tnPixelY = m_tnY + y;
1328
// If this pixel has been resolved already,
1329
// skip this pixel-group.
1330
if (m_pNewFrame->GetPixel (tnPixelX,
1334
// Set the pixel value in the pixel-group.
1335
oCurrentGroup.m_atPixels[y][x]
1336
= a_pPixels[FRAMESIZE (tnPixelY)
1337
* FRAMESIZE (m_tnWidth)
1338
+ FRAMESIZE (tnPixelX)];
1343
// Tell the pixel-group where it is.
1344
oCurrentGroup.m_tnX = m_tnX;
1345
oCurrentGroup.m_tnY = m_tnY;
1349
fprintf (stderr, "Now checking pixel-group, frame %d, "
1350
"x %d, y %d\n", frame, int (m_tnX), int (m_tnY));
1356
// Find matches for the current pixel-group. Search for
1357
// them in the pixel-sorter tree, make sure each wasn't
1358
// already found (i.e. isn't already in the search-border),
1359
// flood-fill each one, and add each to the search-border.
1361
// If the number of matches in the area exceeds the
1362
// match-count-throttle, or if the largest region found
1363
// exceeds the match-size-throttle, then pick the largest
1364
// region that intersects the current pixel-group and apply
1365
// it to the frame now.
1367
// Set up the search-window in a radius around the
1368
// current pixel-group.
1369
m_oSearchWindow.PrepareForSearch (a_reStatus, true);
1370
if (a_reStatus != g_kNoError)
1373
// Search for matches for the current pixel-group
1374
// within the search radius.
1375
m_oSearchWindow.StartSearch (itMatch, oCurrentGroup);
1376
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
1377
m_setMatches.Clear();
1378
while (pMatch = m_oSearchWindow.FoundNextMatch (itMatch,
1379
tnSAD), pMatch != NULL)
1380
#else // THROTTLE_PIXELSORTER_WITH_SAD
1381
while (pMatch = m_oSearchWindow.FoundNextMatch (itMatch),
1383
#endif // THROTTLE_PIXELSORTER_WITH_SAD
1385
// Calculate its motion vector.
1386
PIXELINDEX tnMotionX = pMatch->m_tnX - m_tnX;
1387
PIXELINDEX tnMotionY = pMatch->m_tnY - m_tnY;
1389
#ifdef PRINT_SEARCHBORDER
1390
//if (frame == 61 && DIM == 2)
1391
fprintf (stderr, "Found match at (%d,%d), "
1392
"motion vector (%d,%d)\n",
1393
int (m_tnX), int (m_tnY),
1394
int (tnMotionX), int (tnMotionY));
1395
#endif // PRINT_SEARCHBORDER
1397
// Make sure this match is within the search
1398
// radius. (Sanity check.)
1399
assert (AbsoluteValue (tnMotionX)
1400
<= m_tnSearchRadiusX);
1401
assert (AbsoluteValue (tnMotionY)
1402
<= m_tnSearchRadiusY);
1404
// Make sure all matches refer to unused
1405
// reference-frame pixels.
1407
#ifdef USE_REFERENCEFRAMEPIXELS_ONCE
1410
// Used to loop through the current
1411
// pixel-group's pixels.
1413
for (y = 0; y < PGH; ++y)
1415
for (x = 0; x < PGW; ++x)
1417
PIXELINDEX tnPixelX, tnPixelY;
1418
// The index of the current pixel.
1420
// Calculate the index of the current
1422
tnPixelX = pMatch->m_tnX + x;
1423
tnPixelY = pMatch->m_tnY + y;
1425
// Make sure this reference-frame pixel
1426
// hasn't been used already.
1427
assert (!m_oUsedReferencePixels
1428
.DoesContainPoint (tnPixelY,
1433
#endif // USE_REFERENCEFRAMEPIXELS_ONCE
1436
// That's one more match.
1439
// See if a region with this motion vector already
1440
// intersects/borders the current pixel-group, and
1442
bool bExistingMatch = m_oSearchBorder.HasExistingMatch
1443
(tnMotionX, tnMotionY);
1447
fprintf (stderr, "\tFound match, "
1448
"motion vector (%d,%d)",
1449
int (tnMotionX), int (tnMotionY));
1450
if (!bExistingMatch)
1451
fprintf (stderr, " - new\n");
1453
fprintf (stderr, " - existing\n");
1456
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
1458
// If a region with this motion vector already
1459
// intersects/borders the current pixel-group, then
1460
// this match can be skipped.
1461
if (!bExistingMatch)
1463
// If this match is better than our worst so
1464
// far, get rid of our worst & use the new one
1466
if (m_setMatches.Size() == m_nMatchCountThrottle)
1468
typename MatchedPixelGroupSet::Iterator
1470
// The worst match found so far.
1472
// Get the worst match. (It's at the end of
1474
itWorst = m_setMatches.End();
1477
// If the new item is better than our worst,
1478
// get rid of our worst to make room for the
1480
if (tnSAD < (*itWorst).m_tnSAD)
1481
m_setMatches.Erase (itWorst);
1484
// If this match is close enough to the current
1485
// pixel-group, make a note of it.
1486
if (m_setMatches.Size() < m_nMatchCountThrottle)
1488
m_setMatches.Insert (a_reStatus,
1489
MatchedPixelGroup (tnSAD, pMatch));
1490
if (a_reStatus != g_kNoError)
1495
#else // THROTTLE_PIXELSORTER_WITH_SAD
1497
// If a region with this motion vector doesn't already
1498
// intersect/border the current pixel-group, then
1499
// flood-fill it and add it to the search-border.
1500
if (!bExistingMatch)
1502
// Add this region to the search-border.
1503
(void) SearchBorder_AddNewRegion (a_reStatus,
1504
tnMotionX, tnMotionY);
1505
if (a_reStatus != g_kNoError)
1509
#endif // THROTTLE_PIXELSORTER_WITH_SAD
1512
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
1514
// Now loop through all the good matches found, flood-fill
1515
// each one, and add them to the search-border.
1516
for (itBestMatch = m_setMatches.Begin();
1517
itBestMatch != m_setMatches.End();
1520
PIXELINDEX tnMotionX, tnMotionY;
1521
// The motion vector of this match.
1523
// Get the current match.
1524
const MatchedPixelGroup &rMatch = *itBestMatch;
1526
// Calculate its motion vector.
1527
tnMotionX = rMatch.m_pGroup->m_tnX - m_tnX;
1528
tnMotionY = rMatch.m_pGroup->m_tnY - m_tnY;
1530
// Add this region to the search-border.
1531
(void) SearchBorder_AddNewRegion (a_reStatus,
1532
tnMotionX, tnMotionY);
1533
if (a_reStatus != g_kNoError)
1537
// All done with the matches.
1538
m_setMatches.Clear();
1540
#endif // THROTTLE_PIXELSORTER_WITH_SAD
1542
// If it's time to throttle matches, do so.
1544
// The idea is that matches are only throttled when the
1545
// match-count-throttle is exceeded, and the largest
1546
// matches are applied until the match-size-throttle is
1547
// satisfied. Previously, matches could be throttled if
1548
// only the match-size-throttle was exceeded. But this
1549
// led to bad matches getting applied before a better match
1550
// could be found, which led to an "earthquaking" sort of
1551
// artifact in dark, cloudy areas of the picture. This
1552
// ended up being the simple fix for that problem!
1554
// Now, the challenge is to pick a match-size-throttle that
1555
// keeps the denoiser running quickly without missing many
1556
// good matches, and a match-count-throttle that keeps the
1557
// number of candidates high enough to avoid the shaking
1558
// artifact described above.
1559
if (tnMatches >= m_nMatchCountThrottle
1560
/* || m_oSearchBorder.GetSizeOfLargestActiveRegion()
1561
>= m_nMatchSizeThrottle */)
1563
// Get the largest active-region from the search-border
1564
// and apply it now. Then, keep doing that as long
1565
// as the largets active-region exceeds the match-size-
1567
// This allows at least one region to be applied if the
1568
// match-count-throttle is exceeded, and allows the
1569
// search-border to avoid doing expensive work for
1570
// regions that exceed the match-size-throttle.
1573
FRAMESIZE tnThrottledPixels;
1574
PIXELINDEX tnMotionX, tnMotionY;
1576
// Take the largest region that the current
1577
// pixel-group matched, flood-fill it, and apply it
1578
// to the new frame now, eliminating any competing
1579
// moved-regions encountered along the way.
1580
tnThrottledPixels = SearchBorder_MatchThrottle
1581
(a_reStatus, tnMatches, tnMotionX, tnMotionY);
1582
if (a_reStatus != g_kNoError)
1587
fprintf (stderr, "Match throttle: frame %d, "
1588
"x %03d, y %03d, %03d matches, "
1589
"%04d flood \r", frame,
1590
int (m_tnX), int (m_tnY), int (tnMatches),
1591
int (tnThrottledPixels));
1594
// That's more pixels found by match-throttling.
1595
if (tnMotionX == 0 && tnMotionY == 0)
1596
tnNotMovedThrottledPixels += tnThrottledPixels;
1598
tnMovedThrottledPixels += tnThrottledPixels;
1599
} while (m_oSearchBorder.GetSizeOfLargestActiveRegion()
1600
>= m_nMatchSizeThrottle);
1603
// If no matches were found for the current pixel-group,
1604
// and there are no active border-regions in the area,
1605
// then we've found new information.
1607
// NOTE: this dramatically speeds up the analysis of
1608
// frames that contain mostly new info, but probably
1609
// impacts quality. Flood-fills are allowed to override
1610
// the result, though.
1612
// NOTE: We no longer test whether there are any active
1613
// border-regions in the area. The logic is, they've been
1614
// flood-filled, and if they don't extend into the current
1615
// pixel-group, then it's safe to consider the current
1616
// pixel-group to be new information.
1617
else if (tnMatches == 0)
1619
PIXELINDEX tnX, tnY;
1621
for (tnY = m_tnY; tnY < m_tnY + PGH; ++tnY)
1623
for (tnX = m_tnX; tnX < m_tnX + PGW; ++tnX)
1625
// Allocate a new reference pixel.
1626
ReferencePixel_t *pNewPixel
1627
= m_oPixelPool.Allocate();
1629
// Store the new pixel in the reference frame.
1630
m_pNewFrame->SetPixel (tnX, tnY, pNewPixel);
1632
// Give it the value from the new frame.
1633
pNewPixel->AddSample (a_pPixels[tnY
1634
* m_tnWidth + tnX]);
1640
// Move to the next pixel-group.
1641
if ((m_tnStepX == 1 && m_tnX == tnLastX)
1642
|| (m_tnStepX == -1 && m_tnX == 0))
1644
// We need to move down a line. If we're already on
1645
// the last line, we're done with the frame.
1646
if (m_tnY == tnLastY)
1649
// We should have found enough matches during this line
1650
// to safely apply all regions that exceed the
1651
// match-size-throttle.
1652
if (m_oSearchBorder.GetSizeOfLargestActiveRegion()
1653
>= m_nMatchSizeThrottle)
1655
// Get the largest active-region from the
1656
// search-border and apply it now. Then, keep
1657
// doing that as long as the largets active-region
1658
// exceeds the match-size-throttle.
1659
// This allows at least one region to be applied if
1660
// the match-count-throttle is exceeded, and allows
1661
// the search-border to avoid doing expensive work
1662
// for regions that exceed the match-size-throttle.
1666
FRAMESIZE tnThrottledPixels;
1667
PIXELINDEX tnMotionX, tnMotionY;
1669
// Take the largest region that the current
1670
// pixel-group matched, flood-fill it, and
1671
// apply it to the new frame now, eliminating
1672
// any competing moved-regions encountered
1674
tnThrottledPixels = SearchBorder_MatchThrottle
1675
(a_reStatus, tnMatches,
1676
tnMotionX, tnMotionY);
1677
if (a_reStatus != g_kNoError)
1682
fprintf (stderr, "Match throttle: frame %d, "
1683
"x %03d, y %03d, %03d matches, "
1684
"%04d flood \r", frame,
1685
int (m_tnX), int (m_tnY), int (tnMatches),
1686
int (tnThrottledPixels));
1689
// That's more pixels found by
1690
// match-throttling.
1691
if (tnMotionX == 0 && tnMotionY == 0)
1692
tnNotMovedThrottledPixels
1693
+= tnThrottledPixels;
1695
tnMovedThrottledPixels
1696
+= tnThrottledPixels;
1698
(m_oSearchBorder.GetSizeOfLargestActiveRegion()
1699
>= m_nMatchSizeThrottle);
1702
// Move down a line.
1703
m_oSearchWindow.MoveDown();
1704
m_oSearchBorder.MoveDown (a_reStatus);
1705
if (a_reStatus != g_kNoError)
1710
//fprintf (stderr, "Now on line %d\r", int (m_tnY));
1712
// Now move across the frame in the other direction,
1714
m_tnStepX = -m_tnStepX;
1716
else if (m_tnStepX == 1)
1718
// Move right one pixel.
1719
m_oSearchWindow.MoveRight();
1720
m_oSearchBorder.MoveRight (a_reStatus);
1721
if (a_reStatus != g_kNoError)
1727
// Move left one pixel.
1728
assert (m_tnStepX == -1);
1729
m_oSearchWindow.MoveLeft();
1730
m_oSearchBorder.MoveLeft (a_reStatus);
1731
if (a_reStatus != g_kNoError)
1737
// Get all the remaining moved-regions from the search-border.
1738
m_oSearchBorder.FinishFrame (a_reStatus);
1739
if (a_reStatus != g_kNoError)
1742
// We've found all the possible moved regions between the
1743
// new frame and the reference frame.
1744
// Loop through the moved regions found by motion-detection,
1745
// prune them of already-resolved pixels, and apply each one
1746
// to the new frame.
1747
//fprintf (stderr, "Applied extents:"); // HACK
1748
while (m_setRegions.Size() > 0)
1750
typename MovedRegionSet::Iterator itRegion;
1751
MovedRegion *pRegion;
1752
// The moved region to apply next.
1753
PIXELINDEX tnMotionX, tnMotionY;
1754
// The region's motion vector.
1756
// Get the moved region to apply next.
1757
itRegion = m_setRegions.Begin();
1758
pRegion = *itRegion;
1759
m_setRegions.Erase (itRegion);
1760
pRegion->GetMotionVector (tnMotionX, tnMotionY);
1762
// Flood-fill the candidate region, re-testing all the
1763
// extents. This removes parts that have been resolved
1765
m_oPruningFloodFillControl.SetupForFloodFill
1766
(tnMotionX, tnMotionY);
1767
pRegion->FloodFill (a_reStatus,
1768
m_oPruningFloodFillControl, true, false);
1769
if (a_reStatus != g_kNoError)
1772
// If that makes it smaller than the next highest-
1773
// priority region we found, put it back in & try again.
1774
itRegion = m_setRegions.Begin();
1775
if (itRegion != m_setRegions.End()
1776
&& pRegion->NumberOfPoints()
1777
< (*itRegion)->NumberOfPoints())
1779
// Are there enough points left in this region for
1780
// us to bother with it?
1781
if (pRegion->NumberOfPoints() < PGW * PGH)
1783
// No. Just get rid of it.
1784
m_oSearchBorder.DeleteRegion (pRegion);
1788
// Yes. Put the region back into our set, to be
1791
typename MovedRegionSet::InsertResult
1794
m_setRegions.Insert (a_reStatus, pRegion);
1795
if (a_reStatus != g_kNoError)
1797
m_oSearchBorder.DeleteRegion (pRegion);
1800
assert (oInsertResult.m_bInserted);
1803
// Try the region that's now the highest priority.
1807
// Apply this region to the new frame.
1808
ApplyRegionToNewFrame (a_reStatus, *pRegion);
1809
if (a_reStatus != g_kNoError)
1811
m_oSearchBorder.DeleteRegion (pRegion);
1815
// That's more resolved pixels.
1816
if (tnMotionX == 0 && tnMotionY == 0)
1817
tnNotMovedPixels += pRegion->NumberOfPoints();
1819
tnMovedPixels += pRegion->NumberOfPoints();
1821
// We're done with this region.
1822
m_oSearchBorder.DeleteRegion (pRegion);
1826
// Prepare for searching the next frame.
1827
m_oSearchWindow.FinishFrame();
1830
// Motion-searching is done. Loop through the reference frame's
1831
// pixels, find any unresolved pixels, and create a new pixel for
1832
// them, using the data in the new frame.
1833
for (i = 0; i < m_tnPixels; ++i)
1835
ReferencePixel_t *pNewPixel;
1837
// If this pixel is still unresolved, give it the value of
1838
// the corresponding pixel in the new frame.
1839
pNewPixel = m_pNewFrame->GetPixel (i);
1840
if (pNewPixel == NULL)
1842
// Allocate a new reference pixel.
1843
ReferencePixel_t *pNewPixel = m_oPixelPool.Allocate();
1845
// Store the new pixel in the reference frame.
1846
m_pNewFrame->SetPixel (i, pNewPixel);
1848
// Give it the value from the new frame.
1849
pNewPixel->AddSample (a_pPixels[i]);
1851
// That's one more new pixel.
1854
else if (pNewPixel != NULL
1855
&& pNewPixel->GetFrameReferences() == 1)
1857
// Count up the earlier-found new pixel. (It wasn't safe to
1858
// count them until flood-filling had a chance to override
1860
++tnNoMatchNewPixels;
1864
// Make sure all pixels were accounted for.
1865
// (This is a big sanity check.)
1866
assert (tnNotMovedZeroMotionPixels + tnNotMovedThrottledPixels
1867
+ tnNotMovedPixels + tnMovedThrottledPixels + tnMovedPixels
1868
+ tnNoMatchNewPixels + tnNewPixels == m_tnPixels);
1870
// All done. Remember that the data in the new frame is now valid.
1873
// Make sure none of the pixels have more references than we have
1874
// frames. (Sanity check.)
1876
#ifdef USE_REFERENCEFRAMEPIXELS_ONCE
1877
for (i = 0; i < m_tnPixels; ++i)
1879
int16_t nRefs = m_pNewFrame->GetPixel (i)->GetFrameReferences();
1881
assert (nRefs <= m_nFrames);
1883
#endif // USE_REFERENCEFRAMEPIXELS_ONCE
1886
// We'll have a new reference-frame and new-frame in the next pass.
1887
m_pReferenceFrame = m_pNewFrame = NULL;
1888
m_pNewFramePixels = NULL;
1890
// HACK: print the pixel statistics.
1893
float fInversePixelsPercent = 100.0f / float (m_tnPixels);
1895
fprintf (stderr, "Frame %d: %.1f%%+%.1f%%+%.1f%% not-moved, "
1896
"%.1f%%+%.1f%% moved, %.1f%%+%.1f%% new\n",
1898
(float (tnNotMovedZeroMotionPixels) * fInversePixelsPercent),
1899
(float (tnNotMovedThrottledPixels) * fInversePixelsPercent),
1900
(float (tnNotMovedPixels) * fInversePixelsPercent),
1901
(float (tnMovedThrottledPixels) * fInversePixelsPercent),
1902
(float (tnMovedPixels) * fInversePixelsPercent),
1903
(float (tnNoMatchNewPixels) * fInversePixelsPercent),
1904
(float (tnNewPixels) * fInversePixelsPercent));
1907
// Print the allocation totals.
1909
fprintf (stderr, "%lu moved-regions, %lu pixel-sorters\n",
1910
(unsigned long) m_oSearchBorder.GetMovedRegionCount(),
1911
(unsigned long) m_oSearchWindow.GetPixelSorterNodeCount());
1914
// Purge all remaining temporary memory allocations.
1915
m_oMatchThrottleRegion.Purge();
1916
#ifndef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1917
m_oZeroMotionFloodFillControl.Purge();
1918
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
1919
#ifndef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
1920
m_oMatchThrottleFloodFillControl.Purge();
1921
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
1922
#ifndef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
1923
m_oPruningFloodFillControl.Purge();
1924
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
1926
// Make sure our temporary memory allocations have been purged.
1927
assert (m_oRegionAllocator.GetNumAllocated() == 0);
1928
assert (m_oMovedRegionSetAllocator.GetNumAllocated() == 0);
1933
// Once there is no more input, call this repeatedly to get the
1934
// details of the remaining frames, until it returns NULL.
1935
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
1936
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
1937
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
1938
class REFERENCEFRAME>
1939
const typename MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,
1940
FRAMESIZE,PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
1941
REFERENCEFRAME>::ReferenceFrame_t *
1942
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
1943
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
1944
REFERENCEFRAME>::GetRemainingFrames (void)
1946
ReferenceFrame_t *pFrame;
1947
// The frame to return to the caller.
1949
// If there are no frames left, let our caller know.
1950
if (m_nFirstFrame == m_nLastFrame)
1953
// Get the frame to return to the caller.
1954
pFrame = m_ppFrames[m_nFirstFrame];
1956
// Remember not to hand it to the caller ever again.
1959
// Finally, return the frame to our caller.
1965
// Purge ourselves of temporary structures.
1966
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
1967
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
1968
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
1969
class REFERENCEFRAME>
1971
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
1972
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
1973
REFERENCEFRAME>::Purge (void)
1975
// Clear out the pixel-sorter.
1976
m_oSearchWindow.PurgePixelSorter();
1981
#ifdef THROTTLE_PIXELSORTER_WITH_SAD
1983
// Default constructor.
1984
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
1985
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
1986
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
1987
class REFERENCEFRAME>
1988
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
1989
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
1990
REFERENCEFRAME>::MatchedPixelGroup::MatchedPixelGroup()
1999
// Initializing constructor.
2000
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2001
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2002
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2003
class REFERENCEFRAME>
2004
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2005
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2006
REFERENCEFRAME>::MatchedPixelGroup::MatchedPixelGroup
2007
(Tolerance_t a_tnSAD,
2008
const typename SearchWindow_t::PixelGroup *a_pGroup)
2009
: m_tnSAD (a_tnSAD), m_pGroup (a_pGroup)
2011
// Nothing else to do.
2017
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2018
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2019
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2020
class REFERENCEFRAME>
2021
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2022
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2023
REFERENCEFRAME>::MatchedPixelGroup::~MatchedPixelGroup()
2030
// A comparison operator, suitable for Set<>.
2031
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2032
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2033
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2034
class REFERENCEFRAME>
2036
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2037
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2038
REFERENCEFRAME>::MatchedPixelGroup::SortBySAD::operator()
2039
(const MatchedPixelGroup &a_rLeft,
2040
const MatchedPixelGroup &a_rRight) const
2043
return (a_rLeft.m_tnSAD < a_rRight.m_tnSAD);
2046
#endif // THROTTLE_PIXELSORTER_WITH_SAD
2050
// Apply this region to the new frame.
2051
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2052
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2053
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2054
class REFERENCEFRAME>
2056
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2057
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,REFERENCEFRAME>
2058
::ApplyRegionToNewFrame (Status_t &a_reStatus,
2059
const MovedRegion &a_rRegion)
2061
PIXELINDEX tnMotionX, tnMotionY;
2062
// The region's motion vector, i.e. the offset between the
2063
// new-frame pixel and the corresponding reference-frame pixel.
2064
typename MovedRegion::ConstIterator itExtent;
2065
// An extent to apply to the new frame.
2067
// Used to loop through pixels.
2069
// Make sure they didn't start us off with an error.
2070
assert (a_reStatus == g_kNoError);
2072
// Get this region's motion vector.
2073
a_rRegion.GetMotionVector (tnMotionX, tnMotionY);
2075
// Loop through the region's extents, locate every pixel it
2076
// describes, and set it to the corresponding pixel in the
2077
// reference-frame representation of the new frame.
2078
for (itExtent = a_rRegion.Begin();
2079
itExtent != a_rRegion.End();
2082
// Get the next extent.
2083
const typename MovedRegion::Extent &rExtent = *itExtent;
2085
// Loop through the pixels it represents, set each
2086
// new-frame pixel to the corresponding reference-frame
2088
for (x = rExtent.m_tnXStart; x < rExtent.m_tnXEnd; ++x)
2090
#ifdef PRINT_SEARCHBORDER
2091
if (m_pNewFrame->GetPixel (x, rExtent.m_tnY) != NULL
2092
&& m_pNewFrame->GetPixel (x, rExtent.m_tnY)
2093
->GetFrameReferences() != 1)
2095
fprintf (stderr, "Pixel (%d,%d) already resolved\n",
2096
int (x), int (rExtent.m_tnY));
2098
#endif // PRINT_SEARCHBORDER
2100
// Make sure this new-frame pixel hasn't been
2102
assert (m_pNewFrame->GetPixel (x, rExtent.m_tnY)
2104
|| m_pNewFrame->GetPixel (x, rExtent.m_tnY)
2105
->GetFrameReferences() == 1);
2107
// Get the corresponding reference-frame pixel.
2108
ReferencePixel_t *pReferencePixel
2109
= m_pReferenceFrame->GetPixel (x + tnMotionX,
2110
rExtent.m_tnY + tnMotionY);
2111
assert (pReferencePixel != NULL);
2113
// Set the new-frame pixel to this reference pixel.
2114
m_pNewFrame->SetPixel (x, rExtent.m_tnY,
2117
// Accumulate the new-frame value of this pixel.
2118
pReferencePixel->AddSample
2119
(m_pNewFramePixels[FRAMESIZE (rExtent.m_tnY)
2120
* FRAMESIZE (m_tnWidth) + FRAMESIZE (x)]);
2124
#ifdef USE_REFERENCEFRAMEPIXELS_ONCE
2126
// All of these reference pixels have been used.
2127
for (itExtent = a_rRegion.Begin();
2128
itExtent != a_rRegion.End();
2131
// Get the current extent.
2132
typename MovedRegion::Extent oExtent = *itExtent;
2134
// Move it along the motion vector.
2135
oExtent.m_tnY += tnMotionY;
2136
oExtent.m_tnXStart += tnMotionX;
2137
oExtent.m_tnXEnd += tnMotionX;
2139
// Make sure it's already in the frame.
2140
assert (oExtent.m_tnY >= 0 && oExtent.m_tnY < m_tnHeight
2141
&& oExtent.m_tnXStart >= 0 && oExtent.m_tnXEnd <= m_tnWidth);
2143
// Add it to our running total of used reference-pixels.
2144
m_oUsedReferencePixels.Union (a_reStatus, oExtent.m_tnY,
2145
oExtent.m_tnXStart, oExtent.m_tnXEnd);
2146
if (a_reStatus != g_kNoError)
2150
#endif // USE_REFERENCEFRAMEPIXELS_ONCE
2152
// Remove all pixel-groups containing used reference pixels from
2153
// the search window.
2154
m_oSearchWindow.Prune (a_rRegion, tnMotionX, tnMotionY);
2159
// Default constructor.
2160
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2161
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2162
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2163
class REFERENCEFRAME>
2164
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2165
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2166
REFERENCEFRAME>::SearchBorder_t::SearchBorder_t
2167
(MovedRegionSet &a_rsetRegions,
2168
typename MovedRegion::Allocator &a_rAlloc)
2169
: BaseClass (a_rAlloc), m_rsetRegions (a_rsetRegions)
2171
// Nothing else to do.
2176
// Receive a completed region from the search-border.
2177
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2178
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2179
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2180
class REFERENCEFRAME>
2182
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2183
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2184
REFERENCEFRAME>::SearchBorder_t::OnCompletedRegion
2185
(Status_t &a_reStatus,
2186
typename SearchBorder_t::MovedRegion *a_pRegion)
2188
// Make sure they didn't start us off with an error.
2189
assert (a_reStatus == g_kNoError);
2191
// Make sure they gave us a completed region.
2192
assert (a_pRegion != NULL);
2194
// Put it in our list.
2195
if (a_pRegion->NumberOfPoints() < PGW * PGH)
2197
// This region is too small to be bothered with.
2198
// Just get rid of it.
2199
DeleteRegion (a_pRegion);
2204
typename MovedRegionSet::InsertResult oInsertResult =
2206
m_rsetRegions.Insert (a_reStatus, a_pRegion);
2207
if (a_reStatus != g_kNoError)
2209
assert (oInsertResult.m_bInserted);
2215
// Add a new region, with the given motion vector, to the
2216
// search border. Flood-fills its area before adding.
2217
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2218
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2219
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2220
class REFERENCEFRAME>
2222
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2223
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2224
REFERENCEFRAME>::SearchBorder_AddNewRegion (Status_t &a_reStatus,
2225
PIXELINDEX a_tnMotionX, PIXELINDEX a_tnMotionY)
2228
// Used to loop through the pixel-group's lines.
2230
// Set up a region describing the current pixel-group.
2231
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2232
m_oFloodFillRegion.Clear();
2233
for (tnY = m_tnY; tnY < m_tnY + PGH; ++tnY)
2235
m_oFloodFillRegion.Merge (a_reStatus, tnY, m_tnX,
2237
if (a_reStatus != g_kNoError)
2240
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2241
m_oMatchThrottleRegion.Clear();
2242
for (tnY = m_tnY; tnY < m_tnY + PGH; ++tnY)
2244
m_oMatchThrottleRegion.Merge (a_reStatus, tnY, m_tnX,
2246
if (a_reStatus != g_kNoError)
2249
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2251
// Set its motion vector.
2252
m_oMatchThrottleRegion.SetMotionVector (a_tnMotionX, a_tnMotionY);
2254
// Set its location. (Needed only for debugging purposes.)
2256
m_oMatchThrottleRegion.m_tnX = m_tnX;
2257
m_oMatchThrottleRegion.m_tnY = m_tnY;
2260
// Flood-fill this match, so as to get its full extent.
2261
m_oMatchThrottleFloodFillControl.SetupForFloodFill (a_tnMotionX,
2263
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2264
m_oFloodFillRegion.FloodFill (a_reStatus,
2265
m_oMatchThrottleFloodFillControl, false, true);
2266
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2267
m_oMatchThrottleRegion.FloodFill (a_reStatus,
2268
m_oMatchThrottleFloodFillControl, false, true);
2269
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2270
if (a_reStatus != g_kNoError)
2273
// Now copy the results of the flood-fill to the match-throttle
2274
// region, so that it can be added to the search-border.
2275
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2276
m_oMatchThrottleRegion.Clear();
2277
typename BitmapRegion_t::ConstIterator itExtent;
2278
for (itExtent = m_oFloodFillRegion.Begin();
2279
itExtent != m_oFloodFillRegion.End();
2282
// Get the current extent.
2283
const typename BitmapRegion_t::Extent &rExtent = *itExtent;
2285
// Copy it to the match-throttle region.
2286
m_oMatchThrottleRegion.Merge
2287
(a_reStatus, rExtent.m_tnY,
2288
rExtent.m_tnXStart, rExtent.m_tnXEnd);
2289
if (a_reStatus != g_kNoError)
2292
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2294
// Get the size of the flood-filled region.
2295
FRAMESIZE tnThisMatch = m_oMatchThrottleRegion.NumberOfPoints();
2297
// Add this match to our growing set of moved regions.
2298
m_oSearchBorder.AddNewRegion (a_reStatus,
2299
m_oMatchThrottleRegion);
2300
if (a_reStatus != g_kNoError)
2303
// Make sure that emptied the region.
2304
// (AddNewRegion() is supposed to do that; this will catch any
2305
// changes in that behavior.)
2306
assert (m_oMatchThrottleRegion.NumberOfPoints() == 0);
2308
// Return the size of the flood-filled region.
2314
// Get the best region that matched the current pixel-group
2315
// (which is usually the largest active-region). Expand it as far
2316
// as it can go, i.e. flood-fill in its area. If it's no longer
2317
// the largest region, put it back and try again. Otherwise, apply
2318
// it to the new frame now.
2320
// Pass the number of pixel-group matches as an argument. If a
2321
// best-region candidate is found to have no unresolved pixels,
2322
// it's no longer considered a pixel-group match, and that may lead
2323
// to the discovery that the match-count-throttle is no longer
2326
// Returns the number of points flood-filled, and backpatches the
2327
// motion vector associated with the best region.
2328
// May return zero if analysis reveals that the match-throttles
2329
// weren't really exceeded.
2330
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2331
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2332
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2333
class REFERENCEFRAME>
2335
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2336
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2337
REFERENCEFRAME>::SearchBorder_MatchThrottle (Status_t &a_reStatus,
2338
FRAMESIZE a_nMatchCount,
2339
PIXELINDEX &a_rtnMotionX, PIXELINDEX &a_rtnMotionY)
2341
MovedRegion *pSurvivor;
2342
// The only region to survive pruning -- the largest one, with
2343
// the shortest motion vector.
2345
// Make sure they didn't start us off with an error.
2346
assert (a_reStatus == g_kNoError);
2348
// Keep testing best-active-region candidates until one really is the
2349
// best, or until the match-throttles are no longer being exceeded.
2350
while (m_oSearchBorder.GetSizeOfLargestActiveRegion() > 0)
2352
// Get the best active region to apply to the frame now.
2353
// This also gives us the size of the second-best active-region.
2354
FRAMESIZE tnSecondBestActiveRegionSize = 0;
2355
pSurvivor = m_oSearchBorder.ChooseBestActiveRegion (a_reStatus,
2356
tnSecondBestActiveRegionSize);
2357
if (a_reStatus != g_kNoError)
2360
// Get the region's motion vector.
2361
pSurvivor->GetMotionVector (a_rtnMotionX, a_rtnMotionY);
2363
// Remove all pixels that were already resolved.
2364
m_oPruningFloodFillControl.SetupForFloodFill
2365
(a_rtnMotionX, a_rtnMotionY);
2366
pSurvivor->FloodFill (a_reStatus,
2367
m_oPruningFloodFillControl, true, false);
2368
if (a_reStatus != g_kNoError)
2370
m_oSearchBorder.DeleteRegion (pSurvivor);
2374
#ifdef PRINT_SEARCHBORDER
2375
if (frame == 61 && DIM == 2)
2377
fprintf (stderr, "Flood-filled region:\n");
2378
PrintRegion (*pSurvivor);
2379
fprintf (stderr, "\n");
2381
#endif // PRINT_SEARCHBORDER
2383
// Remember the number of points in the region.
2384
FRAMESIZE tnSurvivorSize = FRAMESIZE (pSurvivor->NumberOfPoints());
2386
// If this region has no unresolved pixels (or too few), try again.
2387
if (tnSurvivorSize < PGW * PGH)
2389
// This region is no longer needed.
2390
m_oSearchBorder.DeleteRegion (pSurvivor);
2392
// Go back and try again.
2396
// If this region is no longer the best choice, put it back
2398
if (tnSurvivorSize < tnSecondBestActiveRegionSize)
2401
m_oSearchBorder.AddNewRegion (a_reStatus, *pSurvivor);
2402
m_oSearchBorder.DeleteRegion (pSurvivor);
2403
if (a_reStatus != g_kNoError)
2406
// If the match-size-throttle is no longer exceeded,
2407
// then let our caller know that no throttling was needed.
2408
if (tnSecondBestActiveRegionSize < m_nMatchSizeThrottle)
2415
// Apply this region to the new frame.
2416
ApplyRegionToNewFrame (a_reStatus, *pSurvivor);
2417
if (a_reStatus != g_kNoError)
2419
m_oSearchBorder.DeleteRegion (pSurvivor);
2423
// Clean up the region, return the number of points it had.
2424
m_oSearchBorder.DeleteRegion (pSurvivor);
2425
return tnSurvivorSize;
2428
// No region was applied to the frame.
2434
// Default constructor.
2435
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2436
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2437
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2438
class REFERENCEFRAME>
2439
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2440
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2441
REFERENCEFRAME>::ZeroMotionFloodFillControl
2442
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2443
::ZeroMotionFloodFillControl()
2444
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2445
::ZeroMotionFloodFillControl
2446
(typename BaseClass::Allocator &a_rAllocator)
2447
: BaseClass (a_rAllocator)
2448
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2450
// We don't know who we're working for yet.
2451
m_pMotionSearcher = NULL;
2457
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2458
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2459
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2460
class REFERENCEFRAME>
2462
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2463
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2464
REFERENCEFRAME>::ZeroMotionFloodFillControl::Init
2465
(Status_t &a_reStatus, MotionSearcher *a_pMotionSearcher)
2467
// Make sure they didn't start us off with an error.
2468
assert (a_reStatus == g_kNoError);
2470
// Make sure they gave us a motion-searcher.
2471
assert (a_pMotionSearcher != NULL);
2473
// Initialize our base class.
2474
BaseClass::Init (a_reStatus
2475
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2476
, a_pMotionSearcher->m_tnWidth,
2477
a_pMotionSearcher->m_tnHeight
2478
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2480
if (a_reStatus != g_kNoError)
2483
// Remember which motion-searcher we're working for.
2484
m_pMotionSearcher = a_pMotionSearcher;
2489
// Return true if the flood-fill should examine the given
2490
// extent. May modify the extent.
2491
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2492
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2493
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2494
class REFERENCEFRAME>
2496
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2497
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2498
REFERENCEFRAME>::ZeroMotionFloodFillControl::ShouldUseExtent
2499
(typename ZeroMotionFloodFillControl::BaseClass::Extent &a_rExtent)
2501
#ifdef ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2503
// Make sure the extent doesn't need to be clipped.
2504
assert (a_rExtent.m_tnY >= 0
2505
&& a_rExtent.m_tnY < m_pMotionSearcher->m_tnHeight
2506
&& a_rExtent.m_tnXStart >= 0
2507
&& a_rExtent.m_tnXStart < m_pMotionSearcher->m_tnWidth
2508
&& a_rExtent.m_tnXEnd > 0
2509
&& a_rExtent.m_tnXEnd <= m_pMotionSearcher->m_tnWidth);
2511
#else // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2513
// If this extent is completely off the screen, skip it.
2514
if (a_rExtent.m_tnY < 0
2515
|| a_rExtent.m_tnY >= m_pMotionSearcher->m_tnHeight
2516
|| a_rExtent.m_tnXStart >= m_pMotionSearcher->m_tnWidth
2517
|| a_rExtent.m_tnXEnd <= 0)
2520
// If this extent is partially off the screen, clip it.
2521
if (a_rExtent.m_tnXStart < 0)
2522
a_rExtent.m_tnXStart = 0;
2523
if (a_rExtent.m_tnXEnd > m_pMotionSearcher->m_tnWidth)
2524
a_rExtent.m_tnXEnd = m_pMotionSearcher->m_tnWidth;
2526
#endif // ZERO_MOTION_FLOOD_FILL_WITH_BITMAP_REGIONS
2528
// Let our caller know to use this extent.
2534
// Returns true if the given point should be included in the
2536
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2537
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2538
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2539
class REFERENCEFRAME>
2541
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2542
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2543
REFERENCEFRAME>::ZeroMotionFloodFillControl::IsPointInRegion
2544
(PIXELINDEX a_tnX, PIXELINDEX a_tnY)
2546
// If this pixel has been resolved, skip it.
2547
if (m_pMotionSearcher->m_pNewFrame->GetPixel (a_tnX, a_tnY) != NULL)
2550
// Get the pixels of interest.
2551
const Pixel_t &rNewPixel = m_pMotionSearcher->m_pNewFramePixels
2552
[a_tnY * m_pMotionSearcher->m_tnWidth + a_tnX];
2553
ReferencePixel_t *pRefPixel
2554
= m_pMotionSearcher->m_pReferenceFrame->GetPixel (a_tnX, a_tnY);
2556
// If the new pixel is close enough to the reference pixel, the
2557
// point is in the region.
2558
if (rNewPixel.IsWithinTolerance
2559
(pRefPixel->GetValue(), m_pMotionSearcher->m_tnZeroTolerance))
2561
// The point is in the region.
2565
// The point is not in the region.
2571
// Default constructor.
2572
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2573
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2574
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2575
class REFERENCEFRAME>
2576
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2577
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2578
REFERENCEFRAME>::MatchThrottleFloodFillControl
2579
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2580
::MatchThrottleFloodFillControl()
2581
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2582
::MatchThrottleFloodFillControl
2583
(typename BaseClass::Allocator &a_rAllocator)
2584
: BaseClass (a_rAllocator)
2585
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2587
// We don't know who we're working for yet.
2588
m_pMotionSearcher = NULL;
2594
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2595
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2596
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2597
class REFERENCEFRAME>
2599
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2600
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2601
REFERENCEFRAME>::MatchThrottleFloodFillControl::Init
2602
(Status_t &a_reStatus, MotionSearcher *a_pMotionSearcher)
2604
// Make sure they didn't start us off with an error.
2605
assert (a_reStatus == g_kNoError);
2607
// Make sure they gave us a motion-searcher.
2608
assert (a_pMotionSearcher != NULL);
2610
// Initialize our base class.
2611
BaseClass::Init (a_reStatus
2612
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2613
, a_pMotionSearcher->m_tnWidth,
2614
a_pMotionSearcher->m_tnHeight
2615
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2617
if (a_reStatus != g_kNoError)
2620
// Remember which motion-searcher we're working for.
2621
m_pMotionSearcher = a_pMotionSearcher;
2626
// Set up to to a flood-fill.
2627
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2628
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2629
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2630
class REFERENCEFRAME>
2632
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2633
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2634
REFERENCEFRAME>::MatchThrottleFloodFillControl::SetupForFloodFill
2635
(PIXELINDEX a_tnMotionX, PIXELINDEX a_tnMotionY)
2637
// Save the motion vector.
2638
m_tnMotionX = a_tnMotionX;
2639
m_tnMotionY = a_tnMotionY;
2644
// Return true if the flood-fill should examine the given
2645
// extent. May modify the extent.
2646
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2647
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2648
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2649
class REFERENCEFRAME>
2651
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2652
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2653
REFERENCEFRAME>::MatchThrottleFloodFillControl::ShouldUseExtent
2654
(typename Region_t::Extent &a_rExtent)
2656
#ifdef MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2658
// Make sure the extent doesn't need to be clipped.
2659
assert (a_rExtent.m_tnY >= 0
2660
&& a_rExtent.m_tnY < m_pMotionSearcher->m_tnHeight
2661
&& a_rExtent.m_tnXStart >= 0
2662
&& a_rExtent.m_tnXStart < m_pMotionSearcher->m_tnWidth
2663
&& a_rExtent.m_tnXEnd > 0
2664
&& a_rExtent.m_tnXEnd <= m_pMotionSearcher->m_tnWidth);
2666
// If this extent (with its motion vector) is completely off the
2668
if (a_rExtent.m_tnY + m_tnMotionY < 0
2669
|| a_rExtent.m_tnY + m_tnMotionY >= m_pMotionSearcher->m_tnHeight
2670
|| a_rExtent.m_tnXStart + m_tnMotionX >= m_pMotionSearcher->m_tnWidth
2671
|| a_rExtent.m_tnXEnd + m_tnMotionX <= 0)
2674
// If this extent (with its motion vector) is partially off the
2676
if (a_rExtent.m_tnXStart + m_tnMotionX < 0)
2677
a_rExtent.m_tnXStart = -m_tnMotionX;
2678
if (a_rExtent.m_tnXEnd + m_tnMotionX > m_pMotionSearcher->m_tnWidth)
2679
a_rExtent.m_tnXEnd = m_pMotionSearcher->m_tnWidth - m_tnMotionX;
2681
#else // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2683
// If this extent is completely off the screen, skip it.
2684
if (a_rExtent.m_tnY < 0
2685
|| a_rExtent.m_tnY >= m_pMotionSearcher->m_tnHeight
2686
|| a_rExtent.m_tnXStart >= m_pMotionSearcher->m_tnWidth
2687
|| a_rExtent.m_tnXEnd <= 0
2688
|| a_rExtent.m_tnY + m_tnMotionY < 0
2689
|| a_rExtent.m_tnY + m_tnMotionY >= m_pMotionSearcher->m_tnHeight
2690
|| a_rExtent.m_tnXStart + m_tnMotionX >= m_pMotionSearcher->m_tnWidth
2691
|| a_rExtent.m_tnXEnd + m_tnMotionX <= 0)
2694
// If this extent is partially off the screen, clip it.
2695
if (a_rExtent.m_tnXStart + m_tnMotionX < 0)
2696
a_rExtent.m_tnXStart = -m_tnMotionX;
2697
if (a_rExtent.m_tnXEnd + m_tnMotionX > m_pMotionSearcher->m_tnWidth)
2698
a_rExtent.m_tnXEnd = m_pMotionSearcher->m_tnWidth - m_tnMotionX;
2699
if (a_rExtent.m_tnXStart < 0)
2700
a_rExtent.m_tnXStart = 0;
2701
if (a_rExtent.m_tnXEnd > m_pMotionSearcher->m_tnWidth)
2702
a_rExtent.m_tnXEnd = m_pMotionSearcher->m_tnWidth;
2704
#endif // MATCH_THROTTLE_FLOOD_FILL_WITH_BITMAP_REGIONS
2706
// Let our caller know to use this extent.
2712
// Returns true if the given point should be included in the
2714
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2715
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2716
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2717
class REFERENCEFRAME>
2719
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2720
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2721
REFERENCEFRAME>::MatchThrottleFloodFillControl::IsPointInRegion
2722
(PIXELINDEX a_tnX, PIXELINDEX a_tnY)
2724
// Get the new pixel, if any.
2725
ReferencePixel_t *pNewPixel = m_pMotionSearcher->m_pNewFrame
2726
->GetPixel (a_tnX, a_tnY);
2728
// We can potentially flood-fill this pixel if it's unresolved or
2729
// if it thinks it's new data.
2730
if (pNewPixel == NULL || pNewPixel->GetFrameReferences() == 1)
2732
// Get the reference pixel's coordinates.
2733
PIXELINDEX tnRefX = a_tnX + m_tnMotionX;
2734
PIXELINDEX tnRefY = a_tnY + m_tnMotionY;
2736
// If the corresponding reference pixel hasn't been
2737
// used already, see if it matches our pixel.
2738
#ifdef USE_REFERENCEFRAMEPIXELS_ONCE
2739
if (!m_pMotionSearcher->m_oUsedReferencePixels.DoesContainPoint
2741
#endif // USE_REFERENCEFRAMEPIXELS_ONCE
2743
// If the new pixel is close enough to it, the point is in
2745
ReferencePixel_t *pRefPixel
2746
= m_pMotionSearcher->m_pReferenceFrame->GetPixel
2748
const Pixel_t &rNewPixel = m_pMotionSearcher
2749
->m_pNewFramePixels[a_tnY
2750
* m_pMotionSearcher->m_tnWidth + a_tnX];
2751
if (rNewPixel.IsWithinTolerance (pRefPixel->GetValue(),
2752
m_pMotionSearcher->m_tnTolerance))
2754
// Let our caller know the point is in the region.
2760
// Let our caller know the point is not in the region.
2766
// Default constructor.
2767
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2768
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2769
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2770
class REFERENCEFRAME>
2771
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2772
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2773
REFERENCEFRAME>::PruningFloodFillControl
2774
#ifdef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
2775
::PruningFloodFillControl()
2776
#else // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
2777
::PruningFloodFillControl
2778
(typename BaseClass::Allocator &a_rAllocator)
2779
: BaseClass (a_rAllocator)
2780
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
2782
// We don't know who we're working for yet.
2783
m_pMotionSearcher = NULL;
2789
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2790
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2791
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2792
class REFERENCEFRAME>
2794
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2795
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2796
REFERENCEFRAME>::PruningFloodFillControl::Init
2797
(Status_t &a_reStatus, MotionSearcher *a_pMotionSearcher)
2799
// Make sure they didn't start us off with an error.
2800
assert (a_reStatus == g_kNoError);
2802
// Make sure they gave us a motion-searcher.
2803
assert (a_pMotionSearcher != NULL);
2805
// Initialize our base class.
2806
BaseClass::Init (a_reStatus
2807
#ifdef PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
2808
, a_pMotionSearcher->m_tnWidth,
2809
a_pMotionSearcher->m_tnHeight
2810
#endif // PRUNING_FLOOD_FILL_WITH_BITMAP_REGIONS
2812
if (a_reStatus != g_kNoError)
2815
// Remember which motion-searcher we're working for.
2816
m_pMotionSearcher = a_pMotionSearcher;
2821
// Set up to to a flood-fill.
2822
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2823
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2824
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2825
class REFERENCEFRAME>
2827
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2828
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2829
REFERENCEFRAME>::PruningFloodFillControl::SetupForFloodFill
2830
(PIXELINDEX a_tnMotionX, PIXELINDEX a_tnMotionY)
2832
// Save the motion vector.
2833
m_tnMotionX = a_tnMotionX;
2834
m_tnMotionY = a_tnMotionY;
2839
// Return true if the flood-fill should examine the given
2840
// extent. May modify the extent.
2841
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2842
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2843
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2844
class REFERENCEFRAME>
2846
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2847
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2848
REFERENCEFRAME>::PruningFloodFillControl::ShouldUseExtent
2849
(typename Region_t::Extent &a_rExtent)
2851
// Make sure the extent doesn't need to be clipped.
2852
// (This style of flood-filling doesn't try to generate new extents
2853
// that would have to be filtered.)
2854
assert (a_rExtent.m_tnY >= 0
2855
&& a_rExtent.m_tnY < m_pMotionSearcher->m_tnHeight
2856
&& a_rExtent.m_tnXStart >= 0
2857
&& a_rExtent.m_tnXStart < m_pMotionSearcher->m_tnWidth
2858
&& a_rExtent.m_tnXEnd > 0
2859
&& a_rExtent.m_tnXEnd <= m_pMotionSearcher->m_tnWidth);
2861
// Let our caller know to use this extent.
2867
// Returns true if the given point should be included in the
2869
template <class PIXEL_NUM, int DIM, class PIXEL_TOL, class PIXELINDEX,
2870
class FRAMESIZE, PIXELINDEX PGW, PIXELINDEX PGH,
2871
class SORTERBITMASK, class PIXEL, class REFERENCEPIXEL,
2872
class REFERENCEFRAME>
2874
MotionSearcher<PIXEL_NUM,DIM,PIXEL_TOL,PIXELINDEX,FRAMESIZE,
2875
PGW,PGH,SORTERBITMASK,PIXEL,REFERENCEPIXEL,
2876
REFERENCEFRAME>::PruningFloodFillControl::IsPointInRegion
2877
(PIXELINDEX a_tnX, PIXELINDEX a_tnY)
2879
// Get the new pixel, if any.
2880
ReferencePixel_t *pNewPixel = m_pMotionSearcher->m_pNewFrame
2881
->GetPixel (a_tnX, a_tnY);
2883
// We can potentially flood-fill this pixel if it's unresolved or
2884
// if it thinks it's new data.
2885
if (pNewPixel == NULL || pNewPixel->GetFrameReferences() == 1)
2887
// Get the reference pixel's coordinates.
2888
PIXELINDEX tnRefX = a_tnX + m_tnMotionX;
2889
PIXELINDEX tnRefY = a_tnY + m_tnMotionY;
2891
// If the corresponding reference pixel hasn't been
2892
// used already, it matches our pixel.
2893
#ifdef USE_REFERENCEFRAMEPIXELS_ONCE
2894
if (!m_pMotionSearcher->m_oUsedReferencePixels.DoesContainPoint
2896
#endif // USE_REFERENCEFRAMEPIXELS_ONCE
2898
// Let our caller know the point is in the region.
2903
// Let our caller know the point is not in the region.
2909
#endif // __MOTION_SEARCHER_H__