Set a reference image for each zone

This commit is contained in:
manupap1 2014-11-11 21:48:56 +01:00
parent 0d430b5f4f
commit 217aafee5a
4 changed files with 231 additions and 100 deletions

View File

@ -311,8 +311,6 @@ Monitor::Monitor(
alarm_ref_blend_perc( p_alarm_ref_blend_perc ),
track_motion( p_track_motion ),
signal_check_colour( p_signal_check_colour ),
delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ),
ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),
purpose( p_purpose ),
last_motion_score(0),
camera( p_camera ),
@ -484,7 +482,6 @@ Monitor::Monitor(
Warning( "Waiting for capture daemon" );
sleep( 1 );
}
ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize());
n_linked_monitors = 0;
linked_monitors = 0;
@ -1217,7 +1214,10 @@ bool Monitor::Analyse()
{
Info( "Received resume indication at count %d", image_count );
shared_data->active = true;
ref_image = *snap_image;
for ( int n_zone = 0; n_zone < n_zones; n_zone++ )
{
zones[n_zone]->SetRefImage(*snap_image);
}
ready_count = image_count+(warmup_count/2);
shared_data->alarm_x = shared_data->alarm_y = -1;
}
@ -1228,7 +1228,10 @@ bool Monitor::Analyse()
{
Info( "Auto resuming at count %d", image_count );
shared_data->active = true;
ref_image = *snap_image;
for ( int n_zone = 0; n_zone < n_zones; n_zone++ )
{
zones[n_zone]->SetRefImage(*snap_image);
}
ready_count = image_count+(warmup_count/2);
auto_resume_time = 0;
}
@ -1300,32 +1303,36 @@ bool Monitor::Analyse()
noteSetMap[SIGNAL_CAUSE] = noteSet;
shared_data->state = state = IDLE;
shared_data->active = signal;
ref_image = *snap_image;
for ( int n_zone = 0; n_zone < n_zones; n_zone++ )
{
zones[n_zone]->SetRefImage(*snap_image);
}
}
else if ( signal && Active() && (function == MODECT || function == MOCORD) )
{
Event::StringSet zoneSet;
int motion_score = last_motion_score;
unsigned int motion_score = last_motion_score;
bool alarm = false;
if ( !(image_count % (motion_frame_skip+1) ) )
{
// Get new score.
motion_score = last_motion_score = DetectMotion( *snap_image, zoneSet );
alarm = DetectMotion( *snap_image, zoneSet, motion_score );
last_motion_score = motion_score;
}
//int motion_score = DetectBlack( *snap_image, zoneSet );
if ( motion_score )
if ( alarm )
{
if ( !event )
if ( motion_score )
{
score += motion_score;
if ( cause.length() )
cause += ", ";
cause += MOTION_CAUSE;
if ( !event )
{
if ( cause.length() )
cause += ", ";
cause += MOTION_CAUSE;
}
noteSetMap[MOTION_CAUSE] = zoneSet;
}
else
{
score += motion_score;
}
noteSetMap[MOTION_CAUSE] = zoneSet;
}
shared_data->active = signal;
@ -1617,10 +1624,10 @@ bool Monitor::Analyse()
}
if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) )
{
if ( state == ALARM ) {
ref_image.Blend( *snap_image, alarm_ref_blend_perc );
} else {
ref_image.Blend( *snap_image, ref_blend_perc );
int ref_blend = ( state == ALARM ) ? alarm_ref_blend_perc : ref_blend_perc;
for ( int n_zone = 0; n_zone < n_zones; n_zone++ )
{
zones[n_zone]->BlendRefImage( *snap_image, ref_blend );
}
}
last_signal = signal;
@ -3166,33 +3173,31 @@ unsigned int Monitor::DetectBlack(const Image &comp_image, Event::StringSet &zon
unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet )
unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet, unsigned int &score )
{
bool alarm = false;
unsigned int score = 0;
score = 0;
if ( n_zones <= 0 ) return( alarm );
if ( config.record_diag_images )
for ( int n_zone = 0; n_zone < n_zones; n_zone++ )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
Zone *zone = zones[n_zone];
if ( config.record_diag_images )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id );
static char diag_path[PATH_MAX] = "";
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-r.jpg", config.dir_events, id, zone->Id() );
zone->WriteRefImage( diag_path );
}
ref_image.WriteJpeg( diag_path );
}
ref_image.Delta( comp_image, &delta_image);
zone->SetDeltaImage( comp_image );
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
if ( config.record_diag_images )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id );
static char diag_path[PATH_MAX] = "";
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-d.jpg", config.dir_events, id, zone->Id() );
zone->WriteDeltaImage( diag_path );
}
delta_image.WriteJpeg( diag_path );
}
// Blank out all exclusion zones
@ -3207,7 +3212,7 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
continue;
}
Debug( 3, "Blanking inactive zone %s", zone->Label() );
delta_image.Fill( RGB_BLACK, zone->GetPolygon() );
zone->FillDeltaImage( RGB_BLACK );
}
// Check preclusive zones first
@ -3221,13 +3226,16 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
int old_zone_score = zone->Score();
bool old_zone_alarmed = zone->Alarmed();
Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" );
if ( zone->CheckAlarms( &delta_image ) )
if ( zone->CheckAlarms( &comp_image ) )
{
alarm = true;
score += zone->Score();
zone->SetAlarm();
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( zone->Label() );
if ( !zone->IsPostProcEnabled() )
{
zone->SetAlarm();
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() );
}
//zone->ResetStats();
} else {
// check if end of alarm
@ -3265,24 +3273,26 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
continue;
}
Debug( 3, "Checking active zone %s", zone->Label() );
if ( zone->CheckAlarms( &delta_image ) )
if ( zone->CheckAlarms( &comp_image ) )
{
alarm = true;
score += zone->Score();
zone->SetAlarm();
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( zone->Label() );
if ( config.opt_control && track_motion )
score += zone->Score();
if ( !zone->IsPostProcEnabled() )
{
if ( (int)zone->Score() > top_score )
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() );
if ( config.opt_control && track_motion )
{
top_score = zone->Score();
alarm_centre = zone->GetAlarmCentre();
if ( (int)zone->Score() > top_score )
{
top_score = zone->Score();
alarm_centre = zone->GetAlarmCentre();
}
}
}
}
}
if ( alarm )
{
for ( int n_zone = 0; n_zone < n_zones; n_zone++ )
@ -3293,19 +3303,22 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
continue;
}
Debug( 3, "Checking inclusive zone %s", zone->Label() );
if ( zone->CheckAlarms( &delta_image ) )
if ( zone->CheckAlarms( &comp_image ) )
{
alarm = true;
score += zone->Score();
zone->SetAlarm();
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( zone->Label() );
if ( config.opt_control && track_motion )
score += zone->Score();
if ( !zone->IsPostProcEnabled() )
{
if ( zone->Score() > (unsigned int)top_score )
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() );
if ( config.opt_control && track_motion )
{
top_score = zone->Score();
alarm_centre = zone->GetAlarmCentre();
if ( zone->Score() > (unsigned int)top_score )
{
top_score = zone->Score();
alarm_centre = zone->GetAlarmCentre();
}
}
}
}
@ -3322,13 +3335,16 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
continue;
}
Debug( 3, "Checking exclusive zone %s", zone->Label() );
if ( zone->CheckAlarms( &delta_image ) )
if ( zone->CheckAlarms( &comp_image ) )
{
alarm = true;
score += zone->Score();
zone->SetAlarm();
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( zone->Label() );
score += zone->Score();
if ( !zone->IsPostProcEnabled() )
{
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() );
}
}
}
}
@ -3346,9 +3362,8 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
shared_data->alarm_x = shared_data->alarm_y = -1;
}
// This is a small and innocent hack to prevent scores of 0 being returned in alarm state
return( score?score:alarm );
}
return alarm;
}
bool Monitor::DumpSettings( char *output, bool verbose )
{

View File

@ -244,8 +244,6 @@ protected:
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
double fps;
Image delta_image;
Image ref_image;
Purpose purpose; // What this monitor has been created to do
int event_count;
@ -385,6 +383,7 @@ public:
}
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet, unsigned int &score );
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
bool CheckSignal( const Image *image );

