summaryrefslogtreecommitdiff
path: root/pcl/tools/cmpi/cmpi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcl/tools/cmpi/cmpi.cpp')
-rw-r--r--pcl/tools/cmpi/cmpi.cpp1034
1 files changed, 1034 insertions, 0 deletions
diff --git a/pcl/tools/cmpi/cmpi.cpp b/pcl/tools/cmpi/cmpi.cpp
new file mode 100644
index 000000000..9527c6e43
--- /dev/null
+++ b/pcl/tools/cmpi/cmpi.cpp
@@ -0,0 +1,1034 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name: samples/cmpi/cmpi.cpp
+// Purpose: Compare images with 'fuzzy' options
+// Author: Ray Johnston
+// Modified by:
+// Created: 2006
+// RCS-ID: $Id:$
+// Copyright: (c) 2006 Artifex Software Inc.
+// License: AFPL
+///////////////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#include "wx/defs.h"
+#include "wx/image.h"
+#include "wx/file.h"
+#include "wx/filename.h"
+#include "wx/filefn.h"
+#include "wx/mstream.h"
+#include "wx/wfstream.h"
+#include "wx/pen.h"
+#include "wx/progdlg.h"
+#include "wx/msgdlg.h"
+
+// derived classes
+
+class MyFrame;
+class MyApp;
+
+// MyCanvas
+
+class MyCanvas: public wxScrolledWindow
+{
+public:
+ MyCanvas() {}
+ MyCanvas( wxWindow *parent, wxWindowID, const wxPoint &pos, const wxSize &size );
+ ~MyCanvas();
+ void OnPaint( wxPaintEvent &event );
+ bool ProcessArgs(int argc, wxChar **argv, wxStatusBar *S);
+ void update_bitmap();
+ void goto_diff_rect_focus();
+ bool pixel_differs( unsigned char **base_rows, unsigned char **compare_rows, int pixel,
+ int i_pix_window, int color_tolerance);
+ void prune_pending_list( int row );
+ bool load_image_files( wxChar *basefilename, wxChar *comparefilename );
+
+ wxImage base_image, compare_image, diff_image, *current_image, *prev_image;
+ wxStatusBar *frame_status_bar;
+ wxChar *basefn, *comparefn;
+ wxBitmap *current_bitmap;
+ float current_zoom, prev_zoom;
+ int i_pix_window;
+ int i_rect_separation;
+ int *neighbor_pix, *neighbor_row, neighbor_count;
+ int color_tolerance;
+ int diff_count;
+ bool batch_mode;
+ unsigned char *alpha;
+ bool mask_on, prev_mask;
+ bool highlight_on;
+
+ typedef struct diff_rect_s {
+ int left, top, right, bottom;
+ struct diff_rect_s *next, *prev;
+ } diff_rect;
+
+ diff_rect *diff_list_head, *diff_list_tail, *diff_list_pending;
+ diff_rect *diff_rect_focus;
+ int diff_list_count, i_diff_rect_focus;
+
+protected:
+
+private:
+ DECLARE_DYNAMIC_CLASS(MyCanvas)
+ DECLARE_EVENT_TABLE()
+};
+
+
+// MyFrame
+
+
+class MyFrame: public wxFrame
+{
+public:
+ MyFrame();
+
+ bool ProcessArgs(int argc, wxChar **argv);
+ void OnAbout( wxCommandEvent &event );
+ void OnHelp( wxCommandEvent &event );
+ void OnNewImages( wxCommandEvent &event );
+ void OnQuit( wxCommandEvent &event );
+ void OnHiLiteToggle( wxCommandEvent &event );
+ void OnMaskToggle( wxCommandEvent &event );
+ void OnView_Image( wxCommandEvent &event );
+ void OnGoTo( wxCommandEvent &event );
+ void OnZoom( wxCommandEvent &event );
+ void OnSettings( wxCommandEvent &event );
+
+ MyCanvas *m_canvas;
+ wxStatusBar *status_bar;
+
+private:
+ DECLARE_DYNAMIC_CLASS(MyFrame)
+ DECLARE_EVENT_TABLE()
+};
+
+
+// MyApp
+
+class MyApp: public wxApp
+{
+public:
+ virtual bool OnInit();
+};
+
+// main program
+
+IMPLEMENT_APP(MyApp)
+
+// MyCanvas
+
+IMPLEMENT_DYNAMIC_CLASS(MyCanvas, wxScrolledWindow)
+
+BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
+ EVT_PAINT(MyCanvas::OnPaint)
+END_EVENT_TABLE()
+
+MyCanvas::MyCanvas( wxWindow *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size )
+ : wxScrolledWindow( parent, id, pos, size, wxSUNKEN_BORDER )
+{
+ SetBackgroundColour(* wxWHITE);
+ current_bitmap = (wxBitmap *)NULL;
+ current_image = (wxImage *)NULL;
+ prev_image = current_image;
+ frame_status_bar = (wxStatusBar *)NULL;
+ neighbor_count = 0;
+ neighbor_pix = neighbor_row = NULL;
+}
+
+// Called when zoom factor changes to create bitmap of correct size
+void
+MyCanvas::update_bitmap()
+{
+ int current_scroll_x, current_scroll_y, sx, sy, client_width_pixels, client_height_rows;
+
+ if ((current_image == prev_image) && (current_zoom == prev_zoom) && (mask_on == prev_mask))
+ return;
+
+ if (current_bitmap) {
+ delete current_bitmap;
+ }
+ GetClientSize( &client_width_pixels, &client_height_rows );
+ GetViewStart( &current_scroll_x, &current_scroll_y );
+ /* Keep center of focus constant as we zoom in and out */
+ /* These calculations could be chained, but this is easier for debugging */
+ sx = current_scroll_x + ( client_width_pixels / 2 );
+ sy = current_scroll_y + ( client_height_rows / 2 );
+ sx = sx * ( current_zoom / prev_zoom );
+ sy = sy * ( current_zoom / prev_zoom );
+ sx = sx - ( client_width_pixels / 2 );
+ sy = sy - ( client_height_rows / 2 );
+ sx = ( sx < 0 ) ? 0 : sx;
+ sy = ( sy < 0 ) ? 0 : sy;
+ prev_image = current_image;
+ prev_zoom = current_zoom;
+ prev_mask = mask_on;
+ current_bitmap = new wxBitmap(
+ current_image->Scale( current_image->GetWidth() * current_zoom,
+ current_image->GetHeight() * current_zoom)
+ );
+ SetScrollbars( 1, 1, current_bitmap->GetWidth(), current_bitmap->GetHeight(), sx, sy );
+
+ wxChar *fn;
+ char *hdr;
+ if (current_image == &base_image) {
+ hdr = "Base Image: ";
+ fn = basefn;
+ } else if (current_image == &compare_image) {
+ hdr = "Compare Image: ";
+ fn = comparefn;
+ } else {
+ hdr = "Diff Image";
+ fn = _T("");
+ }
+#if wxUSE_STATUSBAR
+ frame_status_bar->SetStatusText(wxString(hdr, wxConvUTF8), 0);
+ frame_status_bar->SetStatusText(wxString(fn, wxConvUTF8), 1);
+#endif // wxUSE_STATUSBAR
+ Refresh();
+}
+
+void
+MyCanvas::goto_diff_rect_focus( )
+{
+ int scroll_units_pixels, scroll_units_rows, client_width_pixels, client_height_rows;
+
+ /* make the center of the current diff rect be at the center of the window */
+ int i_rect_center_pixel = ( diff_rect_focus->left + diff_rect_focus->right ) / 2;
+ int i_rect_center_row = ( diff_rect_focus->top + diff_rect_focus->bottom ) / 2;
+
+ GetScrollPixelsPerUnit( &scroll_units_pixels, &scroll_units_rows );
+ GetClientSize( &client_width_pixels, &client_height_rows );
+
+ int scroll_to_pixel = ( (current_zoom * i_rect_center_pixel) - ( client_width_pixels / 2 ) );
+ int scroll_to_row = ( current_zoom * i_rect_center_row - ( client_height_rows / 2 ) );
+
+ Scroll( scroll_to_pixel / scroll_units_pixels, scroll_to_row / scroll_units_rows );
+ Refresh();
+}
+
+
+#define MAX_PIXWINDOW 8
+
+bool
+MyCanvas::pixel_differs( unsigned char **base_rows, unsigned char **compare_rows, int pixel, int i_pw, int color_tolerance)
+{
+
+ /* rows are pointers to the data to be compared */
+ /* the current pixel is in ***_rows[i_pw][3*i_pw] */
+ /* color_tolerance is an absolute diff in each channel R, G, B */
+ int p3 = (pixel * 3); /* center pixel offset in row */
+ int i;
+ bool base_differs = true; /* default to differing */
+
+ /* This row in _rows is i_pw */
+ /* Check pixels from the center out */
+ unsigned char *cp, *bp = base_rows [i_pw] + p3; /* reference is base */
+ for (i=0; i<neighbor_count; i++) {
+ cp = compare_rows [i_pw + neighbor_row[i]] + p3 + (3 * neighbor_pix[i]);
+ if (abs(bp[0] - cp[0]) <= color_tolerance &&
+ abs(bp[1] - cp[1]) <= color_tolerance &&
+ abs(bp[2] - cp[2]) <= color_tolerance ) {
+ if (i == 0)
+ return false; /* matches at current pixel */
+ base_differs = false;
+ break; /* found a match */
+ }
+ }
+ if (base_differs)
+ return true; /* bail out now */
+ /* Need to make sure that this isn't a missing feature */
+ cp = compare_rows [i_pw] + p3; /* reference is compare */
+ for (i=0; i<neighbor_count; i++) {
+ bp = base_rows [i_pw + neighbor_row[i]] + p3 + (3 * neighbor_pix[i]);
+ if (abs(bp[0] - cp[0]) <= color_tolerance &&
+ abs(bp[1] - cp[1]) <= color_tolerance &&
+ abs(bp[2] - cp[2]) <= color_tolerance ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+MyCanvas::prune_pending_list( int row )
+{
+ diff_rect *diff_pending_scan;
+ diff_rect *diff_pending_scan_base = diff_list_pending;
+
+ /* First anneal any rects on the pending list that are close enough */
+ /* This is done first since this list is active for every row */
+ while ( (diff_pending_scan_base != NULL) && (diff_pending_scan_base->next != NULL) ) {
+ diff_pending_scan = diff_pending_scan_base->next;
+ while (diff_pending_scan != NULL) {
+ if ( ( diff_pending_scan_base->right >= (diff_pending_scan->left - i_rect_separation) ) &&
+ ( diff_pending_scan_base->left <= (diff_pending_scan->right + i_rect_separation) ) ) {
+ /* merge the _scan rect into the _base rect and delete the _scan element */
+ diff_pending_scan_base->left = diff_pending_scan_base->left < diff_pending_scan->left ?
+ diff_pending_scan_base->left : diff_pending_scan->left;
+ diff_pending_scan_base->right = diff_pending_scan_base->right > diff_pending_scan->right ?
+ diff_pending_scan_base->right : diff_pending_scan->right;
+ diff_pending_scan_base->top = diff_pending_scan_base->top < diff_pending_scan->top ?
+ diff_pending_scan_base->top : diff_pending_scan->top;
+ diff_pending_scan_base->bottom = diff_pending_scan_base->bottom > diff_pending_scan->bottom ?
+ diff_pending_scan_base->bottom : diff_pending_scan->bottom;
+ /* delete the diff_pending_scan rectangle from the list */
+ diff_pending_scan->prev->next = diff_pending_scan->next;
+ if (diff_pending_scan->next != NULL)
+ diff_pending_scan->next->prev = diff_pending_scan->prev;
+ diff_rect *prev = diff_pending_scan->prev;
+ free( diff_pending_scan );
+ diff_pending_scan = prev;
+ }
+ diff_pending_scan = diff_pending_scan->next;
+ }
+ diff_pending_scan_base = diff_pending_scan_base->next;
+ }
+ /* Remove any pending differences that will be too far above the current row */
+ /* Before adding it, we need to make sure we can't simply anneal to an existing diff */
+ diff_pending_scan = diff_list_pending;
+
+ while (diff_pending_scan != NULL) {
+ if (diff_pending_scan->bottom <= (row - i_rect_separation)) {
+ bool diff_pending_merged = false;
+ diff_rect *diff_list_scan = diff_list_head;
+
+ while (diff_list_scan != NULL) {
+ if ( ( diff_list_scan->bottom >= diff_pending_scan->top - i_rect_separation ) &&
+ ( ( diff_list_scan->right >= (diff_pending_scan->left - i_rect_separation) ) &&
+ ( diff_list_scan->left <= (diff_pending_scan->right + i_rect_separation) )
+ )
+ ) {
+ /* merge the diff_pending_scan rect into the diff_list_scan rect */
+ diff_list_scan->left = diff_list_scan->left < diff_pending_scan->left ?
+ diff_list_scan->left : diff_pending_scan->left;
+ diff_list_scan->right = diff_list_scan->right > diff_pending_scan->right ?
+ diff_list_scan->right : diff_pending_scan->right;
+ diff_list_scan->top = diff_list_scan->top < diff_pending_scan->top ?
+ diff_list_scan->top : diff_pending_scan->top;
+ diff_list_scan->bottom = diff_list_scan->bottom > diff_pending_scan->bottom ?
+ diff_list_scan->bottom : diff_pending_scan->bottom;
+
+ /* delete the diff_pending_scan rectangle from the list */
+ if (diff_pending_scan->prev != NULL)
+ diff_pending_scan->prev->next = diff_pending_scan->next;
+ else
+ diff_list_pending = diff_pending_scan->next;
+ if (diff_pending_scan->next != NULL)
+ diff_pending_scan->next->prev = diff_pending_scan->prev;
+ diff_rect *next = diff_pending_scan->next;
+ free( diff_pending_scan );
+ diff_pending_scan = next;
+ diff_pending_merged = true;
+ break;
+ }
+ diff_list_scan = diff_list_scan->next;
+ }
+
+ if ( ! diff_pending_merged ) {
+ /* move this rect from the pending list to the end of the diff_list */
+ if (diff_pending_scan->prev != NULL)
+ /* not the first on the pending list */
+ diff_pending_scan->prev->next = diff_pending_scan->next;
+ else
+ diff_list_pending = diff_pending_scan->next;
+ if (diff_pending_scan->next != NULL)
+ /* wasn't the last on the pending list */
+ diff_pending_scan->next->prev = diff_pending_scan->prev;
+ /* now put this on the diff_list at the tail */
+ if (diff_list_tail == NULL) {
+ diff_list_tail = diff_list_head = diff_pending_scan;
+ diff_pending_scan->prev = NULL;
+ } else {
+ diff_list_tail->next = diff_pending_scan;
+ diff_pending_scan->prev = diff_list_tail;
+ diff_list_tail = diff_pending_scan;
+ }
+ diff_pending_scan = diff_pending_scan->next;
+ diff_list_tail->next = NULL;
+ diff_list_count++;
+ /* finished moving a rect from 'pending' to the diff_list */
+ } /* end if ! diff_pending_merged */
+ } else {
+ diff_pending_scan = diff_pending_scan->next;
+ } /* end if diff_pending rect ends too far above current row */
+ } /* end while diff_pending_scan != NULL */
+}
+
+bool
+MyCanvas::load_image_files( wxChar *basefilename, wxChar *comparefilename )
+{
+ char str[256];
+ int i, percent_done = 0;
+
+ wxProgressDialog *wxPD = new wxProgressDialog(wxT("Loading and Analyzing Images"),
+ wxT("________________________________________________________________"),
+ 100, 0, wxPD_AUTO_HIDE );
+ sprintf(str, "Loading baseline image file: %-64s", basefilename);
+ wxPD->Update( 0, wxString(str, wxConvUTF8));
+ if ( ! base_image.LoadFile( basefilename, wxBITMAP_TYPE_ANY ) ) {
+ wxLogError(wxT("Can't load baseline image"));
+ return false;
+ }
+ else {
+ sprintf(str, "Loading compare image file: %-64s", basefilename);
+ wxPD->Update( 25, wxString(str, wxConvUTF8));
+ if ( ! compare_image.LoadFile( comparefilename, wxBITMAP_TYPE_ANY )){
+ wxLogError(wxT("Can't load compare image"));
+ return false;
+ }
+ }
+ if (compare_image.GetWidth() != base_image.GetWidth() ||
+ compare_image.GetHeight() != base_image.GetHeight()) {
+ wxLogError(wxT("compare image geometry doesn't match base image"));
+ return false;
+ }
+
+
+ basefn = basefilename;
+ comparefn = comparefilename;
+ current_image = &base_image;
+ base_image.SetAlpha ();
+ compare_image.SetAlpha ();
+ memset (base_image.GetAlpha(), 255, base_image.GetWidth() * base_image.GetHeight());
+ memset (compare_image.GetAlpha(), 255, base_image.GetWidth() * base_image.GetHeight());
+
+ // Compute 'fit' zoom factor
+ int frameW, frameH;
+ DoGetClientSize(&frameW, &frameH);
+ float WRatio = (float)frameW / (float)(base_image.GetWidth());
+ float HRatio = (float)frameH / (float)(base_image.GetHeight());
+ current_zoom = WRatio < HRatio ? WRatio : HRatio;
+ prev_zoom = current_zoom; /* save the value for scroll positioning when zooming */
+
+ // Gather differences
+ unsigned char *base_data = base_image.GetData();
+ unsigned char *compare_data = compare_image.GetData();
+ bool differs;
+ int pixel, row;
+ int image_width = base_image.GetWidth();
+ int image_height = base_image.GetHeight();
+ unsigned char *diff_data = (unsigned char *)malloc(image_width * image_height *3);
+ unsigned char *p_base_rows[MAX_PIXWINDOW];
+ unsigned char *p_compare_rows[MAX_PIXWINDOW];
+
+ diff_list_head = diff_list_tail = diff_list_pending = (diff_rect *)NULL;
+ diff_rect_focus = NULL;
+ i_diff_rect_focus = 0;
+ diff_list_count = 0;
+ alpha = (unsigned char *)calloc(image_height * image_width, 1);
+ mask_on = false;
+ highlight_on = false;
+ prev_mask = mask_on;
+ memset(diff_data, 255, image_width * image_height * 3);
+ memset(alpha, 30, image_width * image_height);
+
+ /* generate the list of neighbors dynamically */
+ neighbor_count = 1;
+ for (i=1; i <= i_pix_window; i++)
+ neighbor_count += 8 * i;
+ neighbor_pix = (int *)calloc(neighbor_count, sizeof(int));
+ neighbor_row = (int *)calloc(neighbor_count, sizeof(int));
+ neighbor_count = 1;
+ for (i=1; i <= i_pix_window; i++) {
+ for (row = -i; row < i; row++, neighbor_count++) { /* lower right, up */
+ neighbor_pix[neighbor_count] = i;
+ neighbor_row[neighbor_count] = row;
+ }
+ for (pixel = i; pixel > -i; pixel--, neighbor_count++) { /* upper right, left */
+ neighbor_pix[neighbor_count] = pixel;
+ neighbor_row[neighbor_count] = i;
+ }
+ for (row = i; row > -i; row--, neighbor_count++) { /* lower right, up */
+ neighbor_pix[neighbor_count] = -i;
+ neighbor_row[neighbor_count] = row;
+ }
+ for (pixel = -i; pixel < i; pixel++, neighbor_count++) { /* upper right, left */
+ neighbor_pix[neighbor_count] = pixel;
+ neighbor_row[neighbor_count] = -i;
+ }
+ }
+
+ for (row=0; row < image_height; row++) {
+
+ if ( 25 + ((row * 75)/ image_height) > percent_done ) {
+ /* update progress dialog */
+ percent_done = 25 + ( ( row * 75 )/ image_height );
+ sprintf(str, " Analyzing Differences: %d %%", percent_done);
+ wxPD->Update( percent_done, wxString(str, wxConvUTF8));
+ }
+
+ int i;
+ for (i = -i_pix_window; i <= i_pix_window; i++) {
+ int row_offset = (row + i) * image_width * 3;
+ p_base_rows[i+i_pix_window] = base_data + row_offset;
+ p_compare_rows[i+i_pix_window] = compare_data + row_offset;
+ }
+
+ for (pixel=0; pixel < image_width; pixel++) {
+ if ( (pixel < i_pix_window) || (pixel >= (image_width - i_pix_window)) ||
+ (row < i_pix_window) || (row >= (image_height - i_pix_window)) ) {
+ /* Need to copy a range of data to our "window" filling in rows and */
+ /* columns outside of the image */
+ // ------------------------------------------------------------- //
+ //
+ // THIS SECTION T.B.I. BUT IT ONLY AFFECTS DIFFERENCES AT THE
+ // EDGE OF THE IMAGES, SO WE LEAVE IT FOR LATER.
+ //
+ // The approach is to copy the data to a 'window' of rows that
+ // is 1+2*i_pix_window square and pass those row pointers to
+ // pixel_differs()
+
+ /* Now call the comparator on the window we just filled in */
+
+ differs = false;
+ }
+ else {
+ differs = pixel_differs( p_base_rows, p_compare_rows, pixel, i_pix_window, color_tolerance);
+ }
+ if (differs) {
+ unsigned char *d = diff_data + 3 * (pixel + (row * image_width));
+ d[0] = d[1] = d[2] = 0;
+ alpha[pixel + (row * image_width)] = 255;
+ diff_count++;
+
+ /* Now maintain the list of diff_rects */
+ bool diff_processed = false;
+ diff_rect *diff_rect_scan = diff_list_pending;
+
+ while (diff_rect_scan != NULL) {
+ /* we already know that any rects still on the pending list are */
+ /* within the row i_rect_separation distance -- just check the pixel distance */
+ if ( (pixel >= (diff_rect_scan->left - i_rect_separation)) &&
+ (pixel <= (diff_rect_scan->right + i_rect_separation)) ) {
+ /* update thebottom to the current row. Set right and left if past current pixel */
+ diff_rect_scan->right = (pixel > diff_rect_scan->right) ? pixel : diff_rect_scan->right ;
+ diff_rect_scan->left = (pixel < diff_rect_scan->left) ? pixel : diff_rect_scan->left ;
+ diff_rect_scan->bottom = row;
+ diff_processed = true;
+ break;
+ }
+ /* if not at end of list, advance to next */
+ if (diff_rect_scan->next == NULL) {
+ break; /* exit loop with _scan at tail of list */
+ /* the above exit at end of list will have diff_processed == false */
+ }
+ diff_rect_scan = diff_rect_scan->next;
+ }
+ if ( ! diff_processed ) {
+ /* This difference wasn't on the 'pending' list -- add a new rect at the head */
+ diff_rect *diff_rect_new = (diff_rect *)calloc(1, sizeof(diff_rect));
+ diff_rect_new->right = diff_rect_new->left = pixel;
+ diff_rect_new->bottom = diff_rect_new->top = row;
+ diff_rect_new->next = diff_list_pending;
+ if (diff_list_pending != NULL) {
+ diff_list_pending->prev = diff_rect_new; /* link back to new head of list */
+ }
+ diff_list_pending = diff_rect_new; /* new head of list */
+ }
+ } /* finished handling a pixel that differs */
+ } /* end of pixels in a row */
+
+ prune_pending_list( row );
+ } /* end of rows in image */
+
+ /* Move any rects still pending to the diff_list */
+ prune_pending_list( row + i_rect_separation + 1 ); /* far enoungh beyond all pending */
+
+ wxPD->Close();
+ wxPD->Destroy();
+ diff_image.Create(image_width, image_height, diff_data, NULL, false);
+ free(neighbor_pix);
+ free(neighbor_row);
+
+ return true;
+}
+
+bool
+MyCanvas::ProcessArgs(int argc=0, wxChar **argv=NULL, wxStatusBar *status_bar=NULL)
+{
+
+ int atarg = 1;
+ char *logfilename = "cmpi.log";
+
+ frame_status_bar = status_bar;
+ i_pix_window = 3; /* Default is sort of loose */
+ color_tolerance = 5; /* also for color variations */
+ diff_count = 0;
+ i_rect_separation = 3 * i_pix_window;
+
+ batch_mode = false;
+ while ((atarg < argc) && (argv[atarg][0] == '-')) {
+ /* process options */
+ switch(argv[atarg][1]) {
+ case 'b':
+ batch_mode = true;
+ break;
+ case 'c':
+ sscanf((char *)argv[atarg]+2, "%d", &color_tolerance);
+ if (color_tolerance < 0)
+ color_tolerance = 0;
+ break;
+ case 'l':
+ if (argv[atarg]+2 != 0)
+ logfilename = (char *)argv[atarg]+2;
+ else {
+ atarg++;
+ logfilename = (char *)argv[atarg];
+ }
+ break;
+ case 'r':
+ sscanf((char *)argv[atarg]+2, "%d", &i_rect_separation);
+ if (i_rect_separation < 0)
+ i_rect_separation = 0;
+ break;
+ case 'w':
+ sscanf((char *)argv[atarg]+2, "%d", &i_pix_window);
+ if (i_pix_window < 0)
+ i_pix_window = 0;
+ break;
+ default:
+ wxLogError(wxT("Unknown option"));
+ }
+ atarg++;
+ }
+ if (argc - atarg < 2) {
+ wxLogError(wxT("Two filename arguments are required"));
+ return false;
+ }
+ if ( ! load_image_files( argv[atarg], argv[atarg+1] ) )
+ return false;
+
+ if (batch_mode) {
+ char str[256], basename[256];
+ wxFile logFile;
+
+ if (wxFileExists(logfilename))
+ logFile.Open(logfilename, wxFile::write_append);
+ else
+ logFile.Create(logfilename);
+
+ logFile.SeekEnd();
+
+ /* find the "basename .p?m" from the base image name */
+ int end = strlen(argv[atarg]) - 1;
+ while ( argv[atarg][end--] != '.' && end > 0 )
+ ;
+ if ( end == 0 )
+ end = strlen(argv[atarg]) - 1;
+ int start = end++;
+ while ( argv[atarg][start] != '/' && argv[atarg][start] != '\\' &&
+ argv[atarg][start] != ':' && start > 0 )
+ start--;
+ strncpy(basename, argv[atarg]+start, end-start);
+ basename[end-start] = 0;
+ sprintf(str, "%s\tDifference count: %d\n", basename, diff_count);
+ logFile.Write(wxString(str, wxConvUTF8));
+ logFile.Close();
+ return false;
+ }
+
+#if wxUSE_STATUSBAR
+ char str[32];
+ sprintf( str, "Rect Count: %-10d", diff_list_count);
+ frame_status_bar->SetStatusText(wxString(str, wxConvUTF8), 2);
+ sprintf( str, "Diff Count: %-10d", diff_count);
+ frame_status_bar->SetStatusText(wxString(str, wxConvUTF8), 3);
+#endif // wxUSE_STATUSBAR
+
+ update_bitmap();
+
+ return true;
+}
+
+MyCanvas::~MyCanvas()
+{
+ base_image.Destroy();
+ compare_image.Destroy();
+ delete current_bitmap;
+}
+
+void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
+{
+ wxPaintDC dc( this );
+ PrepareDC( dc );
+
+ if (current_bitmap && current_bitmap->Ok()) {
+ diff_rect *diff_rect_scan;
+
+ dc.DrawBitmap( *current_bitmap, 0, 0 );
+
+ /* now outline the difference rects */
+ if ( highlight_on ) {
+ dc.SetBrush( *wxTRANSPARENT_BRUSH );
+
+ for (diff_rect_scan = diff_list_head; diff_rect_scan != NULL; diff_rect_scan = diff_rect_scan->next) {
+ int w = current_zoom * (diff_rect_scan->right - diff_rect_scan->left + 1);
+ int h = current_zoom * (diff_rect_scan->bottom - diff_rect_scan->top + 1);
+ bool focus = diff_rect_scan == diff_rect_focus;
+
+ dc.SetPen( wxPen( focus ? *wxBLUE : *wxRED, focus ? 4 : 2, wxSOLID ) );
+ dc.DrawRectangle( current_zoom * diff_rect_scan->left, current_zoom * diff_rect_scan->top,
+ (w <= 0) ? 1 : w, (h <= 0) ? 1 : h );
+ } /* end for diff_rect_scan */
+ } /* end if highlight_on */
+ }
+}
+
+// MyFrame
+
+enum
+{
+ ID_QUIT = wxID_EXIT,
+ ID_ABOUT = wxID_ABOUT,
+ ID_HELP = wxID_HELP,
+ ID_NEW = 100,
+ ID_BASE = 101,
+ ID_COMPARE = 102,
+ ID_DIFF = 103,
+ ID_HILITE = 104,
+ ID_MASK = 105,
+ ID_GOTO_NEXT = 106,
+ ID_GOTO_PREV = 107,
+ ID_ZOOM_FIT = 108,
+ ID_ZOOM_1 = 109,
+ ID_ZOOM_IN = 110,
+ ID_ZOOM_OUT = 111,
+ ID_PIXEL_WINDOW = 112,
+ ID_COLOR_TOLERANCE = 113,
+};
+
+IMPLEMENT_DYNAMIC_CLASS( MyFrame, wxFrame )
+
+BEGIN_EVENT_TABLE(MyFrame,wxFrame)
+ EVT_MENU (ID_ABOUT, MyFrame::OnAbout)
+ EVT_MENU (ID_HELP, MyFrame::OnHelp)
+ EVT_MENU (ID_QUIT, MyFrame::OnQuit)
+ EVT_MENU (ID_NEW, MyFrame::OnNewImages)
+ EVT_MENU (ID_BASE, MyFrame::OnView_Image)
+ EVT_MENU (ID_COMPARE, MyFrame::OnView_Image)
+ EVT_MENU (ID_DIFF, MyFrame::OnView_Image)
+ EVT_MENU (ID_HILITE, MyFrame::OnHiLiteToggle)
+ EVT_MENU (ID_MASK, MyFrame::OnMaskToggle)
+ EVT_MENU (ID_GOTO_NEXT, MyFrame::OnGoTo)
+ EVT_MENU (ID_GOTO_PREV, MyFrame::OnGoTo)
+ EVT_MENU (ID_ZOOM_FIT, MyFrame::OnZoom)
+ EVT_MENU (ID_ZOOM_1, MyFrame::OnZoom)
+ EVT_MENU (ID_ZOOM_IN, MyFrame::OnZoom)
+ EVT_MENU (ID_ZOOM_OUT, MyFrame::OnZoom)
+ EVT_MENU (ID_PIXEL_WINDOW, MyFrame::OnSettings)
+ EVT_MENU (ID_COLOR_TOLERANCE, MyFrame::OnSettings)
+END_EVENT_TABLE()
+
+MyFrame::MyFrame()
+ : wxFrame( (wxFrame *)NULL, wxID_ANY, _T("cmpi - fuzzy image comparator"),
+ wxPoint(20,20), wxSize(1200,900) )
+{
+ status_bar = (wxStatusBar *)NULL;
+ wxMenuBar *menu_bar = new wxMenuBar();
+ wxMenu *menuFile = new wxMenu;
+ menuFile->Append( ID_NEW, _T("&Open Image Files...\tCtrl-O"));
+
+ menuFile->AppendSeparator();
+ menuFile->Append( ID_QUIT, _T("E&xit\tCtrl-Q"));
+ menu_bar->Append(menuFile, _T("&File"));
+
+ wxMenu *menuView = new wxMenu;
+ menuView->Append( ID_BASE, _T("&Base Image\tb"));
+ menuView->Append( ID_COMPARE, _T("&Compare Image\tc"));
+ menuView->Append( ID_DIFF, _T("&Diff Image\td"));
+ menuView->AppendSeparator();
+ menuView->Append( ID_HILITE, _T("&Highlight Toggle\th"));
+ menuView->Append( ID_MASK, _T("&Mask Toggle Image\tm"));
+ menuView->AppendSeparator();
+ menuView->Append( ID_GOTO_NEXT, _T("&Next Difference\tn"));
+ menuView->Append( ID_GOTO_PREV, _T("&Previous Difference\tp"));
+ menuView->AppendSeparator();
+ menuView->Append( ID_ZOOM_FIT, _T("Zoom &Fit in Window\tf"));
+ menuView->Append( ID_ZOOM_1, _T("Zoom &1:1\t1"));
+ menuView->Append( ID_ZOOM_IN, _T("Zoom &In\t+"));
+ menuView->Append( ID_ZOOM_OUT, _T("Zoom &Out\t-"));
+ menu_bar->Append(menuView, _T("&View"));
+
+ wxMenu *menuSettings = new wxMenu;
+ menuSettings->Append( ID_PIXEL_WINDOW, _T("Pixel &Window ...\tCtrl-W"));
+ menuSettings->Append( ID_COLOR_TOLERANCE, _T("Color &Tolerance...\tCtrl-T"));
+ menu_bar->Append(menuSettings, _T("&Settings"));
+
+ wxMenu *menuHelp = new wxMenu;
+ menuHelp->Append( ID_ABOUT, _T("&About..."));
+ menuHelp->Append( ID_HELP, _T("&Help\tF1"));
+ menu_bar->Append(menuHelp, _T("&Help"));
+
+ SetMenuBar( menu_bar );
+
+#if wxUSE_STATUSBAR
+ status_bar = CreateStatusBar(4);
+ int widths[] = { 150, -1, 200, 200 };
+ SetStatusWidths( 4, widths );
+#endif // wxUSE_STATUSBAR
+
+ m_canvas = new MyCanvas( this, wxID_ANY, wxPoint(0,0), wxSize(10,10) );
+}
+
+bool MyFrame::ProcessArgs(int argc, wxChar **argv)
+{
+ if (!m_canvas->ProcessArgs(argc, argv, status_bar))
+ Close();
+ return true;
+}
+
+void MyFrame::OnQuit( wxCommandEvent &WXUNUSED(event) )
+{
+ Close( true );
+}
+
+void MyFrame::OnAbout( wxCommandEvent &WXUNUSED(event) )
+{
+ (void)wxMessageBox( _T("cmpi - Image Comparison tool with fuzzy compare -- Version 0.9\n")
+ _T("Artifex Software (c) 2006"),
+ _T("Written by Ray Johnston"),
+ wxICON_INFORMATION | wxOK );
+}
+
+void MyFrame::OnHelp( wxCommandEvent &WXUNUSED(event) )
+{
+ (void)wxMessageBox( _T(
+ "usage: cmpi [-w#] [-c#] [-r#] [-l logfilename] baseline.ppm compare.ppm\n"
+ "\n"
+ " w# is pixel distance from the reference location 0 to 3\n"
+ " c# is the color tolerance (same for all components)\n"
+ " r# is the max rect separation used to collect adjacent\n"
+ " or nearby pixels into rectangular regions\n"
+ "\n"
+ "Hotkeys are:\n"
+ "\n"
+ " b display baseline image\n"
+ " c display compare image\n"
+ " d display diff image\n"
+ " f fit page to window\n"
+ " h highlight on/off toggle\n"
+ " m mask on/off toggle (only for base or compare images)\n"
+ " n next difference rectangle - center on screen, highlight blue\n"
+ " p previous difference rectangle - center on screen, highlight blue\n"
+ " 1 set zoom to 1:1\n"
+ " - zoom out\n"
+ " + zoom in\n"
+ "\n"
+ " ctrl-q quit\n"
+ ),
+ _T("Usage:"),
+ wxICON_INFORMATION | wxOK );
+}
+
+void MyFrame::OnNewImages( wxCommandEvent &WXUNUSED(event) )
+{
+#if wxUSE_FILEDLG
+ unsigned int i;
+ wxString filename = wxFileSelector(_T("Select base image file"));
+ if ( !filename )
+ return;
+
+ wxChar *basefilename = (wxChar *)calloc(filename.Len(), sizeof(wxChar));
+ for ( i=0; i < filename.Len(); i++)
+ basefilename[i] = filename.GetChar(i);
+ basefilename[i] = 0;
+
+ filename = wxFileSelector(_T("Select compare image file"));
+ if ( !filename )
+ return;
+
+ wxChar *comparefilename = (wxChar *)calloc(filename.Len(), sizeof(wxChar));
+ for ( i=0; i < filename.Len(); i++)
+ comparefilename[i] = filename.GetChar(i);
+ comparefilename[i] = 0;
+
+ m_canvas->load_image_files(basefilename, comparefilename);
+
+#endif // wxUSE_FILEDLG
+}
+
+void MyFrame::OnView_Image( wxCommandEvent &event )
+{
+ MyCanvas *m = m_canvas;
+
+ switch (event.GetId()) {
+ case ID_COMPARE:
+ m->current_image = &m->compare_image;
+ break;
+ case ID_DIFF:
+ m->current_image = &m->diff_image;
+ break;
+ case ID_BASE:
+ default:
+ m->current_image = &m->base_image;
+ break;
+ }
+ m->update_bitmap();
+}
+
+
+void MyFrame::OnMaskToggle( wxCommandEvent &WXUNUSED(event) )
+{
+ MyCanvas *m = m_canvas;
+
+ if ((m->current_image == &m->base_image) || (m->current_image == &m->compare_image)) {
+ if ( ! m->mask_on ) {
+ memcpy(m->base_image.GetAlpha(), m->alpha, m->base_image.GetWidth() * m->base_image.GetHeight());
+ memcpy(m->compare_image.GetAlpha(), m->alpha, m->base_image.GetWidth() * m->base_image.GetHeight());
+ m->mask_on = true;
+ } else {
+ memset (m->base_image.GetAlpha(), 255, m->base_image.GetWidth() * m->base_image.GetHeight());
+ memset (m->compare_image.GetAlpha(), 255, m->base_image.GetWidth() * m->base_image.GetHeight());
+ m->mask_on = false;
+ }
+ m->update_bitmap();
+ }
+}
+
+void MyFrame::OnHiLiteToggle( wxCommandEvent &WXUNUSED(event) )
+{
+ m_canvas->highlight_on = ! m_canvas->highlight_on;
+ m_canvas->Refresh();
+}
+
+void MyFrame::OnZoom( wxCommandEvent &event )
+{
+ MyCanvas *m = m_canvas;
+ double log_2, log2_zoom;
+ float WRatio, HRatio;
+ int frameW, frameH;
+
+ switch (event.GetId()) {
+ case ID_ZOOM_IN:
+ log_2 = log(2.0);
+ log2_zoom = log(m->current_zoom) / log_2;
+
+ log2_zoom = ceil(log2_zoom + 0.00001);
+ m->current_zoom = exp(log2_zoom * log_2);
+ break;
+ case ID_ZOOM_OUT:
+ log_2 = log(2.0);
+ log2_zoom = log(m->current_zoom) / log_2;
+
+ log2_zoom = floor(log2_zoom - 0.00001);
+ m->current_zoom = exp(log2_zoom * log_2);
+ break;
+ case ID_ZOOM_FIT:
+ // Compute 'fit' zoom factor
+ DoGetClientSize(&frameW, &frameH);
+ WRatio = (float)frameW / (float)(m->base_image.GetWidth());
+ HRatio = (float)frameH / (float)(m->base_image.GetHeight());
+ m->current_zoom = WRatio < HRatio ? WRatio : HRatio;
+ break;
+ case ID_ZOOM_1:
+ default:
+ m->current_zoom = 1.0;
+ }
+ // Now scale the bitmap from the image
+ m->update_bitmap();
+}
+
+void MyFrame::OnGoTo( wxCommandEvent &event )
+{
+ MyCanvas *m = m_canvas;
+
+ if (m->diff_rect_focus == NULL) {
+ m->diff_rect_focus = m->diff_list_head;
+ m->i_diff_rect_focus = 1; /* first rect */
+ }
+ else {
+ switch (event.GetId()) {
+ case ID_GOTO_NEXT:
+ if (m->diff_rect_focus->next != NULL) {
+ m->diff_rect_focus = m->diff_rect_focus->next;
+ m->i_diff_rect_focus++;
+ }
+ break;
+ case ID_GOTO_PREV:
+ if (m->diff_rect_focus->prev != NULL) {
+ m->diff_rect_focus = m->diff_rect_focus->prev;
+ m->i_diff_rect_focus--;
+ }
+ break;
+ default: /* 't' Center on current focus rect */
+ break;
+ }
+ }
+#if wxUSE_STATUSBAR
+ char str[32];
+ sprintf( str, "At: %d / %-10d", m->i_diff_rect_focus, m->diff_list_count);
+ status_bar->SetStatusText(wxString(str, wxConvUTF8), 2);
+#endif // wxUSE_STATUSBAR
+ m->goto_diff_rect_focus(); /* Make this one the center */
+
+}
+
+void MyFrame::OnSettings( wxCommandEvent &event )
+{
+ MyCanvas *m = m_canvas;
+
+#if 0000 /* TBI */
+
+ wxMessageDialog *settings_dlg;
+
+ settings_dlg = new WxMessageDialog(this, _T("Change Difference Settings\n"),
+ _T("Pixel Window Color Tolerance\n"),
+ wxCANCEL | wxOK );
+
+ m_spin_pix_win = new wxSpinButton( settings_dlg, ID_SPIN_PIX_WIN, wxPoint( 20, 40 ) );
+ m_spin_pix_win->SetRange( 0, MAX_PIXWINDOW );
+ m_spin_pix_win->SetValue( m->i_pix_window );
+
+ m_spin_col_tol = new wxSpinButton( settings_dlg, ID_SPIN_COL_TOL, wxPoint( 20, 80 ) );
+ m_spin_col_tol->SetRange( 0, 255 );
+ m_spin_col_tol->SetValue( m->color_tolerance );
+
+
+ switch (event.GetId()) {
+ case ID_PIXEL_WINDOW:
+ settings_dlg.SetFocus( m_spin_pix_win );
+ break;
+ case ID_COLOR_TOLERANCE:
+ settings_dlg.SetFocus( m_spin_col_tol );
+ break;
+ default:
+ break;
+ }
+
+ /* on "Find Diffs" BUtton */
+ if ( m_spin_window.GetValue != m->i_pix_window ||
+ m_spin_tolerance.GetValue != m->color_tolerance )
+
+#endif // 0000
+}
+
+//-----------------------------------------------------------------------------
+// MyApp
+//-----------------------------------------------------------------------------
+
+bool MyApp::OnInit()
+{
+
+#if wxUSE_PNM
+ wxImage::AddHandler( new wxPNMHandler );
+#endif
+
+ MyFrame *frame = new MyFrame();
+ frame->Show( true );
+
+ return frame->ProcessArgs(MyApp::argc, MyApp::argv);
+}