// cs154v1.cpp : Defines the entry point for the console application.

/*

This code is slightly modified in order to support programming
the game of set. When the user hits the 'S' (capital S), nine
windows holding set cards (from .\\VideoBMPs) appear.

Images for the cards and separate images for drawing on top of
the cards are available after this: they are in setimages and
setimages_painted.

The function processSetCard is a place-holder for your code
that will determine the number, color, fill, and shape of the
symbols on a particular set card.

Also, once the code determines the symbols on all nine cards, 
it should somehow indicate which three cards (if any) comprise a set... .
This will have to be done in a function outside processSetCard.


+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
How to get OpenCV and this working?

1. Download and install OpenCV for Windows
2. If you don't have visual studio, the "Express Edition" is free - google for it.
3. Double-click the "solution" file (cs154v1.sln)
4. Compile (hopefully!) and run.
5. If you don't have a camera, it's not as much fun :-) Still images are supported, though.
6. Some key presses (with focus on the undistored image window) matter...
   see the code for what each one does. A couple of important ones:
      The 'r' key toggles on and off the selected color
	  The 's' key starts/stops the image grabbing
	  The ' ' (space bar) moves through saved images (in ./VideoBMPs)

7. It is listening on port PORT_NUMBER (default 2000) for a client (in any language)
8. The cs154tk.py file has a couple of messages understood - but much more could be done!

Q. The program quits when you hit 'q' in the undistorted image window or if you send it
   a 'q' from the client. It's not robust enough to recover if you simply kill the 
   client (though this could be done without too much trouble).
 
 */

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <tchar.h>
#include <math.h>
#include <time.h>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

// ARGH! why is this necessary ??
#define _NO_HMC_WINSOCK_
#include "SmallSocket.h"
#undef _NO_HMC_WINSOCK_

using namespace std; // ouch!

/* Global variables */

char wndname[] = "VideoTool";
char wndname2[] = "Undistorted image";
char wndname3[] = "Painted image";
char *setwnd[] = {"set0","set1","set2","set3","set4","set5",
                  "set6","set7","set8","set9","set10","set11"};
#define NUM_WINDOWS 9

void setGlobals();  // the code appears at the very bottom of this file

// Our global color definitions...
int R_Min = 0;
int R_Max = 255;
int G_Min = 0;
int G_Max = 255;
int B_Min = 0;
int B_Max = 255;

double Hue_Min = -1.0; // more generaous than needed, but OK
double Hue_Max = 1.0;  // same is true here
double Sat_Min = 0.0;
double Sat_Max = 1.0;
double Val_Min = 0.0;
double Val_Max = 1.0;

// these are simply a few example functions
void RGBtoHSV( double r, double g, double b, double *h, double *s, double *v );
bool isRed(int r, int g, int b, double h, double s, double v);
void processRed( IplImage* frame, IplImage* f_p );

// here, static refers to the fact that these variables
// are local to this file (they're not available in other files,
// which is safer because it allows reuse of their names)
static CvSize imageSize;
static CvPoint old_click_pt;
static CvPoint new_click_pt;
static double framenum;

// global points for drawing boxes, defining loops, etc.
static CvPoint pt1; 
static CvPoint pt2; 
static CvPoint pt3;
static CvPoint pt4;
static CvPoint pt5;
static CvPoint pt6;
static CvPoint pt7;

// global calibration data
static float* M; 
static float* D;

// more global variables
// these are simply recording all the different states you
// can put the program in via the keyboard
static bool bmpRecording = false;
static bool capturing = true;
static bool getImageFromFile = false;
static bool getImageAgainFromFile = false;
static bool getImage0FromMyImages = false;
static bool saveNextImage = false;
static bool quittingTime = false;
static bool detectRed = false;
static bool showing_savedImage = false;
static bool saveOnlyOneImageToFile = true;
static bool firstTimeForSet = true;

static char* filename = new char[150];
static char* filename2 = new char[150];
static int frameNumber = 0;
static int keyCode = -1;

