1) Fixed and refinished the JPEG and MJPEG capture palettes support.

2) Added the SSE2 alarmedpixels code, although its unused at the moment.
3) Small changes in libjpeg interaction.
4) Small other changes.
This commit is contained in:
Kfir Itzhak 2011-07-05 18:23:02 +03:00
parent 7e0206bbcc
commit ca57ae6438
7 changed files with 470 additions and 108 deletions

View File

@ -580,7 +580,12 @@ bool Image::ReadJpeg( const char *filename, int p_colours, int p_subpixelorder)
jpeg_stdio_src( cinfo, infile ); jpeg_stdio_src( cinfo, infile );
jpeg_read_header( cinfo, TRUE ); 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) if ( cinfo->image_width != width || cinfo->image_height != height)
{ {
width = cinfo->image_width; width = cinfo->image_width;
@ -642,11 +647,14 @@ bool Image::ReadJpeg( const char *filename, int p_colours, int p_subpixelorder)
#endif #endif
} else { } else {
/* Assume RGB */ /* Assume RGB */
/*
#ifdef JCS_EXTENSIONS #ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_RGB; cinfo->out_color_space = JCS_EXT_RGB;
#else #else
cinfo->out_color_space = JCS_RGB; cinfo->out_color_space = JCS_RGB;
#endif #endif
*/
cinfo->out_color_space = JCS_RGB;
subpixelorder = ZM_SUBPIX_ORDER_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB;
} }
break; break;
@ -763,11 +771,14 @@ bool Image::WriteJpeg( const char *filename, int quality_override ) const
#endif #endif
} else { } else {
/* Assume RGB */ /* Assume RGB */
/*
#ifdef JCS_EXTENSIONS #ifdef JCS_EXTENSIONS
cinfo->in_color_space = JCS_EXT_RGB; cinfo->out_color_space = JCS_EXT_RGB;
#else #else
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->in_color_space = JCS_RGB; cinfo->in_color_space = JCS_RGB;
#endif
} }
break; 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 ); zm_jpeg_mem_src( cinfo, inbuffer, inbuffer_size );
jpeg_read_header( cinfo, TRUE ); 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) 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 #endif
} else { } else {
/* Assume RGB */ /* Assume RGB */
/*
#ifdef JCS_EXTENSIONS #ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_RGB; cinfo->out_color_space = JCS_EXT_RGB;
#else #else
cinfo->out_color_space = JCS_RGB; cinfo->out_color_space = JCS_RGB;
#endif #endif
*/
cinfo->out_color_space = JCS_RGB;
subpixelorder = ZM_SUBPIX_ORDER_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB;
} }
break; break;
@ -992,11 +1011,14 @@ bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_over
#endif #endif
} else { } else {
/* Assume RGB */ /* Assume RGB */
/*
#ifdef JCS_EXTENSIONS #ifdef JCS_EXTENSIONS
cinfo->in_color_space = JCS_EXT_RGB; cinfo->out_color_space = JCS_EXT_RGB;
#else #else
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->in_color_space = JCS_RGB; cinfo->in_color_space = JCS_RGB;
#endif
} }
break; break;
} }

View File

@ -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 */ 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;
}
} }

View File

@ -44,4 +44,6 @@ void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level );
// Prototypes for memory compress/decompression object */ // 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_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_jpeg_mem_dest(j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size );
void zm_add_std_huff_tables( j_decompress_ptr cinfo );
} }

View File

@ -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 */ /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */
} else { } else {
if( capture ) 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 #if HAVE_LIBSWSCALE
/* Try using swscale for the conversion */ /* Try using swscale for the conversion */
conversion_type = 1; conversion_type = 1;
@ -437,6 +437,7 @@ LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel,
/* JPEG */ /* JPEG */
if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) {
Debug(2,"Using JPEG image decoding");
conversion_type = 3; 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 */ /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */
} else { } else {
if( capture ) 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 #if HAVE_LIBSWSCALE
/* Try using swscale for the conversion */ /* Try using swscale for the conversion */
conversion_type = 1; conversion_type = 1;
@ -743,19 +744,27 @@ void LocalCamera::Initialise()
v4l2_jpegcompression jpeg_comp; v4l2_jpegcompression jpeg_comp;
if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) {
if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { 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 { } else {
/* Set JPEG flags and quality */ /* Set flags and quality. MJPEG should not have the huffman tables defined */
jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; 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; jpeg_comp.quality = 85;
/* Update the JPEG options */ /* Update the JPEG options */
if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { 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 { } else {
Debug(4, "JPEG quality: %d",jpeg_comp.quality); if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) {
Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); 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 */ /* Open the device */
if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { 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; return selected_palette;
} }
@ -2047,20 +2056,22 @@ int LocalCamera::Capture( Image &image )
} }
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
if(conversion_type == 1) { if(conversion_type == 1) {
Debug( 9, "Calling sws_scale to perform the conversion" );
/* Use swscale to convert the image directly into the shared memory */ /* Use swscale to convert the image directly into the shared memory */
avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height ); 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 ); sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize );
} }
#endif #endif
if(conversion_type == 2) { if(conversion_type == 2) {
Debug( 9, "Calling the conversion function" );
/* Call the image conversion function and convert directly into the shared memory */ /* Call the image conversion function and convert directly into the shared memory */
(*conversion_fptr)(buffer, directbuffer, pixels); (*conversion_fptr)(buffer, directbuffer, pixels);
} }
else if(conversion_type == 3) { else if(conversion_type == 3) {
Debug( 9, "Decoding the JPEG image" );
/* JPEG decoding */ /* JPEG decoding */
image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder);
} }