View File

@ -60,10 +60,20 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_
max_blob_size = 0;
image = 0;
score = 0;
text = "";
post_proc_enabled = false;
post_proc_in_progress = false;
include_nat_det = true;
reinit_nat_det = false;
overload_count = 0;
extend_alarm_count = 0;
delta_image = Image( monitor->Width(), monitor->Height(), ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE );
ref_image = Image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder());
bl_image = Image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder());
bl_image.Fill( RGB_BLACK );
pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE);
pg_image->Clear();
pg_image->Fill( 0xff, polygon );
@ -91,14 +101,10 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_
}
}
}
if ( config.record_diag_images )
if ( config.record_diag_images && (id > 0))
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id);
}
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id);
pg_image->WriteJpeg( diag_path );
}
}
@ -142,6 +148,37 @@ void Zone::SetScore(unsigned int nScore)
score = nScore;
}
void Zone::SetText(std::string sText)
{
text = "[Zone ";
text += label;
text += "]\n" + sText;
}
void Zone::AssignRefImage( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size )
{
ref_image.Assign( p_width, p_height, p_colours, p_subpixelorder, new_buffer, buffer_size);
}
void Zone::SetRefImage(const Image &srcImage)
{
ref_image = srcImage;
}
void Zone::BlendRefImage( const Image &srcImage, int transparency )
{
ref_image.Blend( srcImage, transparency );
}
void Zone::SetDeltaImage( const Image &srcImage )
{
ref_image.Delta( srcImage, &delta_image );
}
void Zone::FillDeltaImage( Rgb colour )
{
delta_image.Fill( colour, polygon);
}
void Zone::SetAlarmImage(const Image* srcImage)
{
@ -149,6 +186,16 @@ void Zone::SetAlarmImage(const Image* srcImage)
image = new Image(*srcImage);
}
bool Zone::WriteRefImage( const char *filename, int quality_override ) const
{
return ref_image.WriteJpeg( filename, quality_override );
}
bool Zone::WriteDeltaImage( const char *filename, int quality_override ) const
{
return delta_image.WriteJpeg( filename, quality_override );
}
int Zone::GetOverloadCount()
{
return overload_count;
@ -194,9 +241,33 @@ bool Zone::CheckExtendAlarmCount()
//===========================================================================
void Zone::SetConfig( zConf zone_conf )
{
post_proc_enabled = zone_conf.RequireNatDet;
include_nat_det = zone_conf.IncludeNatDet;
reinit_nat_det = zone_conf.ReInitNatDet;
if ( post_proc_enabled ) {
std::string sMessage;
if ( include_nat_det ) {
sMessage = "(native detection included";
}
if ( reinit_nat_det ) {
if (sMessage.empty()) {
sMessage = "(native detection will be reinitialized";
} else {
sMessage += ", reinitialization is required";
}
}
if (!sMessage.empty()) {
sMessage += ")";
}
Info("Post processing enabled for zone '%s' %s", label, sMessage.c_str());
}
}
bool Zone::CheckAlarms( const Image *delta_image )
bool Zone::CheckAlarms( const Image *comp_image )
{
ResetStats();
@ -205,12 +276,13 @@ bool Zone::CheckAlarms( const Image *delta_image )
Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
overload_count--;
post_proc_in_progress = false;
return( false );
}
delete image;
// Get the difference image
Image *diff_image = image = new Image( *delta_image );
Image *diff_image = image = new Image( delta_image );
int diff_width = diff_image->Width();
uint8_t* diff_buff = (uint8_t*)diff_image->Buffer();
uint8_t* pdiff;
@ -245,10 +317,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 );
}
snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 );
diff_image->WriteJpeg( diag_path );
}
@ -259,14 +328,17 @@ bool Zone::CheckAlarms( const Image *delta_image )
if( alarm_pixels ) {
if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
/* Not enough pixels alarmed */
post_proc_in_progress = false;
return (false);
} else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) {
/* Too many pixels alarmed */
overload_count = overload_frames;
post_proc_in_progress = false;
return (false);
}
} else {
/* No alarmed pixels */
post_proc_in_progress = false;
return (false);
}
@ -342,10 +414,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 );
}
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 );
diff_image->WriteJpeg( diag_path );
}
@ -354,14 +423,17 @@ bool Zone::CheckAlarms( const Image *delta_image )
if( alarm_filter_pixels ) {
if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
/* Not enough pixels alarmed */
post_proc_in_progress = false;
return (false);
} else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) {
/* Too many pixels alarmed */
overload_count = overload_frames;
post_proc_in_progress = false;
return (false);
}
} else {
/* No filtered pixels */
post_proc_in_progress = false;
return (false);
}
@ -583,15 +655,13 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 );
}
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 );
diff_image->WriteJpeg( diag_path );
}
if ( !alarm_blobs )
{
post_proc_in_progress = false;
return( false );
}
@ -642,10 +712,7 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 );
}
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 );
diff_image->WriteJpeg( diag_path );
}
Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs );
@ -653,14 +720,17 @@ bool Zone::CheckAlarms( const Image *delta_image )
if( alarm_blobs ) {
if( min_blobs && (alarm_blobs < min_blobs) ) {
/* Not enough pixels alarmed */
post_proc_in_progress = false;
return (false);
} else if(max_blobs && (alarm_blobs > max_blobs) ) {
/* Too many pixels alarmed */
overload_count = overload_frames;
post_proc_in_progress = false;
return (false);
}
} else {
/* No blobs */
post_proc_in_progress = false;
return (false);
}
@ -797,13 +867,23 @@ bool Zone::CheckAlarms( const Image *delta_image )
}
}
}
if( monitor->Colours() == ZM_COLOUR_GRAY8 ) {
image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() );
} else {
image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() );
if ( post_proc_enabled ) {
post_proc_in_progress = true;
}
if ( include_nat_det ) {
if( monitor->Colours() == ZM_COLOUR_GRAY8 ) {
image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() );
} else {
image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() );
}
} else {
score = 0;
image = new Image( bl_image );
}
// Update reference image if required
if ( reinit_nat_det ) {
ref_image = *comp_image;
}
// Only need to delete this when 'image' becomes detached and points somewhere else
delete diff_image;
}