// pointers to images in memory
static IplImage *frame;
static IplImage* undis_frame;
static IplImage* undis_frame_painted;

static IplImage** setimages;
static IplImage** setimage_frames;
static IplImage** setimages_painted;
static CvArr* undisMap;

// globals for tuning parameters by keyboard...
static int blueValue = 255;

// a set-card-processing function
void processSetCard(int i);

/*
 * a small function to see if j is in the first
 * N of imageIndices's elements
 */
bool alreadyHaveIt(int j, int* imageIndices, int N)
{
	for (int i=0 ; i<N ; ++i)
	  if (imageIndices[i] == j) return true;
	return false;
}

// handle mouse clicks here
void mouse_callback(int event, int x, int y, int flags, void* param)
{
	if (event == CV_EVENT_LBUTTONDOWN)
	{
		// cout << "(x,y) = (" << x << "," << y << ")" << endl;

		// reset old_click_pt
		old_click_pt.x = new_click_pt.x;
		old_click_pt.y = new_click_pt.y;

		// get new click point -- note the coordinate change in y
		new_click_pt.x = x;  // coming in from the window system
		new_click_pt.y = imageSize.height-y;  // window system and images have different y axes
	}
}

//CvMouseCallback theMouseCallback = &mouse_callback;

// print pixel information
void pixelInformation(IplImage* f)
{
	if (f == NULL) return;

	// see if things have changed...
	if (new_click_pt.x != old_click_pt.x || new_click_pt.y != old_click_pt.y) // has it changed?
	{
		if (new_click_pt.x < 0 || new_click_pt.x > 319 || new_click_pt.y < 0 || new_click_pt.y > 239) return;
		// draw a line from old to new
		// cvLine(frame,old_click_pt,new_click_pt,CV_RGB(255,0,0),/*thickness*/1,/*connectivity*/8);
		// set the old = the new
		old_click_pt.x = new_click_pt.x;
		old_click_pt.y = new_click_pt.y;
		// print the RGB values and coordinates of the newly clicked point:
		// note that blue is #0, green is #1, and red is #2 !!

		/* note that this uses the undistorted frame right here... */

		// this is insane!!!
		// see the processRed function for an explanation
		// of why we need "realy" for images from file
		int realy = new_click_pt.y;
		if (getImageAgainFromFile || getImageFromFile) 
		{
			realy = imageSize.height - realy;
		}
		uchar* pixel = cvPtr2D(f,realy,new_click_pt.x);

		cout << "(x,y) = (" << new_click_pt.x << "," << new_click_pt.y << ") with (r,g,b) = ("
			 << (int)(pixel[2]) << "," << (int)(pixel[1]) << "," << (int)(pixel[0]) << ")" << endl;

		double hue,sat,val;
		RGBtoHSV((double)pixel[2],(double)pixel[1],(double)pixel[0],&hue,&sat,&val);

		cout << "           and (h,s,v) = (" << hue << "," << sat << "," << val << ")" << endl;
	}
}



/* End of globals */

/*
 * a pause-then-exit function
 */
void myexit(int y = 0)
{
  int x;
  printf("enter a number to exit\n");
  scanf("%d", &x);
  exit(0);
}


/*
 * the thread for the server
 */
