2003-03-26 19:57:29 +08:00
|
|
|
//
|
|
|
|
// ZoneMinder Image Class Interface, $Date$, $Revision$
|
2006-01-17 18:56:30 +08:00
|
|
|
// Copyright (C) 2003, 2004, 2005, 2006 Philip Coombes
|
2003-03-26 19:57:29 +08:00
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 2
|
|
|
|
// of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef ZM_IMAGE_H
|
|
|
|
#define ZM_IMAGE_H
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <math.h>
|
2006-01-20 23:27:48 +08:00
|
|
|
#include <zlib.h>
|
2003-03-26 19:57:29 +08:00
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
2004-01-15 05:26:47 +08:00
|
|
|
#include "zm_jpeg.h"
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "zm_rgb.h"
|
|
|
|
#include "zm_coord.h"
|
|
|
|
#include "zm_box.h"
|
2005-11-30 01:51:06 +08:00
|
|
|
#include "zm_poly.h"
|
2003-03-26 19:57:29 +08:00
|
|
|
|
|
|
|
//
|
|
|
|
// This is image class, and represents a frame captured from a
|
|
|
|
// camera in raw form.
|
|
|
|
//
|
|
|
|
class Image
|
|
|
|
{
|
|
|
|
protected:
|
2004-02-18 23:35:33 +08:00
|
|
|
enum { CHAR_HEIGHT=11, CHAR_WIDTH=6 };
|
2006-11-16 19:34:53 +08:00
|
|
|
enum { LINE_HEIGHT=CHAR_HEIGHT+0 };
|
2004-02-16 03:53:10 +08:00
|
|
|
typedef unsigned char BlendTable[256][256];
|
|
|
|
typedef BlendTable *BlendTablePtr;
|
|
|
|
|
2005-11-30 01:51:06 +08:00
|
|
|
struct Edge
|
|
|
|
{
|
|
|
|
int min_y;
|
|
|
|
int max_y;
|
|
|
|
double min_x;
|
|
|
|
double _1_m;
|
|
|
|
|
|
|
|
static int CompareYX( const void *p1, const void *p2 )
|
|
|
|
{
|
|
|
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
|
|
|
if ( e1->min_y == e2->min_y )
|
|
|
|
return( int(e1->min_x - e2->min_x) );
|
|
|
|
else
|
|
|
|
return( int(e1->min_y - e2->min_y) );
|
|
|
|
}
|
|
|
|
static int CompareX( const void *p1, const void *p2 )
|
|
|
|
{
|
|
|
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
|
|
|
return( int(e1->min_x - e2->min_x) );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2004-02-16 03:53:10 +08:00
|
|
|
protected:
|
|
|
|
static bool initialised;
|
|
|
|
static unsigned char *abs_table;
|
|
|
|
static unsigned char *y_r_table;
|
|
|
|
static unsigned char *y_g_table;
|
|
|
|
static unsigned char *y_b_table;
|
|
|
|
static BlendTablePtr blend_tables[101];
|
2005-11-22 18:41:50 +08:00
|
|
|
static jpeg_compress_struct *jpg_ccinfo[100];
|
|
|
|
static jpeg_decompress_struct *jpg_dcinfo;
|
|
|
|
static struct zm_error_mgr jpg_err;
|
2003-03-26 19:57:29 +08:00
|
|
|
|
|
|
|
protected:
|
|
|
|
int width;
|
|
|
|
int height;
|
2006-01-15 06:47:02 +08:00
|
|
|
int pixels;
|
2003-03-26 19:57:29 +08:00
|
|
|
int colours;
|
|
|
|
int size;
|
|
|
|
JSAMPLE *buffer;
|
|
|
|
bool our_buffer;
|
2006-11-16 19:34:53 +08:00
|
|
|
char text[1024];
|
2003-03-26 19:57:29 +08:00
|
|
|
|
2003-04-23 07:16:57 +08:00
|
|
|
protected:
|
|
|
|
mutable unsigned int *blend_buffer;
|
|
|
|
|
2004-02-16 03:53:10 +08:00
|
|
|
protected:
|
|
|
|
static void Initialise();
|
|
|
|
static BlendTablePtr GetBlendTable( int );
|
|
|
|
|
2003-03-26 19:57:29 +08:00
|
|
|
public:
|
2004-03-04 23:05:54 +08:00
|
|
|
Image()
|
|
|
|
{
|
|
|
|
if ( !initialised )
|
|
|
|
Initialise();
|
|
|
|
width = 0;
|
|
|
|
height = 0;
|
2006-01-15 06:47:02 +08:00
|
|
|
pixels = 0;
|
2004-03-04 23:05:54 +08:00
|
|
|
colours = 0;
|
|
|
|
size = 0;
|
|
|
|
our_buffer = true;
|
|
|
|
buffer = 0;
|
|
|
|
blend_buffer = 0;
|
2005-11-20 02:43:46 +08:00
|
|
|
text[0] = '\0';
|
2004-03-04 23:05:54 +08:00
|
|
|
}
|
2003-03-26 19:57:29 +08:00
|
|
|
Image( const char *filename )
|
|
|
|
{
|
2004-02-16 03:53:10 +08:00
|
|
|
if ( !initialised )
|
|
|
|
Initialise();
|
2005-11-23 21:38:38 +08:00
|
|
|
width = 0;
|
|
|
|
height = 0;
|
2006-01-15 06:47:02 +08:00
|
|
|
pixels = 0;
|
2005-11-23 21:38:38 +08:00
|
|
|
colours = 0;
|
|
|
|
size = 0;
|
2003-10-16 17:00:25 +08:00
|
|
|
buffer = 0;
|
2003-03-26 19:57:29 +08:00
|
|
|
ReadJpeg( filename );
|
|
|
|
our_buffer = true;
|
2003-04-23 07:16:57 +08:00
|
|
|
blend_buffer = 0;
|
2005-11-20 02:43:46 +08:00
|
|
|
text[0] = '\0';
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
|
|
|
Image( int p_width, int p_height, int p_colours, JSAMPLE *p_buffer=0 )
|
|
|
|
{
|
2004-02-16 03:53:10 +08:00
|
|
|
if ( !initialised )
|
|
|
|
Initialise();
|
2003-03-26 19:57:29 +08:00
|
|
|
width = p_width;
|
|
|
|
height = p_height;
|
2006-01-15 06:47:02 +08:00
|
|
|
pixels = width*height;
|
2003-03-26 19:57:29 +08:00
|
|
|
colours = p_colours;
|
|
|
|
size = width*height*colours;
|
2004-01-15 05:26:47 +08:00
|
|
|
if ( p_buffer )
|
2003-03-26 19:57:29 +08:00
|
|
|
{
|
2004-01-15 05:26:47 +08:00
|
|
|
our_buffer = false;
|
|
|
|
buffer = p_buffer;
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-01-15 05:26:47 +08:00
|
|
|
our_buffer = true;
|
|
|
|
buffer = new JSAMPLE[size];
|
|
|
|
memset( buffer, 0, size );
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
2003-04-23 07:16:57 +08:00
|
|
|
blend_buffer = 0;
|
2005-11-20 02:43:46 +08:00
|
|
|
text[0] = '\0';
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
|
|
|
Image( const Image &p_image )
|
|
|
|
{
|
2004-02-16 03:53:10 +08:00
|
|
|
if ( !initialised )
|
|
|
|
Initialise();
|
2003-03-26 19:57:29 +08:00
|
|
|
width = p_image.width;
|
|
|
|
height = p_image.height;
|
2006-01-15 06:47:02 +08:00
|
|
|
pixels = p_image.pixels;
|
2003-03-26 19:57:29 +08:00
|
|
|
colours = p_image.colours;
|
|
|
|
size = p_image.size;
|
|
|
|
buffer = new JSAMPLE[size];
|
|
|
|
memcpy( buffer, p_image.buffer, size );
|
|
|
|
our_buffer = true;
|
2003-04-23 07:16:57 +08:00
|
|
|
blend_buffer = 0;
|
2005-11-20 02:43:46 +08:00
|
|
|
strncpy( text, p_image.text, sizeof(text) );
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
|
|
|
~Image()
|
|
|
|
{
|
|
|
|
if ( our_buffer )
|
|
|
|
{
|
|
|
|
delete[] buffer;
|
|
|
|
}
|
2003-04-23 07:16:57 +08:00
|
|
|
delete[] blend_buffer;
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
|
|
|
|
2004-03-04 23:05:54 +08:00
|
|
|
inline int Width() const { return( width ); }
|
|
|
|
inline int Height() const { return( height ); }
|
2006-01-15 06:47:02 +08:00
|
|
|
inline int Pixels() const { return( pixels ); }
|
2004-03-04 23:05:54 +08:00
|
|
|
inline int Colours() const { return( colours ); }
|
2006-01-15 06:47:02 +08:00
|
|
|
inline int Size() const { return( size ); }
|
2004-03-04 23:05:54 +08:00
|
|
|
inline JSAMPLE *Buffer() const { return( buffer ); }
|
|
|
|
inline JSAMPLE *Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); }
|
2003-03-26 19:57:29 +08:00
|
|
|
|
|
|
|
inline void Assign( int p_width, int p_height, int p_colours, unsigned char *new_buffer )
|
|
|
|
{
|
|
|
|
if ( p_width != width || p_height != height || p_colours != colours )
|
|
|
|
{
|
|
|
|
width = p_width;
|
|
|
|
height = p_height;
|
2006-01-15 06:47:02 +08:00
|
|
|
pixels = width*height;
|
2003-03-26 19:57:29 +08:00
|
|
|
colours = p_colours;
|
2003-05-02 23:03:16 +08:00
|
|
|
int new_size = width*height*colours;
|
2005-11-22 18:41:50 +08:00
|
|
|
if ( size < new_size )
|
2003-05-02 23:03:16 +08:00
|
|
|
{
|
|
|
|
size = new_size;
|
|
|
|
delete[] buffer;
|
|
|
|
buffer = new JSAMPLE[size];
|
|
|
|
memset( buffer, 0, size );
|
|
|
|
}
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
2003-04-13 00:17:17 +08:00
|
|
|
memcpy( buffer, new_buffer, size );
|
2003-03-26 19:57:29 +08:00
|
|
|
}
|
2004-03-04 23:05:54 +08:00
|
|
|
inline void Assign( const Image &image )
|
|
|
|
{
|
|
|
|
if ( image.width != width || image.height != height || image.colours != colours )
|
|
|
|
{
|
|
|
|
width = image.width;
|
|
|
|
height = image.height;
|
2006-01-15 06:47:02 +08:00
|
|
|
pixels = width*height;
|
2004-03-04 23:05:54 +08:00
|
|
|
colours = image.colours;
|
|
|
|
int new_size = width*height*colours;
|
2005-11-22 18:41:50 +08:00
|
|
|
if ( size < new_size )
|
2004-03-04 23:05:54 +08:00
|
|
|
{
|
|
|
|
size = new_size;
|
|
|
|
delete[] buffer;
|
|
|
|
buffer = new JSAMPLE[size];
|
|
|
|
memset( buffer, 0, size );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memcpy( buffer, image.buffer, size );
|
|
|
|
}
|
2003-03-26 19:57:29 +08:00
|
|
|
|
|
|
|
inline void CopyBuffer( const Image &image )
|
|
|
|
{
|
2006-05-08 20:46:53 +08:00
|
|
|
if ( image.size != size )
|
|
|
|
{
|
|
|
|
Fatal(( "Attempt to copy different size image buffers, expected %d, got %d", size, image.size ));
|
|
|
|
}
|
2003-03-26 19:57:29 +08:00
|
|
|
memcpy( buffer, image.buffer, size );
|
|
|
|
}
|
|
|
|
inline Image &operator=( const unsigned char *new_buffer )
|
|
|
|
{
|
|
|
|
memcpy( buffer, new_buffer, size );
|
|
|
|
return( *this );
|
|
|
|
}
|
|
|
|
|
2005-11-30 01:51:06 +08:00
|
|
|
bool ReadRaw( const char *filename );
|
|
|
|
bool WriteRaw( const char *filename ) const;
|
|
|
|
|
2005-10-18 05:45:06 +08:00
|
|
|
bool ReadJpeg( const char *filename );
|
2005-11-19 01:26:12 +08:00
|
|
|
bool WriteJpeg( const char *filename, int quality_override=0 ) const;
|
2006-01-20 23:27:48 +08:00
|
|
|
bool DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size );
|
2005-11-19 01:26:12 +08:00
|
|
|
bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const;
|
2003-03-26 19:57:29 +08:00
|
|
|
|
2006-01-20 23:27:48 +08:00
|
|
|
bool Unzip( const Bytef *inbuffer, unsigned long inbuffer_size );
|
|
|
|
bool Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level=Z_BEST_SPEED ) const;
|
|
|
|
|
2005-12-02 00:24:25 +08:00
|
|
|
bool Crop( int lo_x, int lo_y, int hi_y, int hi_y );
|
|
|
|
|
2003-03-26 19:57:29 +08:00
|
|
|
void Overlay( const Image &image );
|
|
|
|
void Blend( const Image &image, int transparency=10 ) const;
|
|
|
|
static Image *Merge( int n_images, Image *images[] );
|
|
|
|
static Image *Merge( int n_images, Image *images[], double weight );
|
|
|
|
static Image *Highlight( int n_images, Image *images[], const Rgb threshold=RGB_BLACK, const Rgb ref_colour=RGB_RED );
|
2003-04-16 21:02:15 +08:00
|
|
|
Image *Delta( const Image &image ) const;
|
2003-03-26 19:57:29 +08:00
|
|
|
|
2006-11-16 19:34:53 +08:00
|
|
|
void Annotate( const char *p_text, const Coord &coord, const Rgb fg_colour=RGB_WHITE, const Rgb bg_colour=RGB_BLACK );
|
2003-03-26 19:57:29 +08:00
|
|
|
Image *HighlightEdges( Rgb colour, const Box *limits=0 );
|
2005-12-21 08:13:23 +08:00
|
|
|
//Image *HighlightEdges( Rgb colour, const Polygon &polygon );
|
2003-03-26 19:57:29 +08:00
|
|
|
void Timestamp( const char *label, const time_t when, const Coord &coord );
|
|
|
|
void Colourise();
|
|
|
|
void DeColourise();
|
|
|
|
|
|
|
|
void Clear() { memset( buffer, 0, size ); }
|
|
|
|
void Fill( Rgb colour, const Box *limits=0 );
|
2005-11-30 01:51:06 +08:00
|
|
|
void Fill( Rgb colour, int density, const Box *limits=0 );
|
|
|
|
void Outline( Rgb colour, const Polygon &polygon );
|
|
|
|
void Fill( Rgb colour, const Polygon &polygon );
|
|
|
|
void Fill( Rgb colour, int density, const Polygon &polygon );
|
2003-03-26 19:57:29 +08:00
|
|
|
|
2003-05-02 23:03:16 +08:00
|
|
|
void Rotate( int angle );
|
2005-05-06 00:42:37 +08:00
|
|
|
void Flip( bool leftright );
|
2003-10-16 17:00:25 +08:00
|
|
|
|
2004-02-16 03:47:23 +08:00
|
|
|
void Scale( unsigned int factor );
|
2003-03-26 19:57:29 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // ZM_IMAGE_H
|