View File

@ -28,6 +28,19 @@
class Monitor;
//! A structure to store the post processing configuration for the zone
struct zConf
{
bool RequireNatDet;
bool IncludeNatDet;
bool ReInitNatDet;
zConf():
RequireNatDet(false),
IncludeNatDet(false),
ReInitNatDet(false)
{}
};
//
// This describes a 'zone', or an area of an image that has certain
// detection characteristics.
@ -87,12 +100,20 @@ protected:
Box alarm_box;
Coord alarm_centre;
unsigned int score;
std::string text;
Image *pg_image;
Range *ranges;
Image *image;
Image delta_image;
Image ref_image;
Image bl_image;
int overload_count;
int extend_alarm_count;
bool post_proc_enabled;
bool include_nat_det;
bool reinit_nat_det;
bool post_proc_in_progress;
protected:
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
@ -130,7 +151,14 @@ public:
inline void ClearAlarm() { alarmed = false; }
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
inline unsigned int Score() const { return( score ); }
inline std::string Text() const { return( text ); }
void SetConfig( zConf zone_conf );
inline bool IsPostProcEnabled() const { return post_proc_enabled; }
inline bool IsNatDetIncluded() const { return include_nat_det; }
inline bool IsNatDetReInitialized() const { return reinit_nat_det; }
inline void StartPostProcessing() { post_proc_in_progress = true; }
inline void StopPostProcessing() { post_proc_in_progress = false; }
inline bool IsPostProcInProgress() { return post_proc_in_progress; }
inline void ResetStats()
{
alarmed = false;
@ -142,9 +170,10 @@ public:
min_blob_size = 0;
max_blob_size = 0;
score = 0;
text = "";
}
void RecordStats( const Event *event );
bool CheckAlarms( const Image *delta_image );
bool CheckAlarms( const Image *comp_image );
bool DumpSettings( char *output, bool verbose );
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
@ -161,7 +190,15 @@ public:
void SetExtendAlarmCount(int nOverCount);
int GetExtendAlarmFrames();
void SetScore(unsigned int nScore);
void SetText(std::string sText);
void SetAlarmImage(const Image* srcImage);
void AssignRefImage( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size );
void SetRefImage( const Image &srcImage);
void BlendRefImage( const Image &srcImage, int transparency=12 );
void SetDeltaImage( const Image &srcImage );
void FillDeltaImage( Rgb colour );
bool WriteRefImage( const char *filename, int quality_override=0 ) const;
bool WriteDeltaImage( const char *filename, int quality_override=0 ) const;
inline const Image *getPgImage() const { return( pg_image ); }
inline const Range *getRanges() const { return( ranges ); }