DWORD WINAPI ThreadFunc( LPVOID lpParam ) 
{ 
   	char recvbuf[1024]; // probably don't need this much space...
	char sendbuf[1024];

	string t("HI");
	SmallSocket SS(SmallSocket::SERVER);

    sprintf( recvbuf, "Server Started", *(DWORD*)lpParam ); 
	
	while (true)
	{
		SS.receive(recvbuf,80);
		cout << "Received: " << recvbuf << endl;
		
		if (recvbuf[0] == 'q') {
			SS.sendout("q",2);
			// set a variable that tells it to quit...
			quittingTime = true;
		    // cvReleaseCapture( &capture );
			//	cvDestroyWindow("result");
			break;
		}

		else if (recvbuf[0] == 'e') {
			// just an example of passing in more information
			// and using sscanf to extract it
			// sscanf(recvbuf,"q %lf %lf %lf ",&savedXforLog,&savedYforLog,&savedTforLog);
			//
			//the connection requires that something come back...
			// note that we're sending two zeros every time here...
			sprintf(sendbuf, "%d %d", 42, 2008);
			int message_length = (int)(strlen(sendbuf));
			SS.sendout(sendbuf,message_length);
		}

		else if (!strncmp("sendingdata",recvbuf,(int)strlen("sendingdata"))) {
			// acknowledge message
			cout << "Got a sendingdata message\n\n";
			// parse it
			char s[30];
			sscanf(recvbuf, "%s %d %d %d %d %d %d", s, &R_Min, &R_Max, &G_Min, &G_Max, &B_Min, &B_Max);
			printf("%s %d %d %d %d %d %d\n", s, R_Min, R_Max, G_Min, G_Max, B_Min, B_Max);
	  
			// always need to reply....
			SS.sendout("hi",3);
		}

		else {
			// always need to reply....
			SS.sendout("spam",5);
		}

		// pause for a bit...
		Sleep(10);
	}

    return 0; 

} 

/*
 * the vision-processing thread
 */
