// VideoTool.cpp : Defines the entry point for the console application. #include #include #include #include #include #include #include #include #include #include // TODO: reference additional headers your program requires here #include "cv.h" #include "highgui.h" bool alreadyHaveIt(int j,int imageIndices[12]); // ARGH! why is this necessary ?? #define _NO_HMC_WINSOCK_ #include "SmallSocket.h" #undef _NO_HMC_WINSOCK_ using namespace std; // Our Globals! double Hue_Min = -.1; double Hue_Max = .5; double Sat_Min = .12; double Sat_Max = .99; double Val_Min = .04; double Val_Max = .99; double Val_Dist_Max = 2; double Val_Dist_Min = .5; int R_Min = 20; // these are the two functions you'll need to write void RGBtoHSV( double r, double g, double b, double *h, double *s, double *v ); void makeVerticalCut(IplImage* frame, IplImage* f_p, int x, int ymin, int ymax, int* color, int* transitions); void makeHorizontalCut(IplImage* frame, IplImage* f_p, int y, int xmin, int xmax, int* x1, int* transitions); int colorOf(double h, double s, double v); struct Card processCard(IplImage* frame, IplImage* f_p); int getColor(double r, double g, double b, double sat); double getIntensity(IplImage* frame, int x, int y, int cardColor); void playSet(); bool isASet(struct Card i, struct Card j, struct Card k); bool isValid(int sum); void highlight(IplImage* frame); void safe(uchar* pix); void printCard(struct Card c); // globals for tuning parameters by keyboard... static int blueValue = 255; double redHueMin = 330; double redHueMax = 30; double satMin = .3; double valMin = .1; double greenHueMin = 90; double greenHueMax = 150; double purpleHueMin = 210; double purpleHueMax = 270; // these are close, but not quite right double clearThreshold = 75; double shadedThreshold = 25; static int MAX_DIST = 3; static int MAX_ERROR = 100; typedef enum { RED = 0, GREEN, PURPLE, SOLID, SHADED, CLEAR, DIAMOND, OVAL, SQUIGGLE, OMFGWEFUCKEDUP }; struct Card { int number, color, shape, texture, index; }; void setGlobals(); // the code appears at the very bottom of this file 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"}; // 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 getImage0FromVideoBMPs = 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 CvArr* undisMap; // handle mouse clicks here void mouse_callback(int event, int x, int y, int flags) { 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 } } // 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" 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]) << ")"; 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; } } // the thread for the server DWORD WINAPI ThreadFunc( LPVOID lpParam ) { char recvbuf[80]; char sendbuf[80]; string t("HI"); SmallSocket SS(SmallSocket::SERVER); sprintf( recvbuf, "Server Started", *(DWORD*)lpParam ); //MessageBox( NULL, szMsg, "ThreadFunc", MB_OK ); 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; } 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", 0, 0); SS.sendout(sendbuf,80); } // pause for a bit... Sleep(10); } return 0; } int main( int argc, char** argv ) { // in case you want to log things... //ofstream logfile("logfile_out.txt"); //ifstream loginfile("logfile_in.txt"); // set up the globals... setGlobals(); // 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 ); } /* * set up the video windows */ for (int i=0 ; i<12 ; ++i) cvNamedWindow( setwnd[i], CV_WINDOW_AUTOSIZE ); 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 ); /* * 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 (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) { 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); } else if (getImageFromFile || getImageAgainFromFile) { if (getImageFromFile) { // ask for a frame filename int i; // should factor this path out!! if (getImage0FromVideoBMPs) { i = 0; getImage0FromVideoBMPs = false; } else { cout << "What image number (in C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\VideoBMPs): "; cin >> i; } sprintf(filename2,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\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; } else if (getImageAgainFromFile) { cvReleaseImage(&undis_frame_painted); undis_frame_painted = cvCloneImage(undis_frame); } } else { ; } // click only on undis_image pixelInformation(undis_frame_painted); // save the images to a file, if desired if (bmpRecording == true && capturing == true) { sprintf(filename,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\BMPs\\image%05d.bmp",frameNumber); cout << "Captured single image (dist): " << filename << endl; cvSaveImage(filename,frame); sprintf(filename2,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\BMPsUnd\\image%05d.bmp",frameNumber); cout << "Captured single image (undist): " << filename2 << endl; cvSaveImage(filename2,undis_frame); ++frameNumber; //saveNextImage = false; //if (getImageFromFile) { // bmpRecording = false; //} 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) { //processCard(undis_frame, undis_frame_painted); playSet(); detectRed = false; } /* * display everything in the windows */ cvShowImage(wndname, frame); cvShowImage(wndname2, undis_frame); cvShowImage(wndname3, undis_frame_painted); //handle key presses /* * quits */ if( (keyCode = cvWaitKey( 10 )) == ((int)'q') ) { break; // quit everything } /* * we use = and - to increase and decrease blueValue */ else if (keyCode == (int)'=') { if (blueValue < 251) blueValue += 5; } else if (keyCode == (int)'-') { if (blueValue > 4) blueValue -= 5; } 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)'0'){ if(Val_Dist_Max +.1 <=3.0) Val_Dist_Max +=.1; } else if (keyCode == (int)'9'){ if(Val_Dist_Max -.1 >=0.0) Val_Dist_Max -=.1; } else if (keyCode == (int)')'){ if(Val_Dist_Min +.1 <=1.0) Val_Dist_Min +=.1; } else if (keyCode == (int)'('){ if(Val_Dist_Min -.1 >=0.0) Val_Dist_Min -=.1; } 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; } /* * plays the game of Set */ else if (keyCode == (int)'S') { capturing = false; getImageFromFile = false; char filenameset[500]; // for filename... /* is this the first play? -- we need to open 12 windows... */ if (firstTimeForSet == true) { srand( (unsigned)time( NULL ) ); cout << "Setting up set!" << endl; setimages = new IplImage*[12]; // should delete this somewhere... for (int i=0 ; i<12 ; ++i) { sprintf(filenameset,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\VideoBMPs\\image%05d.bmp",i); //cvReleaseImage(&undis_frame); setimages[i] = cvLoadImage(filenameset); // load image0 12 times. cout << "Setting up window " << i << " named " << setwnd[i] << endl; cvShowImage(setwnd[i], setimages[i]); } firstTimeForSet = false; } else { cout << "Reading in random images..." << endl; int imageIndices[12]; for (int i=0 ; i<12 ; ++i) { imageIndices[i] = -1; } for (int i=0 ; i<12 ; ++i) { cout << "i is " << i << endl; while (1) { int randImage = (int)(81.0*rand()/RAND_MAX); if (randImage < 0 || randImage > 80) continue; cout << " randImage is " << randImage << endl; bool b = alreadyHaveIt(randImage,imageIndices); if (!b) { imageIndices[i] = randImage; } else { cout << "Have it "; for (int i=0 ; i<12 ; ++i) cout << imageIndices[i] << " "; cout << endl; continue; } break; } sprintf(filenameset,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\VideoBMPs\\image%05d.bmp",imageIndices[i]); //cvReleaseImage(&(setimages[i])); setimages[i] = cvLoadImage(filenameset); // load image0 12 times. cout << "Setting up window " << i << " named " << setwnd[i] << " with image #" << imageIndices[i] << endl; cvShowImage(setwnd[i], setimages[i]); } } } /* * records video to bitmaps (files) */ 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 */ 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; } /* * the spacebar advances the image in the VideoBMPs directory */ else if (keyCode == (int)' ') { if (showing_savedImage == true) { int i; sscanf(filename2,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\VideoBMPs\\image%05d.bmp",&i); sprintf(filename2,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\VideoBMPs\\image%05d.bmp",++i); cvReleaseImage(&undis_frame); undis_frame = cvLoadImage(filename2); cvReleaseImage(&undis_frame_painted); undis_frame_painted = cvLoadImage(filename2); // wrap around! if (undis_frame == NULL) { i = 0; sprintf(filename2,"C:\\Program Files\\ERSP\\sample_code\\driver\\VideoTool\\VideoBMPs\\image%05d.bmp",i); cvReleaseImage(&undis_frame); undis_frame = cvLoadImage(filename2); cvReleaseImage(&undis_frame_painted); undis_frame_painted = cvLoadImage(filename2); continue; } 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; getImage0FromVideoBMPs = true; bmpRecording = false; saveNextImage = false; showing_savedImage = true; } } } // release things before quitting cvReleaseCapture( &capture ); cvDestroyWindow("result"); } // end of main bool alreadyHaveIt(int j,int imageIndices[12]) { for (int i=0 ; i<12 ; ++i) { if (imageIndices[i] == j) return true; } return false; } // just in case you'd like to use these... inline double mymin(double a, double b) { return ab?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 // 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; } struct Card processCard(IplImage* frame, IplImage* f_p) { //First, make a vertical cut down the middle CvPoint c1, c2; c1.x = 0; c1.y = 0; c2.x = 320; c2.y = 240; int x = (c2.x - c1.x) / 2 + c1.x; // for(int x = 0; x < 320; ++x) // { int cardColor, transitions; // sets color and transitions (most of the time) makeVerticalCut(frame, f_p, x, c1.y, c2.y, &cardColor, &transitions); //cout << "Cut's color is " << cardColor << " and transitions is " << transitions << endl; int texture, number, dummy, shape; //cout << "Transitions: " << transitions << endl; // sets number if (transitions > 1) { makeVerticalCut(frame, f_p, 11.0/14*c2.x, c1.y, c2.y, &dummy, &transitions); if (transitions > 1) { number = 3; if (dummy != cardColor) cardColor = dummy; } else number = 1; } else { number = 2; makeVerticalCut(frame, f_p, 9.0/14*c2.x, c1.y, c2.y, &cardColor, &transitions); } // sets texture double sumIntensity = 0; if (number == 2) // take patch to side { for (int x = -5; x <= 5; ++x) { int xBox = 9.0/14*c2.x + x; // offset by x from middle of shape for (int y = 115; y <= 125; ++y) { double i = getIntensity(frame, xBox, y, cardColor); // returns avg of the two colors that the shape isn't uchar* painted = cvPtr2D(f_p, y, xBox); painted[0] = 50; painted[1] = 0; painted[2] = 100; sumIntensity += i; } } } else // take patch in middle { for (int x = -5; x <= 5; ++x) { int xBox = 7.0/14*c2.x + x; // offset by x from middle of shape for (int y = 115; y <= 125; ++y) { double i = getIntensity(frame, xBox, y, cardColor); // returns avg of the two colors that the shape isn't uchar* painted = cvPtr2D(f_p, y, xBox); painted[0] = 50; painted[1] = 0; painted[2] = 100; sumIntensity += i; } } } double avgIntensity = sumIntensity / 121; // 121 pixels in the 11x11 patch we are sampling if (avgIntensity > clearThreshold) texture = CLEAR; else if (avgIntensity > shadedThreshold) texture = SHADED; else texture = SOLID; /*cout << "Color: " << cardColor << " Number: " << number << " Texture: "; switch(texture) { case 3: cout << "Solid" << endl; break; case 4: cout << "Shaded" << endl; break; case 5: cout << "Clear" << endl; break; }*/ // sets shape shape = -1; vector line_x; int numLines = 7; for(int index = 0; index < numLines; ++index) line_x.push_back(0); for (int index = 0; index < numLines; ++index) { makeHorizontalCut(frame, f_p, 190 - (index/11.0)*c2.y, c1.x, c2.x, &line_x[index], &transitions); } double tolerance = 2.5; int correct = 0; int localThreshold = 20; int min = 320; // only look for min on the inner 5 lines (ignore the top and bottom lines) for (int j = 1; j < numLines-1; ++j) { if (line_x[j] != -1) // if no first transition, then ignore, because x value is -1 { if (line_x[j] < min) min = line_x[j]; } } double sum = 0.0; int linesSummed = 0; for (int j = 1; j < numLines-1; ++j) { if (line_x[j] != -1) // if no first transition, then ignore, because x value is -1 { if (line_x[j] < min + localThreshold && line_x[j] > min - localThreshold) // used to stop from measuring transitions on wrong part of shape { sum += line_x[j] - min; ++linesSummed; } } } //test used to determine what case is causing a triangle to be a squiggle //cout << (line_x[1] > line_x[2]) << (line_x[5] > line_x[4]) << (line_x[3] < line_x[2]) << (line_x[0] > line_x[1]) << endl; //test used to determine what tolerance should be //cout << "min: " << min << ", sum: " << sum << ", linesSummed: " << linesSummed << ", sum/linesSummed: " << sum/linesSummed; //test used to print the x location of the first transitions for each line //cout << "min: " << min; //for (int j = 1; j < numLines-1; ++j) // cout << ", line " << j << " " << line_x[j]; //cout << endl; if ((sum/linesSummed) < tolerance) { shape = OVAL; } else if (line_x[1] > line_x[2] && line_x[5] > line_x[4] && (line_x[3] < line_x[2] || line_x[0] > line_x[1])) { shape = DIAMOND; } else { shape = SQUIGGLE; } /* cout << "Color: " << cardColor << " Number: " << number << " Texture: "; switch(texture) { case 3: cout << "Solid"; break; case 4: cout << "Shaded"; break; case 5: cout << "Clear"; break; } cout << " Shape: "; switch(shape) { case 6: cout << "DIAMOND" << endl; break; case 7: cout << "OVAL" << endl; break; case 8: cout << "SQUIGGLE" << endl; break; default: cout << "oops" << endl; break; } */ struct Card result; result.number = number; result.shape = shape; result.texture = texture; result.color = cardColor; return result; } void makeVerticalCut(IplImage* frame, IplImage* f_p, int x, int ymin, int ymax, int* cardColor, int* transitions) { // some useful local variables... double avgColor = 0; double numColor = 0; int currentColor = OMFGWEFUCKEDUP; int prevColor = OMFGWEFUCKEDUP; double r,g,b; double hue,sat,val; bool tran = false; bool firstTransition = true; *transitions = 0; for (int y = ymin; y < ymax; ++y) { // 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 uchar* color = cvPtr2D(frame,realy,x); // get the pixel we care about uchar* painted = cvPtr2D(f_p, realy, x); r = (int)(color[0]); // 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[2]); // the blue component of the pixel we care about // you might want to call this here! RGBtoHSV(r,g,b,&hue,&sat,&val); int pixelColor = getColor(r, g, b, sat); if (tran && pixelColor == currentColor) { (*transitions)++; if(firstTransition) *cardColor = 2 - currentColor; // swaps RED & PURPLE firstTransition = false; tran = false; } else if (tran && prevColor == pixelColor) { tran = false; } else if (pixelColor != currentColor) { tran = true; } prevColor = currentColor; currentColor = pixelColor; painted[0] = 0; painted[1] = 0; painted[2] = 0; switch(pixelColor) { case RED: numColor++; painted[0] = 255; break; case GREEN: avgColor+=GREEN; painted[1] = 255; numColor++; break; case PURPLE: avgColor+=PURPLE; painted[2] = 255; numColor++; break; case OMFGWEFUCKEDUP: break; default: cerr << "Unrecognized pixel.\n"; exit(32767); } } double col = avgColor / numColor; if(numColor == 0) col = -1; //cout << avgColor << " " << numColor << " " << col << endl; /*if (col < 2.0 / 3.0 && col >= 0.0) *cardColor = PURPLE; else if (col >= 2.0 / 3.0 && col < 4.0 / 3.0) *cardColor = GREEN; else if (col >= 4.0 / 3.0 && col <= 6.0 / 3.0) *cardColor = RED; else *cardColor = OMFGWEFUCKEDUP;*/ } void makeHorizontalCut(IplImage* frame, IplImage* f_p, int y, int xmin, int xmax, int* x1, int* transitions) { //Make a few horizontal cuts through the image to look for shape // some useful local variables... double avgColor = 0; double numColor = 0; int currentColor = OMFGWEFUCKEDUP; int prevColor = OMFGWEFUCKEDUP; double r,g,b; double hue,sat,val; bool tran = false; bool firstTransition = true; *x1 = -1; *transitions = 0; for (int x = xmin; x < xmax; ++x) { uchar* color = cvPtr2D(frame,y,x); // get the pixel we care about uchar* painted = cvPtr2D(f_p, y, x); r = (int)(color[0]); // 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[2]); // the blue component of the pixel we care about // you might want to call this here! RGBtoHSV(r,g,b,&hue,&sat,&val); int pixelColor = getColor(r, g, b, sat); if (tran && pixelColor == currentColor) { (*transitions)++; if(firstTransition && x > 15) // the x > 15 is sort out a few problems with green pixels on the far left { *x1 = x; firstTransition = false; } tran = false; } else if (tran && prevColor == pixelColor) { tran = false; } else if (pixelColor != currentColor) { tran = true; } // if no transitions were found... if (firstTransition) *x1 = -1; prevColor = currentColor; currentColor = pixelColor; painted[0] = 0; painted[1] = 0; painted[2] = 0; switch(pixelColor) { case RED: painted[0] = 255; break; case GREEN: painted[1] = 255; break; case PURPLE: painted[2] = 255; break; case OMFGWEFUCKEDUP: break; default: cerr << "Unrecognized pixel.\n"; exit(32767); } } } int colorOf(double h, double s, double v) { //Whee, red straddling the zero boundary screws everything up if ((h < redHueMax || h > redHueMin) && (s > satMin) && (v > valMin)) return RED; //Green if ((h < greenHueMax && h > greenHueMin && s > satMin && v > valMin)) return GREEN; if ((h < purpleHueMax && h > purpleHueMin && s > satMin && v > valMin)) return PURPLE; return OMFGWEFUCKEDUP; } 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; pt4.y = 12; pt5.x = 240; pt5.y = 228; pt6.x = 92; pt6.y = 24; pt7.x = 228; pt7.y = 216; } int getColor(double r, double g, double b, double sat) { int maxColor; int threshold = 13; if (r < 5 && g < 16 && b < 16) maxColor = RED; else if (r >= g && r >= b) maxColor = RED; else if (g > b) maxColor = GREEN; else maxColor = PURPLE; if(sat > 0.6) return maxColor; else return OMFGWEFUCKEDUP; } double getIntensity(IplImage* frame, int x, int y, int cardColor) { int r,g,b; double hue, sat, val; uchar* color = cvPtr2D(frame,y,x); // get the pixel we care about r = (int)(color[0]); // 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[2]); // the blue component of the pixel we care about RGBtoHSV(r,g,b,&hue,&sat,&val); // this function is getting the wrong colors as input!!!!!!1 if(val < 0.30) return 0; else if (val < 0.43) return 50; else if ((val == 0.43) && (cardColor == PURPLE)) return 50; else return 100; } void playSet() { struct Card cards[12]; // classify cards int index = 0; for (index = 0; index < 12; ++index) { cards[index] = processCard(setimages[index], setimages[index]); cards[index].index = index; } // check for a set for (int i = 0; i < 12; ++i) { for (int j = i + 1; j < 12; ++j) { for (int k = j + 1; k < 12; ++k) { if (isASet(cards[i], cards[j], cards[k])) { printf("Testing cards:\n"); // printCard(cards[i]); // printCard(cards[j]); // printCard(cards[k]); highlight(setimages[cards[i].index]); highlight(setimages[cards[j].index]); highlight(setimages[cards[k].index]); printf("Found a set with cards %d, %d, %d\n", cards[i].index, cards[j].index, cards[k].index); return; } } } } } bool isASet(struct Card i, struct Card j, struct Card k) { int number[4]; number[0] = i.number; number[1] = j.number; number[2] = k.number; int color[4]; color[0] = i.color; color[1] = j.color; color[2] = k.color; int shape[4]; shape[0] = i.shape; shape[1] = j.shape; shape[2] = k.shape; int texture[4]; texture[0] = i.texture; texture[1] = j.texture; texture[2] = k.texture; number[3] = 0; color[3] = 0; shape[3] = 0; texture[3] = 0; for (int i = 0; i < 3; ++i) { number[3] += number[i]; color[3] += color[i]; shape[3] += shape[i]-6; texture[3] += texture[i]-3; } return (isValid(number[3]) && isValid(color[3]) && isValid(shape[3]) && isValid(texture[3])); } bool isValid(int sum) { return (sum == 0 || sum == 3 || sum == 6); } //Highlight the pixels in the card void highlight(IplImage* frame) { // pixels are handled as uchar*'s uchar* painted; // a pixel from the output, "painted" for(int x = 0; x < 320; ++x) { for (int y = 0; y < 240; ++y) { painted = cvPtr2D(frame, y, x); painted[0] += 50; painted[1] += 50; painted[2] += 50; safe(painted); } } } void safe(uchar* pix) { for (int i = 0; i < 3; ++i) if (pix[i] > 255) pix[i] = 255; } void printCard(struct Card c) { cout << "Card #" << c.index << ": " << c.number << " "; switch(c.color) { case RED: cout << "red"; break; case GREEN: cout << "green"; break; case PURPLE: cout << "purple"; break; default: cerr << "Unrecognized color: " << c.color << endl; exit(1); } cout << ", "; switch(c.texture) { case CLEAR: cout << "clear"; break; case SHADED: cout << "shaded"; break; case SOLID: cout << "solid"; break; default: cerr << "Unrecognized texture: " << c.texture << endl; exit(1); } switch(c.shape) { case DIAMOND: cout << "diamond"; break; case OVAL: cout << "oval"; break; case SQUIGGLE: cout << "squiggle"; break; default: cerr << "Unrecognized shape: " << c.shape << endl; exit(1); } if (c.number > 1) cout << "s."; }