diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 18d3d0db8..49390a036 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -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 ) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 8addd658c..824818d0a 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -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 ); diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 90c954d9e..498cce6a5 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -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; } diff --git a/src/zm_zone.h b/src/zm_zone.h index c431f88a0..6e9267b1c 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -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 ); }