View File

@ -199,7 +199,7 @@ __attribute__((noinline)) void* sse2_aligned_memcpy(void* dest, const void* src,
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder); const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
__asm__ __volatile__( __asm__ __volatile__(
"sse2copy_begin:\n\t" "sse2_copy_iter:\n\t"
"movdqa (%0),%%xmm0\n\t" "movdqa (%0),%%xmm0\n\t"
"movdqa 0x10(%0),%%xmm1\n\t" "movdqa 0x10(%0),%%xmm1\n\t"
"movdqa 0x20(%0),%%xmm2\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, %0\n\t"
"add $0x80, %1\n\t" "add $0x80, %1\n\t"
"cmp %2, %0\n\t" "cmp %2, %0\n\t"
"jb sse2copy_begin\n\t" "jb sse2_copy_iter\n\t"
"test %3, %3\n\t" "test %3, %3\n\t"
"jz sse2copy_finish\n\t" "jz sse2_copy_finish\n\t"
"cld\n\t" "cld\n\t"
"rep movsb\n\t" "rep movsb\n\t"
"sse2copy_finish:\n\t" "sse2_copy_finish:\n\t"
: :
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder) : "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"

View File

@ -60,38 +60,45 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_
image = 0; image = 0;
score = 0; score = 0;
overload_count = 0; overload_count = 0;
pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE);
pg_image->Clear();
pg_image->Fill( 0xff, polygon ); pg_image->Fill( 0xff, polygon );
pg_image->Outline( 0xff, polygon ); pg_image->Outline( 0xff, polygon );
pg_image->Crop( polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() );
ranges = new Range[polygon.Height()]; ranges = new Range[monitor->Height()];
int y = polygon.LoY(); for ( int y = polygon.LoY(); y <= polygon.HiY(); y++)
for ( int py = 0; py < polygon.Height(); py++, y++ )
{ {
int x = polygon.LoX(); ranges[y].lo_x = -1;
ranges[py].lo_x = -1; ranges[y].hi_x = -1;
ranges[py].hi_x = -1; ranges[y].off_x = 0;
ranges[py].off_x = 0; const uint8_t *ppoly = pg_image->Buffer( polygon.LoX(), y );
const uint8_t *ppoly = pg_image->Buffer( 0, py ); for ( int x = polygon.LoX(); x <= polygon.HiX(); x++, ppoly++ )
for ( int px = 0; px < polygon.Width(); px++, x++, ppoly++ )
{ {
if ( *ppoly ) if ( *ppoly )
{ {
if ( ranges[py].lo_x == -1 ) if ( ranges[y].lo_x == -1 )
{ {
ranges[py].lo_x = x; ranges[y].lo_x = x;
ranges[py].off_x = px;
} }
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() Zone::~Zone()
@ -119,21 +126,23 @@ bool Zone::CheckAlarms( const Image *delta_image )
ResetStats(); ResetStats();
if ( overload_count ) if ( overload_count )
{ {
Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); 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 ); Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames );
overload_count--; overload_count--;
return( false ); return( false );
} }
delete image; delete image;
// Get the difference 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(); int diff_width = diff_image->Width();
uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); 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_lo_x = 0;
int alarm_hi_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_x = -1;
int alarm_mid_y = -1; int alarm_mid_y = -1;
int lo_y = polygon.LoY(); unsigned int lo_y = polygon.LoY();
int hi_y = polygon.HiY(); unsigned int lo_x = polygon.LoX();
int lo_x; unsigned int hi_x = polygon.HiX()+1;
int hi_x; 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( 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++ )
{ Debug( 5, "Checking for alarmed pixels" );
lo_x = ranges[py].lo_x; /* if(config.cpu_extensions && sseversion >= 20) {
hi_x = ranges[py].hi_x; sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
} else {
Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
pdiff = diff_buff + ((diff_width * y) + lo_x); } */
ppoly = pg_image->Buffer( ranges[py].off_x, py ); std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
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;
}
}
}
if ( pixel_diff_count && alarm_pixels ) if ( pixel_diff_count && alarm_pixels )
pixel_diff = 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 ); 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 ) if ( config.record_diag_images )
{ {
static char diag_path[PATH_MAX] = ""; static char diag_path[PATH_MAX] = "";
@ -193,22 +182,26 @@ bool Zone::CheckAlarms( const Image *delta_image )
diff_image->WriteJpeg( diag_path ); diff_image->WriteJpeg( diag_path );
} }
if ( !alarm_pixels ) if( alarm_pixels ) {
{ if( min_alarm_pixels && (alarm_pixels < min_alarm_pixels) ) {
return( false ); /* Not enough pixels alarmed */
} return (false);
if ( min_alarm_pixels && (alarm_pixels < min_alarm_pixels) ) } else if( max_alarm_pixels && (alarm_pixels > max_alarm_pixels) ) {
{ /* Too many pixels alarmed */
return( false ); overload_count = overload_frames;
} return (false);
if ( max_alarm_pixels && (alarm_pixels > max_alarm_pixels) ) }
{ } else {
overload_count = overload_frames; /* No alarmed pixels */
return( false ); return (false);
} }
score = (100*alarm_pixels)/polygon.Area(); 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 ); Debug( 5, "Current score is %d", score );
if ( check_method >= FILTERED_PIXELS ) if ( check_method >= FILTERED_PIXELS )
{ {
int bx = filter_box.X(); int bx = filter_box.X();
@ -223,12 +216,12 @@ bool Zone::CheckAlarms( const Image *delta_image )
unsigned char *cpdiff; unsigned char *cpdiff;
int ldx, hdx, ldy, hdy; int ldx, hdx, ldy, hdy;
bool block; 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; lo_x = ranges[y].lo_x;
hi_x = ranges[py].hi_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++ ) 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; int last_x, last_y;
BlobStats *bsx, *bsy; BlobStats *bsx, *bsy;
BlobStats *bsm, *bss; 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 lo_x = ranges[y].lo_x;
int hi_x = ranges[py].hi_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++ ) for ( int x = lo_x; x <= hi_x; x++, pdiff++ )
{ {
if ( *pdiff == WHITE ) if ( *pdiff == WHITE )
@ -632,11 +625,14 @@ bool Zone::CheckAlarms( const Image *delta_image )
if ( type == INCLUSIVE ) if ( type == INCLUSIVE )
{ {
// score >>= 1;
score /= 2; score /= 2;
} }
else if ( type == EXCLUSIVE ) else if ( type == EXCLUSIVE )
{ {
// score <<= 1;
score *= 2; score *= 2;
} }
Debug( 5, "Adjusted score is %d", score ); Debug( 5, "Adjusted score is %d", score );
@ -665,12 +661,12 @@ bool Zone::CheckAlarms( const Image *delta_image )
int lo_y = polygon.LoY(); int lo_y = polygon.LoY();
int hi_y = polygon.HiY(); int hi_y = polygon.HiY();
// First mask out anything we don't want // 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); pdiff = diff_buff + ((diff_width * y) + lo_x);
int lo_x2 = ranges[py].lo_x; int lo_x2 = ranges[y].lo_x;
int hi_x2 = ranges[py].hi_x; int hi_x2 = ranges[y].hi_x;
int lo_gap = lo_x2-lo_x; int lo_gap = lo_x2-lo_x;
if ( lo_gap > 0 ) 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++ ) for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ )
{ {
if ( !*ppoly ) if ( !*ppoly )
@ -963,3 +959,261 @@ bool Zone::DumpSettings( char *output, bool /*verbose*/ )
return( true ); 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<sizeof(calc_minpthreshold);i++)
calc_minpthreshold[i] = minpthreshold>>1;
current_minpthreshold = minpthreshold;
}
if(maxpthreshold != current_maxpthreshold) {
for(unsigned int i=0;i<sizeof(calc_maxpthreshold);i++)
calc_maxpthreshold[i] = maxpthreshold>>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
}

View File

@ -77,7 +77,7 @@ protected:
// Outputs/Statistics // Outputs/Statistics
bool alarmed; bool alarmed;
int pixel_diff; int pixel_diff;
int alarm_pixels; unsigned int alarm_pixels;
int alarm_filter_pixels; int alarm_filter_pixels;
int alarm_blob_pixels; int alarm_blob_pixels;
int alarm_blobs; int alarm_blobs;
@ -94,7 +94,9 @@ protected:
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 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: 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 ) 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 )
{ {