diff --git a/src/zm_image.cpp b/src/zm_image.cpp index ae511d881..121dcf1ca 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -580,7 +580,12 @@ bool Image::ReadJpeg( const char *filename, int p_colours, int p_subpixelorder) jpeg_stdio_src( cinfo, infile ); jpeg_read_header( cinfo, TRUE ); - + + /* Check if the image has huffman tables defined. If not, add the standard ones */ + if(cinfo->dc_huff_tbl_ptrs[0] == NULL || cinfo->ac_huff_tbl_ptrs[0] == NULL) { + zm_add_std_huff_tables(cinfo); + } + if ( cinfo->image_width != width || cinfo->image_height != height) { width = cinfo->image_width; @@ -642,11 +647,14 @@ bool Image::ReadJpeg( const char *filename, int p_colours, int p_subpixelorder) #endif } else { /* Assume RGB */ +/* #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; #endif +*/ + cinfo->out_color_space = JCS_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB; } break; @@ -763,11 +771,14 @@ bool Image::WriteJpeg( const char *filename, int quality_override ) const #endif } else { /* Assume RGB */ +/* #ifdef JCS_EXTENSIONS - cinfo->in_color_space = JCS_EXT_RGB; + cinfo->out_color_space = JCS_EXT_RGB; #else + cinfo->out_color_space = JCS_RGB; +#endif +*/ cinfo->in_color_space = JCS_RGB; -#endif } break; } @@ -820,6 +831,11 @@ bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, int p_colours zm_jpeg_mem_src( cinfo, inbuffer, inbuffer_size ); jpeg_read_header( cinfo, TRUE ); + + /* Check if the image has huffman tables defined. If not, add the standard ones */ + if(cinfo->dc_huff_tbl_ptrs[0] == NULL || cinfo->ac_huff_tbl_ptrs[0] == NULL) { + zm_add_std_huff_tables(cinfo); + } if ( cinfo->image_width != width || cinfo->image_height != height) { @@ -881,11 +897,14 @@ bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, int p_colours #endif } else { /* Assume RGB */ +/* #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; -#endif +#endif +*/ + cinfo->out_color_space = JCS_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB; } break; @@ -992,11 +1011,14 @@ bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_over #endif } else { /* Assume RGB */ +/* #ifdef JCS_EXTENSIONS - cinfo->in_color_space = JCS_EXT_RGB; + cinfo->out_color_space = JCS_EXT_RGB; #else + cinfo->out_color_space = JCS_RGB; +#endif +*/ cinfo->in_color_space = JCS_RGB; -#endif } break; } diff --git a/src/zm_jpeg.cpp b/src/zm_jpeg.cpp index ed745f973..7158e0d8a 100644 --- a/src/zm_jpeg.cpp +++ b/src/zm_jpeg.cpp @@ -392,4 +392,75 @@ void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuff src->pub.next_input_byte = NULL; /* until buffer loaded */ } +void zm_add_std_huff_tables( j_decompress_ptr cinfo ) { +/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ + static const JHUFF_TBL dclumin = { + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + 0 + }; + static const JHUFF_TBL dcchrome = { + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + 0 + }; + static const JHUFF_TBL aclumin = { + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }, + { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }, + 0 + }; + static const JHUFF_TBL acchrome = { + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }, + { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }, + 0 + }; + + cinfo->dc_huff_tbl_ptrs[0] = &dclumin; + cinfo->dc_huff_tbl_ptrs[1] = &dcchrome; + cinfo->ac_huff_tbl_ptrs[0] = &aclumin; + cinfo->ac_huff_tbl_ptrs[1] = &acchrome; + +} + } diff --git a/src/zm_jpeg.h b/src/zm_jpeg.h index 85df4c491..188b75af0 100644 --- a/src/zm_jpeg.h +++ b/src/zm_jpeg.h @@ -44,4 +44,6 @@ void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level ); // Prototypes for memory compress/decompression object */ void zm_jpeg_mem_src(j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size ); void zm_jpeg_mem_dest(j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size ); + +void zm_add_std_huff_tables( j_decompress_ptr cinfo ); } diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index 5f869ff03..e91df4962 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -399,7 +399,7 @@ LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ } else { if( capture ) - Warning("Unable to find a match for the selected palette and target colourspace. Conversion required, performance penalty expected"); + Warning("No match for the selected palette and colourspace. Conversion required, performance penalty expected"); #if HAVE_LIBSWSCALE /* Try using swscale for the conversion */ conversion_type = 1; @@ -437,6 +437,7 @@ LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, /* JPEG */ if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { + Debug(2,"Using JPEG image decoding"); conversion_type = 3; } @@ -514,7 +515,7 @@ LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ } else { if( capture ) - Warning("Unable to find a match for the selected palette and target colourspace. Conversion required, performance penalty expected"); + Warning("No match for the selected palette and colourspace. Conversion required, performance penalty expected"); #if HAVE_LIBSWSCALE /* Try using swscale for the conversion */ conversion_type = 1; @@ -743,19 +744,27 @@ void LocalCamera::Initialise() v4l2_jpegcompression jpeg_comp; if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { - Warning("Failed to get JPEG compression options"); + Warning("Failed to get JPEG compression options: %s", strerror(errno) ); } else { - /* Set JPEG flags and quality */ - jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + /* Set flags and quality. MJPEG should not have the huffman tables defined */ + if(palette == V4L2_PIX_FMT_MJPEG) { + jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + } else { + jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | V4L2_JPEG_MARKER_DHT; + } jpeg_comp.quality = 85; /* Update the JPEG options */ if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { - Warning("Failed to set JPEG compression options"); + Warning("Failed to set JPEG compression options: %s", strerror(errno) ); } else { - Debug(4, "JPEG quality: %d",jpeg_comp.quality); - Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); + if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) { + Debug(3,"Failed to get updated JPEG compression options: %s", strerror(errno) ); + } else { + Debug(4, "JPEG quality: %d",jpeg_comp.quality); + Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); + } } } } @@ -1088,7 +1097,7 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { /* Open the device */ if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { - Error( "Failed to open video device %s: %s", device.c_str(), strerror(errno) ); + Error( "Automatic format selection failed to open video device %s: %s", device.c_str(), strerror(errno) ); return selected_palette; } @@ -2047,20 +2056,22 @@ int LocalCamera::Capture( Image &image ) } #if HAVE_LIBSWSCALE if(conversion_type == 1) { + + Debug( 9, "Calling sws_scale to perform the conversion" ); /* Use swscale to convert the image directly into the shared memory */ - avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height ); - sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); } #endif if(conversion_type == 2) { + Debug( 9, "Calling the conversion function" ); /* Call the image conversion function and convert directly into the shared memory */ (*conversion_fptr)(buffer, directbuffer, pixels); } else if(conversion_type == 3) { + Debug( 9, "Decoding the JPEG image" ); /* JPEG decoding */ image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); } diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index 4853b4cc0..3cd081692 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -199,7 +199,7 @@ __attribute__((noinline)) void* sse2_aligned_memcpy(void* dest, const void* src, const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder); __asm__ __volatile__( - "sse2copy_begin:\n\t" + "sse2_copy_iter:\n\t" "movdqa (%0),%%xmm0\n\t" "movdqa 0x10(%0),%%xmm1\n\t" "movdqa 0x20(%0),%%xmm2\n\t" @@ -219,12 +219,12 @@ __attribute__((noinline)) void* sse2_aligned_memcpy(void* dest, const void* src, "add $0x80, %0\n\t" "add $0x80, %1\n\t" "cmp %2, %0\n\t" - "jb sse2copy_begin\n\t" + "jb sse2_copy_iter\n\t" "test %3, %3\n\t" - "jz sse2copy_finish\n\t" + "jz sse2_copy_finish\n\t" "cld\n\t" "rep movsb\n\t" - "sse2copy_finish:\n\t" + "sse2_copy_finish:\n\t" : : "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index c66e6c517..c67932919 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -60,38 +60,45 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_ image = 0; score = 0; - overload_count = 0; + overload_count = 0; pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); + pg_image->Clear(); pg_image->Fill( 0xff, polygon ); pg_image->Outline( 0xff, polygon ); - pg_image->Crop( polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); - ranges = new Range[polygon.Height()]; - int y = polygon.LoY(); - for ( int py = 0; py < polygon.Height(); py++, y++ ) + ranges = new Range[monitor->Height()]; + for ( int y = polygon.LoY(); y <= polygon.HiY(); y++) { - int x = polygon.LoX(); - ranges[py].lo_x = -1; - ranges[py].hi_x = -1; - ranges[py].off_x = 0; - const uint8_t *ppoly = pg_image->Buffer( 0, py ); - for ( int px = 0; px < polygon.Width(); px++, x++, ppoly++ ) + ranges[y].lo_x = -1; + ranges[y].hi_x = -1; + ranges[y].off_x = 0; + const uint8_t *ppoly = pg_image->Buffer( polygon.LoX(), y ); + for ( int x = polygon.LoX(); x <= polygon.HiX(); x++, ppoly++ ) { if ( *ppoly ) { - if ( ranges[py].lo_x == -1 ) + if ( ranges[y].lo_x == -1 ) { - ranges[py].lo_x = x; - ranges[py].off_x = px; + ranges[y].lo_x = x; } - if ( ranges[py].hi_x < x ) + if ( ranges[y].hi_x < x ) { - ranges[py].hi_x = x; + ranges[y].hi_x = x; } } } } + + 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-poly.jpg", config.dir_events, monitor->Name(), id); + } + pg_image->WriteJpeg( diag_path ); + } } Zone::~Zone() @@ -119,21 +126,23 @@ bool Zone::CheckAlarms( const Image *delta_image ) ResetStats(); - if ( overload_count ) - { - 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--; - return( false ); - } + if ( overload_count ) + { + 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--; + return( false ); + } delete image; // Get the difference 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; + const uint8_t* ppoly; - unsigned long pixel_diff_count = 0; + unsigned int pixel_diff_count = 0; int alarm_lo_x = 0; int alarm_hi_x = 0; @@ -142,47 +151,27 @@ bool Zone::CheckAlarms( const Image *delta_image ) int alarm_mid_x = -1; int alarm_mid_y = -1; - - int lo_y = polygon.LoY(); - int hi_y = polygon.HiY(); - int lo_x; - int hi_x; + + unsigned int lo_y = polygon.LoY(); + unsigned int lo_x = polygon.LoX(); + unsigned int hi_x = polygon.HiX()+1; + unsigned int hi_y = polygon.HiY()+1; Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y ); - - Debug( 5, "Checking for alarmed pixels" ); - uint8_t *pdiff; - const uint8_t *ppoly; - uint8_t calc_max_pixel_threshold = 255; /* To be able to remove one check from the loop below */ - if(max_pixel_threshold) - calc_max_pixel_threshold = max_pixel_threshold; - for ( int y = lo_y, py = 0; y <= hi_y; y++, py++ ) - { - lo_x = ranges[py].lo_x; - hi_x = ranges[py].hi_x; - - Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); - pdiff = diff_buff + ((diff_width * y) + lo_x); - ppoly = pg_image->Buffer( ranges[py].off_x, py ); - - for ( int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) - { - if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) - { - alarm_pixels++; - pixel_diff_count += *pdiff; - *pdiff = WHITE; - } - else - { - *pdiff = BLACK; - } - } - } + + Debug( 5, "Checking for alarmed pixels" ); + /* if(config.cpu_extensions && sseversion >= 20) { + sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); + } else { + std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); + } */ + std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); + if ( pixel_diff_count && alarm_pixels ) pixel_diff = pixel_diff_count/alarm_pixels; Debug( 5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff ); + if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; @@ -193,22 +182,26 @@ bool Zone::CheckAlarms( const Image *delta_image ) diff_image->WriteJpeg( diag_path ); } - if ( !alarm_pixels ) - { - return( false ); - } - if ( min_alarm_pixels && (alarm_pixels < min_alarm_pixels) ) - { - return( false ); - } - if ( max_alarm_pixels && (alarm_pixels > max_alarm_pixels) ) - { - overload_count = overload_frames; - return( false ); - } + if( alarm_pixels ) { + if( min_alarm_pixels && (alarm_pixels < min_alarm_pixels) ) { + /* Not enough pixels alarmed */ + return (false); + } else if( max_alarm_pixels && (alarm_pixels > max_alarm_pixels) ) { + /* Too many pixels alarmed */ + overload_count = overload_frames; + return (false); + } + } else { + /* No alarmed pixels */ + return (false); + } + + score = (100*alarm_pixels)/polygon.Area(); + if(score < 1) + score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug( 5, "Current score is %d", score ); - + if ( check_method >= FILTERED_PIXELS ) { int bx = filter_box.X(); @@ -223,12 +216,12 @@ bool Zone::CheckAlarms( const Image *delta_image ) unsigned char *cpdiff; int ldx, hdx, ldy, hdy; bool block; - for ( int y = lo_y, py = 0; y <= hi_y; y++, py++ ) + for ( int y = lo_y; y <= hi_y; y++ ) { - lo_x = ranges[py].lo_x; - hi_x = ranges[py].hi_x; + lo_x = ranges[y].lo_x; + hi_x = ranges[y].hi_x; - pdiff = diff_buff + ((diff_width * y) + lo_x); + pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) { @@ -310,12 +303,12 @@ bool Zone::CheckAlarms( const Image *delta_image ) int last_x, last_y; BlobStats *bsx, *bsy; BlobStats *bsm, *bss; - for ( int y = lo_y, py = 0; y <= hi_y; y++, py++ ) + for ( int y = lo_y; y <= hi_y; y++ ) { - int lo_x = ranges[py].lo_x; - int hi_x = ranges[py].hi_x; + int lo_x = ranges[y].lo_x; + int hi_x = ranges[y].hi_x; - pdiff = diff_buff + ((diff_width * y) + lo_x); + pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) { if ( *pdiff == WHITE ) @@ -632,11 +625,14 @@ bool Zone::CheckAlarms( const Image *delta_image ) if ( type == INCLUSIVE ) { + // score >>= 1; score /= 2; } else if ( type == EXCLUSIVE ) { + // score <<= 1; score *= 2; + } Debug( 5, "Adjusted score is %d", score ); @@ -665,12 +661,12 @@ bool Zone::CheckAlarms( const Image *delta_image ) int lo_y = polygon.LoY(); int hi_y = polygon.HiY(); // First mask out anything we don't want - for ( int y = lo_y, py = 0; y <= hi_y; y++, py++ ) + for ( int y = lo_y; y <= hi_y; y++ ) { pdiff = diff_buff + ((diff_width * y) + lo_x); - int lo_x2 = ranges[py].lo_x; - int hi_x2 = ranges[py].hi_x; + int lo_x2 = ranges[y].lo_x; + int hi_x2 = ranges[y].hi_x; int lo_gap = lo_x2-lo_x; if ( lo_gap > 0 ) @@ -686,7 +682,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) } } - ppoly = pg_image->Buffer( lo_gap, py ); + ppoly = pg_image->Buffer( lo_gap, y ); for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) { if ( !*ppoly ) @@ -963,3 +959,261 @@ bool Zone::DumpSettings( char *output, bool /*verbose*/ ) return( true ); } +void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) { + uint32_t pixelsalarmed = 0; + uint32_t pixelsdifference = 0; + uint8_t *pdiff; + const uint8_t *ppoly; + uint8_t calc_max_pixel_threshold = 255; + unsigned int lo_y; + unsigned int hi_y; + unsigned int lo_x; + unsigned int hi_x; + + if(max_pixel_threshold) + calc_max_pixel_threshold = max_pixel_threshold; + + lo_y = polygon.LoY(); + hi_y = polygon.HiY(); + for ( int y = lo_y; y <= hi_y; y++ ) + { + lo_x = ranges[y].lo_x; + hi_x = ranges[y].hi_x; + + Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); + pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); + ppoly = ppoly_image->Buffer( lo_x, y ); + + for ( int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) + { + if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) + { + pixelsalarmed++; + pixelsdifference += *pdiff; + *pdiff = WHITE; + } + else + { + *pdiff = BLACK; + } + } + } + + /* Store the results */ + *pixel_count = pixelsalarmed; + *pixel_sum = pixelsdifference; +} + +void Zone::sse2_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) { +#if (defined(__i386__) || defined(__x86_64__)) + __attribute__((aligned(16))) static uint8_t calc_maxpthreshold[16] = {127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127}; + __attribute__((aligned(16))) static uint8_t calc_minpthreshold[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + static uint8_t current_minpthreshold = 0; + static uint8_t current_maxpthreshold = 255; + unsigned int minpthreshold = min_pixel_threshold; + unsigned int maxpthreshold = max_pixel_threshold; + uint32_t pixelsalarmed = 0; + uint32_t pixelsdifference = 0; + unsigned int lo_y = polygon.LoY(); + unsigned int hi_y = polygon.HiY()+1; + unsigned int lo_x = polygon.LoX(); + unsigned int hi_x = polygon.HiX()+1; + + if(maxpthreshold == 0) + maxpthreshold = 255; + + if(minpthreshold != current_minpthreshold) { + for(unsigned int i=0;i>1; + current_minpthreshold = minpthreshold; + } + + if(maxpthreshold != current_maxpthreshold) { + for(unsigned int i=0;i>1; + current_maxpthreshold = maxpthreshold; + } + + /* + * We have to work on 16 byte aligned addresses. + * Assume width is multiples of 16 and align the lo_x and hi_x to be on a 16 byte boundary + */ + if((lo_x % 16) != 0) { + lo_x = lo_x - (lo_x % 16); + } + if((hi_x % 16) != 0) { + hi_x = hi_x + (16 - (hi_x % 16)); + if( hi_x > pdiff_image->Width() ) + /* Clamp hi_x to width */ + hi_x = pdiff_image->Width(); + } + if( hi_y > pdiff_image->Height() ) { + /* Clamp hi y to height */ + hi_y = pdiff_image->Height(); + } + + unsigned int x = lo_x; + unsigned int y = lo_y; + unsigned long xgap = hi_x - lo_x; + unsigned long width = pdiff_image->Width(); + uint8_t* pdiff = (uint8_t*)pdiff_image->Buffer(lo_x, lo_y); + const uint8_t* ppoly = ppoly_image->Buffer(lo_x, lo_y); + + /* Some sanity checks */ + if((width % 16) != 0) { + Fatal("Image width is not multiples of 16!"); + } + if((xgap % 16) != 0) { + /* Shouldn't happen but just in case */ + Fatal("Difference between calculated hi_x and lo_x is not multiples of 16"); + } + if(lo_x == hi_x) { + Error("lo_x and hi_x are identical, nothing to scan. Scanning the whole line instead"); + lo_x = 0; + hi_x = width; + } + + + /* XMM0,1,2,3 - General purpose */ + /* XMM4 - alarmed pixels count */ + /* XMM5 - difference accumulator */ + /* XMM6 - min pixel threshold mask */ + /* XMM7 - max pixel threshold mask */ + /* XMM8 - divide mask */ + /* XMM9 - 0x01 mask */ + /* + Register map: + %0 - pixelsalarmed + %1 - pixelsdifference + %2 - pdiff + %3 - ppoly + %4 - X + %5 - Y + %6 - min pixel mask + %7 - max pixel mask + %8 - lo_y + %9 - lo_x + %10 - hi_x + %11 - hi_y + %12 - width + %13 - xgap + */ + + /* Initial setup + * set X to lo_x + * set Y to lo_y + * set pdiff to pdiff start + (width * lo_y) + lo_x + * set ppoly to ppoly start + (width * lo_y) + lo_x + * set xgap to hi_x - lo_x + */ + + __asm__ __volatile__ ( + "pxor %%xmm4, %%xmm4\n\t" // Zero out the alarmed pixels count + "pxor %%xmm5, %%xmm5\n\t" // Zero out the difference accumulator + "movdqa %6, %%xmm6\n\t" // Load the min pixel threshold (divided by 2) + "movdqa %7, %%xmm7\n\t" // Load the max pixel threshold (divided by 2) +#if defined(__x86_64__) + "mov $0x7F7F7F7F, %%eax\n\t" /* Divide mask */ + "movd %%eax, %%xmm8\n\t" + "pshufd $0x0, %%xmm8, %%xmm8\n\t" + "mov $0x01010101, %%eax\n\t" /* 0x1 mask */ + "movd %%eax, %%xmm9\n\t" + "pshufd $0x0, %%xmm9, %%xmm9\n\t" +#endif + /* Iteration start */ + "sse2_ap_iter:\n\t" + "movdqa (%2), %%xmm0\n\t" // Load the pdiff + "movdqa (%3), %%xmm1\n\t" // Load the ppoly + + "pand %%xmm0, %%xmm1\n\t" // Filter out pixels not inside polygon. Result stored on XMM1 + "movdqa %%xmm1, %%xmm2\n\t" // Move the result into XMM2 + "psrlq $0x1, %%xmm2\n\t" // Divide the result by 2 (part 1) +#if defined(__x86_64__) + "pand %%xmm8, %%xmm2\n\t" // Divide the result by 2 (part 2) +#else + "mov $0x7F7F7F7F, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "pand %%xmm0, %%xmm2\n\t" // Divide the result by 2 (part 2) +#endif + + /* Filter out pixels bigger than max threshold and update XMM0 */ + "movdqa %%xmm7, %%xmm0\n\t" // Copy max threshold to XMM0 + "pcmpgtb %%xmm2, %%xmm0\n\t" // Filter out pixels bigger than max threshold, result stored on XMM0 + "pand %%xmm2, %%xmm0\n\t" // XMM0 = Dividied pixels that are in poly and meet maximum threshold + + /* Filter out pixels smaller than min threshold */ + "pcmpgtb %%xmm6, %%xmm0\n\t" // Filter out pixels smaller than min threshold, result stored on XMM0 + + /* Write white or black depending if pixel is alarmed or not */ + "movntdq %%xmm0, (%2)\n\t" // Set the pixel to white or black depending on the result + + /* Update the alarmed pixels count */ +#if defined(__x86_64__) + "movdqa %%xmm9, %%xmm3\n\t" // Move 0x01 mask to XMM3 +#else + "mov $0x01010101, %%eax\n\t" + "movd %%eax, %%xmm3\n\t" + "pshufd $0x0, %%xmm3, %%xmm3\n\t" +#endif + "pxor %%xmm2, %%xmm2\n\t" // Set XMM2 to zeros + + "pand %%xmm0, %%xmm3\n\t" // Set alarmed pixels to 1 in XMM3 + "psadbw %%xmm2, %%xmm3\n\t" // DEST[0-15] and DEST[64-79] contain the results + "paddd %%xmm3, %%xmm4\n\t" // Update the alarmed pixels count + + /* Update XMM0 to contain pixels in poly that meet min and max thresholds */ + "pand %%xmm1, %%xmm0\n\t" // XMM0 = Pixels in poly that meet min and max thresholds + + /* Update the difference accumulator */ + "psadbw %%xmm0, %%xmm2\n\t" // DEST[0-15] and DEST[64-79] contain the results + "paddd %%xmm2, %%xmm5\n\t" // Update the difference accumulator + + /* Move to the next pixels in the row */ + "add $0x10, %2\n\t" // Add 16 to pdiff + "add $0x10, %3\n\t" // Add 16 to ppoly + "add $0x10, %4\n\t" // Add 16 to X + "cmp %10, %4\n\t" // Check if we reached max X + "jb sse2_ap_iter\n\t" // Go for another iteration + + "sub %13, %2\n\t" // Reset pdiff to low X + "sub %13, %3\n\t" // Reset ppoly to low X + "sub %13, %4\n\t" // Reset X to low x + + "add $0x1, %5\n\t" // Increment Y to advance to the next line + "add %12, %2\n\t" // Move pdiff to the next row + "add %12, %3\n\t" // Move ppoly to the next row + + "cmp %11, %5\n\t" // Check if we reached max Y + "jb sse2_ap_iter\n\t" // Go for another iteration + + /* Calculate the alarmed pixels */ + "pshufd $0x56, %%xmm4, %%xmm0\n\t" + "paddd %%xmm4, %%xmm0\n\t" + "movd %%xmm0, %0\n\t" + + /* Calculate the pixels difference */ + "pshufd $0x56, %%xmm5, %%xmm1\n\t" + "paddd %%xmm5, %%xmm1\n\t" + "movd %%xmm1, %1\n\t" + + : "=m" (pixelsalarmed), "=m" (pixelsdifference) +#if (defined(_DEBUG) && !defined(__x86_64__)) /* Use one less register to allow compilation to success on 32bit with omit frame pointer disabled */ + : "r" (pdiff), "r" (ppoly), "r" (x), "r" (y), "m" (*calc_minpthreshold), "m" (*calc_maxpthreshold), "m" (lo_y), "m" (lo_x), "m" (hi_x), "m" (hi_y), "m" (width), "m" (xgap) +#else + : "r" (pdiff), "r" (ppoly), "r" (x), "r" (y), "m" (*calc_minpthreshold), "m" (*calc_maxpthreshold), "m" (lo_y), "m" (lo_x), "r" (hi_x), "m" (hi_y), "m" (width), "m" (xgap) +#endif +#if defined(__x86_64__) + : "%rax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "%xmm9", "cc", "memory" +#else + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" +#endif + ); + + /* Store the results */ + *pixel_count = pixelsalarmed; + *pixel_sum = pixelsdifference; +#else + Panic("SSE function called on a non x86\\x86-64 platform"); +#endif +} diff --git a/src/zm_zone.h b/src/zm_zone.h index 98e020c59..ae247ada4 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -77,7 +77,7 @@ protected: // Outputs/Statistics bool alarmed; int pixel_diff; - int alarm_pixels; + unsigned int alarm_pixels; int alarm_filter_pixels; int alarm_blob_pixels; int alarm_blobs; @@ -94,7 +94,9 @@ protected: 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 ); - + void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum); + void sse2_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum); + public: Zone( 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=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0 ) {