int main(int argc, char *argv[])
{
	// start the server's thread 
	// make sure that all imageprocessing is in main's thread
	DWORD dwThreadId, dwThrdParam = 1; 
    HANDLE hThread; 
    char szMsg[80];

    hThread = CreateThread( 
        NULL,                        // default security attributes 
        0,                           // use default stack size  
        ThreadFunc,                  // thread function 
        &dwThrdParam,                // argument to thread function 
        0,                           // use default creation flags 
        &dwThreadId);                // returns the thread identifier 
 
    // Check the return value for success. 
    if (hThread == NULL) 
    {
        sprintf( szMsg, "CreateThread failed." ); 
        MessageBox( NULL, szMsg, "main", MB_OK );
    }



	/* start of our frame-grabbing program */

	setGlobals();

	cvNamedWindow( wndname, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( wndname3, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( wndname2, CV_WINDOW_AUTOSIZE );

    /*
	 * set up the mouse input
	 */
	//cvSetMouseCallback( wndname, mouse_callback );
	cvSetMouseCallback( wndname2, mouse_callback /*theMouseCallback*/ );

	/*
	 * more camera-specific stuff
	 */
	CvCapture* capture = 0;
	capture = cvCaptureFromCAM(0);

	// this is allocating the undis_frame once (it will get written each time)
	// get one frame and then copy it...
	if (capture && cvGrabFrame(capture)) 
	{
		frame = cvRetrieveFrame( capture );
		undis_frame = cvCloneImage(frame);
		undis_frame_painted = cvCloneImage(frame);
		// now let's set up the undistortion parameters
		// note that the docs say 3*wider, but that did not work
		// 3*wider _and_ 3*higher worked...
		undisMap = cvCreateMat( imageSize.width*3, imageSize.height*3, CV_32SC3 );
		// need to release this at the end of the program...
		cvUnDistortInit( frame, undisMap, M, D, 1 );
	}
	else
	{
		cout << "No camera??" << endl;
		capturing = false;
	}

	// main loop
	while (!quittingTime)
    {
		/*
		 * live video capture
		 */
		if (capturing)
		{
			if( !cvGrabFrame( capture ))   break;
			frame = cvRetrieveFrame( capture );
			if( !frame ) break;

			// cvUnDistortOnce( frame, undis_frame, M, D, 1 );  // might take some extra time...
			cvUnDistort( frame, undis_frame, undisMap, 1 );
			cvReleaseImage(&undis_frame_painted);
			undis_frame_painted = cvCloneImage(undis_frame);
		}
		/*
		 * taking image frames from file
		 */
		else if (getImageFromFile || getImageAgainFromFile)
		{
			/*
			 * first-time load from file
			 */
			if (getImageFromFile) {  
				// ask for a frame filename
				int i;
				// should factor this path out!!
				if (getImage0FromMyImages) { 
					i = 0;
					getImage0FromMyImages = false;
				}
				else {
					cout << "What image number (in .\\VideoBMPs): ";
					cin >> i;
				}
				sprintf(filename2,".\\VideoBMPs\\image%05d.bmp",i);
				// we probably need to release the images returned by cvLoadImage somewhere...
				//frame = cvLoadImage(filename2);
				//undis_frame = cvCloneImage(frame);
				cvReleaseImage(&undis_frame);
				undis_frame = cvLoadImage(filename2);
				cvReleaseImage(&undis_frame_painted);
				undis_frame_painted = cvLoadImage(filename2);
				if (undis_frame == NULL) {
					cout << "Could not find that image.\n";
					continue;
				}
				getImageFromFile = false;
				getImageAgainFromFile = true;
			}
			/*
			 * future loads just need to clear the drawing image
			 */
			else if (getImageAgainFromFile) {
				cvReleaseImage(&undis_frame_painted);
				undis_frame_painted = cvCloneImage(undis_frame);
			}
		}
		/*
		 * this doesn't seem necessary...
		 */
		else
		{
			;
		}

		/*
		 * this call will check for any mouse clicks, non-blocking
		 *
		 * ** click only on the undis_image (undistorted image) **
		 */
		pixelInformation(undis_frame_painted);

		// save images to a file, if desired
		if (bmpRecording == true && capturing == true)
		{
			// this will write the file to the subfolder MyImages
			// in the directory in which the .sln file is located
			sprintf(filename2,".\\VideoBMPs\\image%05d.bmp",frameNumber);
			cout << "Captured single image named: " << filename2 <<  endl;
			cvSaveImage(filename2,undis_frame);
			++frameNumber;

			// stop recording if we only wanted one image
			if (saveOnlyOneImageToFile == true) bmpRecording = false;
		}

		// here we can add graphics to each frame
		// in order that they show up on the screen
		// we also decide what to display in this routine
		if (detectRed == true)
		{
			processRed(undis_frame,undis_frame_painted);
		}

		/*
		 * display everything in the windows
		 */
		cvShowImage(wndname, frame);  // camera image
		cvShowImage(wndname2, undis_frame);  // undistorted image
		cvShowImage(wndname3, undis_frame_painted);  // undis. image with user's graphics

		/*
		 * conditionals to handle keypresses
		 *
		 * gets keypress and checks for 'q' to quit
		 */
		if( (keyCode = cvWaitKey( 10 )) == ((int)'q') ) {          
			break;                       // quit everything
		}
		/*
		 * we use = and - to increase and decrease blueValue
		 */

		else if (keyCode == (int)'1') {
			if(Hue_Max - .01 >= -.5) Hue_Max -= .01;
		}
		else if (keyCode == (int)'2') {
			if(Hue_Max + .01 <= 1.0) Hue_Max += .01;
		}
		else if (keyCode == (int)'3') {
			if(Sat_Max - .01 >= 0) Sat_Max -= .01;
		}
		else if (keyCode == (int)'4') {
			if(Sat_Max + .01 <= 1.0) Sat_Max += .01;
		}
		else if (keyCode == (int)'5') {
			if(Val_Max - .01 >= 0) Val_Max -= .01;
		}
		else if (keyCode == (int)'6') {
			if(Val_Max + .01 <= 1.0) Val_Max += .01;
		}
		else if (keyCode == (int)'!') {
			if(Hue_Min - .01 >= -.5) Hue_Min -= .01;
		}
		else if (keyCode == (int)'@') {
			if(Hue_Min + .01 <= 1.0) Hue_Min += .01;
		}
		else if (keyCode == (int)'#') {
			if(Sat_Min - .01 >= 0) Sat_Min -= .01;
		}
		else if (keyCode == (int)'$') {
			if(Sat_Min + .01 <= 1.0) Sat_Min += .01;
		}
		else if (keyCode == (int)'%') {
			if(Val_Min - .01 >= 0) Val_Min -= .01;
		}
		else if (keyCode == (int)'^') {
			if(Val_Min + .01 <= 1.0) Val_Min += .01;
		}
		else if (keyCode == (int)'7'){
			if(R_Min - 5 >= 0) R_Min -=5;
		}
		else if (keyCode == (int)'8'){
			if(R_Min + 5 <= 255) R_Min +=5;
		}
		else if (keyCode == (int)'p') {
			//cout << "Hue_Max: " << Hue_Max << " Hue_Min: " << Hue_Min << endl;
			//cout << "Sat_Max: " << Sat_Max << " Sat_Min: " << Sat_Min << endl;
			//cout << "Val_Max: " << Val_Max << " Val_Min: " << Val_Min << endl;
			//cout << "Val_Dist_Max: " << Val_Dist_Max << "Val_Dist_Min" << Val_Dist_Min << endl;
			cout << "R_Min: " << R_Min << endl;
		}
		/*
		 * write frames to file == 'f'
		 */
		else if (keyCode == (int)'f') {                           
			if (bmpRecording) {
				cout << "Stopping the writing to bitmaps\n";
				bmpRecording = false;
			} else {
				cout << "Starting to write to bitmaps\n";
				bmpRecording = true;
			}
			showing_savedImage = false;
		} 
		/*
		 * gets image from a file # in .\\VideoBMPs (hardcoded)
		 */
		else if (keyCode == (int)'g') {
			cout << "Getting image from file.\n";
			capturing = false;
			getImageFromFile = true;
			getImageAgainFromFile = false;
			bmpRecording = false;
			saveNextImage = false;
			showing_savedImage = true;
		} 
		/*
		 * toggle continuous video
		 */
		else if (keyCode == (int)'s') {
			if (capturing) {
				cout << "Stopping continuous capturing.\n";
				capturing = false;
				getImageFromFile = false;
			}
			else {
				cout << "Starting continuous capturing.\n";
				capturing = true;
				getImageFromFile = false;
				getImageAgainFromFile = false;
				/* reset undis_image */
				// note this assumes that everything went well
				// initially with the first captured frame... !!
				cvReleaseImage(&undis_frame);
				undis_frame = cvCloneImage(frame);
				cvReleaseImage(&undis_frame_painted);
				undis_frame_painted = cvCloneImage(frame);
				// not to mention that these transition images have to be released!
			}
			showing_savedImage = false;
		} 
		else if (keyCode == (int)'e') {
			// emulate server behavior
			saveNextImage = true;
			showing_savedImage = false;
		} 
		/*
		 * toggle finding red (and displaying)
		 */
		else if (keyCode == (int)'r') {
			detectRed = !detectRed;
		}
		/*
		 * a capital 'S' toggles playing set...
		 */
		else if (keyCode == (int)'S') 
		{
			capturing = false;       // we're no longer capturing...
			getImageFromFile = false;
			char filenameset[500];   // holds our set file names... 

			if (firstTimeForSet)     // need to set up the windows if it's
			{                        // the first time playing set...
				for (int i=0 ; i<NUM_WINDOWS ; ++i) {  
					cvNamedWindow( setwnd[i], CV_WINDOW_AUTOSIZE );
					cvMoveWindow( setwnd[i], i%3 * 335, i/3 * 280);  
				} 
				srand( (unsigned)time( NULL ) );       // seed for future random #s
				cout << "Setting up set!" << endl;     // Hi!
				setimages = new IplImage*[NUM_WINDOWS]; // should delete these somewhere...
				setimages_painted = new IplImage*[NUM_WINDOWS]; // for overlays

				for (int i=0 ; i<NUM_WINDOWS ; ++i)
				{
					sprintf(filenameset,"..\\VideoBMPs\\image%05d.bmp",i);
					//cvReleaseImage(&undis_frame);
					setimages[i] = cvLoadImage(filenameset); // load image0 numWindows times.
					setimages_painted[i] = cvLoadImage(filenameset);
					cout << "Setting up window " << i << " named " << setwnd[i] << endl;
					cvShowImage(setwnd[i], setimages[i]);
				}	

				firstTimeForSet = false; // make sure we do this only once
			}
			// don't really want an "else" here
			{
				cout << "Reading in random images..." << endl;

				int imageIndices[NUM_WINDOWS];         // list of indices
				for (int i=0 ; i<NUM_WINDOWS ; ++i) {  // set them all to -1
					imageIndices[i] = -1; 
				}

				for (int i=0 ; i<NUM_WINDOWS ; ++i)
				{
					cout << "i is " << i << endl;     // keep us updated
					while (true) 
					{
						int randImage = (int)(81.0*rand()/RAND_MAX);
						if (randImage < 0 || randImage > 80) continue;
						cout << "  randImage is " << randImage << endl;
						
						if (!alreadyHaveIt(randImage,imageIndices,i))
						   imageIndices[i] = randImage;
						else
						{
							cout << "Have it ";
							for (int i=0 ; i<NUM_WINDOWS ; ++i)
								cout << imageIndices[i] << " ";
							cout << endl;
							continue;
						}
						break;
					}

					/*
					 * now load image i
					 */
					sprintf(filenameset,".\\VideoBMPs\\image%05d.bmp",imageIndices[i]);
					cout << "Reading in the file " << filenameset << " for #" << i << endl;
					//cvReleaseImage(&(setimages[i]));
					setimages[i] = cvLoadImage(filenameset);         // for proceesing
					setimages_painted[i] = cvLoadImage(filenameset); // for showing results
					cout << "Setting up window " << i << " named " << setwnd[i] << " with image #" << imageIndices[i] << endl;
					cvShowImage(setwnd[i], setimages[i]);
				}
			}
			/*
			 * now, process the cards
			 */
			int i=3;
			processSetCard( i );
		}
		/*
		 * the spacebar advances the image in the VideoBMPs directory
		 */
		else if (keyCode == (int)' ') {
			if (showing_savedImage == true)
			{
				int i;
				// get the last frame number from the variable filename2
				sscanf(filename2,".\\VideoBMPs\\image%05d.bmp",&i);
				// reset filename2 to hold the next frame number
				sprintf(filename2,".\\VideoBMPs\\image%05d.bmp",++i);
				cvReleaseImage(&undis_frame);
				undis_frame = cvLoadImage(filename2);
				cvReleaseImage(&undis_frame_painted);
				undis_frame_painted = cvLoadImage(filename2);

				// wrap around! (if necessary)
				if (undis_frame == NULL) 
				{
					i = 0;
					sprintf(filename2,".\\VideoBMPs\\image%05d.bmp",i);
					cvReleaseImage(&undis_frame);
					undis_frame = cvLoadImage(filename2);
					cvReleaseImage(&undis_frame_painted);
					undis_frame_painted = cvLoadImage(filename2);
					continue;
				}

				// we should be OK to keep going now
				cout << "Getting image " <<  i << " from .\\VideoBMPs.\n";
				getImageAgainFromFile = true;
				getImageFromFile = false;
			}
			/*
			 * initial hit of the space bar...
			 */
			else {
				cout << "No image from file is currently showing..." << endl;
				cout << "Getting image 0 from .\\VideoBMPs.\n";
				capturing = false;
				getImageFromFile = true;
				getImageAgainFromFile = false;
				getImage0FromMyImages = true;
				bmpRecording = false;
				saveNextImage = false;
				showing_savedImage = true;
			}
		}
		
	}

	// release things before quitting
    cvReleaseCapture( &capture );
	cvDestroyWindow("result");

    return 0;
}

// just in case you'd like to use these...
inline double mymin(double a, double b) { return a<b?a:b; }
inline double mymax(double a, double b) { return a>b?a:b; }

// read about HSV at http://en.wikipedia.org/wiki/HSV_color_space
// and http://www.cs.rit.edu/~ncs/color/t_convert.html#RGB%20to%20HSV%20&%20HSV%20to%20RGB
//
// feel free to grab code from above or elsewhere online, if you'd like to 
// be sure you understand what RANGE _your_ code for HSV is "returning"
// in the h, s, and v pointers...
void RGBtoHSV( double r, double g, double b, double *h, double *s, double *v )
{
	double maxRGB;
	double minRGB;
	double delta;

	// get max and min
	if(r >= g && r >= b)
	{
		maxRGB = r;
		if(g >= b)
			minRGB = b;
		else
			minRGB = g;
	}
	else if(g >= b)
	{
		maxRGB = g;
		if(r >= b)
			minRGB = b;
		else
			minRGB = r;
	}
	else
	{
		maxRGB = b;
		if(r >= g)
			minRGB = g;
		else
			minRGB = r;
	}

	delta = maxRGB - minRGB;
	*v = maxRGB/255.0;

	if (maxRGB <= 0.0)
	{
		*h = -1;
		*s = 0;
		return;
	}

	*s = delta/maxRGB;   // better scaling than 255!
	if(delta == 0)
	{
		*h = -1;
		return;
	}

	if(r == maxRGB)
		*h = (g - b)/delta;
	else if (g == maxRGB)
		*h = (1.0/3.0) + (b-r)/delta;
	else
		*h = (2.0/3.0) + (r-g)/delta;
}

// this determines whether a pixel is red or not...
bool isRed(int r, int g, int b, double hue, double sat, double val)
{
	if (/*(hue >= Hue_Min) &&
	    (hue <= Hue_Max) &&
		(sat >= Sat_Min) &&
		(sat <= Sat_Max) &&
		(val >= Val_Min) &&
		(val <= Val_Max) && */
		(r >= R_Min) &&
		(r <= R_Max) &&
		(g >= G_Min) &&
		(g <= G_Max) &&
		(b >= B_Min) &&
		(b <= B_Max) &&
		true)
	{
		return true;
	}
	else
	{
		return false;
	}
}

/*
 * here is where the set-processing happens
 * for image #i
 */
void processSetCard(int i) 
{
	// we know that the images to process are in setimages,
	// and we will draw on setimages_painted
    IplImage* sourceImage = setimages[i];
	IplImage* destinationImage = setimages_painted[i];
	char* destinationWindow = setwnd[i];

	// show where the point 30,30 is in the image
	CvPoint pt; pt.x = 30; pt.y = 200;
	cvCircle( destinationImage, pt, 10,  CV_RGB(255,255,0), /*thickness*/ 4 );

	// show some pixel manipulation - pixels are handled as uchar*'s
	int r,g,b,realrow;
	uchar* sourcepix;     // a pixel in the input, "frame"
	uchar* destpix;   // a pixel in the output, "painted"
	int row_offset = -50;
	int col_offset = -50;
	for (int row=10 ; row<30 ; ++row)
	{
		for (int col=200 ; col < 230 ; ++col)
		{
			// realrow = row;
			realrow = imageSize.height - row;
			sourcepix = cvPtr2D(sourceImage,realrow,col);    // get the pixel we care about
			r = (int)(sourcepix[2]);  // the red component of the pixel we care about
			g = (int)(sourcepix[1]);  // the green component of the pixel we care about
			b = (int)(sourcepix[0]);  // the blue component of the pixel we care about
			destpix = cvPtr2D(destinationImage,realrow+row_offset,col+col_offset);    // get the pixel we (might) change
			destpix[0] = b; destpix[1] = g; destpix[2] = r;
		}
	}
	cvShowImage(destinationWindow, destinationImage);
}

// here is where we seek out the red molding and indicate what we've found
// the input named "frame" is the source location of our pixels
// the "output" named f_p is the destination for drawing
void processRed(IplImage* frame, IplImage* f_p)
{
    // some useful local variables...
	int r,g,b;
	double hue,sat,val;

	// pixels are handled as uchar*'s
	uchar* color;     // a pixel from the input, "frame"
	uchar* painted;   // a pixel from the output, "painted"

	// this draws a rectangle from pt4 to pt5 with the color
	// specified by the three RGB values in the CV_RGB macro
	// the R, G, and B channels are all 0 to 255...

	// see the "this is insane!" caution below in order to
	// use graphics on the live video stream...
	cvRectangle(f_p, pt4, pt5, CV_RGB(0,0,255), 1); // 1 == thickness
	cvCircle( f_p, pt4, 10,  CV_RGB(255,255,0), /*thickness*/ 1 );

	// This loops through the pixels in the rectangle specified
	// by pt4 and pt5 (see setGlobals for their corners)

	int xtotal = 0;
	int ytotal = 0;
	int xlow = (int)mymin(pt4.x,pt5.x);
	int xhi = (int)mymax(pt4.x,pt5.x);
	int ylow = (int)mymin(pt4.y,pt5.y);
	int yhi = (int)mymax(pt4.y,pt5.y);
	for (int x = xlow; x<xhi; x++)		//the x direction goes left to right       
	{
		for (int y = ylow; y<yhi; y++)  //the y direction goes top to bottom
		{
			// this is insane!!!
			// unfortunately, access to the pixels is different depending on
			// the source of the pixels: static images and video reverse their
			// y orientations

			// we handle this here...
			int realy = y;
			if (getImageAgainFromFile || getImageFromFile) 
			{
				realy = imageSize.height - realy;  // reverse if static image
			}

			// note that we need to use "realy" in each of these cases
			color = cvPtr2D(frame,realy,x);    // get the pixel we care about
			painted = cvPtr2D(f_p,realy,x);    // get the pixel we (might) change

			r = (int)(color[2]);  // the red component of the pixel we care about
			g = (int)(color[1]);  // the green component of the pixel we care about
			b = (int)(color[0]);  // the blue component of the pixel we care about

			RGBtoHSV(r,g,b,&hue,&sat,&val);

			if(isRed(r,g,b,hue,sat,val))
			{
				painted[0] = 0;
				painted[1] = 255;
				painted[2] = 0;
			}
			else {
				//painted[0] = 0;
				//painted[1] = 0;
				//painted[2] = 0;
			}
			
		}
	}
}

	

void setGlobals()
{
	// mouse click locations (old and new, to tell if we've
	// clicked on a new point
	old_click_pt.x = 0;
	old_click_pt.y = 0;
	new_click_pt.x = 0;
	new_click_pt.y = 0;

	// in theory these should get deleted at the end of the program, too...

	// this is the calibration matrix of the camera
	M = new float[9];
	M[0]=    393.1788940f; M[1]=      0.0000000f; M[2]=    162.8373871f;
	M[3]=      0.0000000f; M[4]=    395.7017517f; M[5]=    121.8078156f;
	M[6]=      0.0000000f; M[7]=      0.0000000f; M[8]=      1.0000000f;

	// these are the distortion parameters of the camera
    D = new float[4];
	D[0]=-0.358375f;
	D[1]=0.168058f;
	D[2]=-0.006497f;
	D[3]=0.001283f;

    // theseimage sizes are hard-coded, but should probably be obtained for each
    // image individually -- however, with our cameras and images, this is safe.
	imageSize.width = 320; imageSize.height = 240;

	// set up some global points for drawing, etc.
    int margin = 12;
	pt1.x = margin; pt1.y = margin; 
    pt2.x = imageSize.width-margin; pt2.y = imageSize.height-margin; 

	pt3.x = imageSize.width/2;
	pt3.y = imageSize.height/2;

	pt4.x = 80;   // p4 and p5 are the rectangle in which we
	pt4.y = 12;   // search for pixels of the correct h, s, v...

	pt5.x = 240;
	pt5.y = 228;

	pt6.x = 92; 
	pt6.y = 24;

	pt7.x = 228;
	pt7.y = 216;

}


