// VideoTool.cpp : Defines the entry point for the console application. #include #include #include #include #include #include #include #include // TODO: reference additional headers your program requires here #include "cv.h" #include "highgui.h" // ARGH! why is this necessary ?? #define _NO_HMC_WINSOCK_ #include "SmallSocket.h" #undef _NO_HMC_WINSOCK_ using namespace std; int colorMode = 0; // Our Reds! double RedHueMin = -.1; double RedHueMax = .5; double RedSatMin = .12; double RedSatMax = .99; double RedValMin = .04; double RedValMax = .99; double RedValFactorMax = 2; double RedValFactorMin = .5; int RedRMin = 20; // Our Greens! double GreenHueMin = -.3; double GreenHueMax = 1.0; double GreenSatMin = .16; double GreenSatMax = .40; double GreenValMin = .2; double GreenValMax = .65; double GreenValFactorMax = 2; double GreenValFactorMin = .5; int GreenGMin = 20; int GreenGMax = 170; int GreenGMaxBy = 5; bool foundRed = false; bool toggle = false; // used to display rectangle results only once per static loaded image // Our Purples! double PurpleHueMin = -.4; double PurpleHueMax = 1.2; double PurpleSatMin = .00; double PurpleSatMax = .21; double PurpleValMin = .01; double PurpleValMax = .99; double PurpleValFactorMax = 2; double PurpleValFactorMin = .5; int PurpleColorMax = 180; int PurpleRMin = 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 processRed( IplImage* frame, IplImage* f_p ); void processGreen( IplImage* frame, IplImage* f_p ); void processPurple( IplImage* frame, IplImage* f_p ); void erode(int** dst, int** src, int rows, int cols); void erode3(int** dst, int** src, int rows, int cols); void dilate(int** dst, int** src, int rows, int cols); // set these for calibration of Rectangle position estimation int knownPixelHeight = 70; // (dummy value) double knownDistance = 121; // cm (dummy value) double knownRectWidth = 30.48; // width of the rectangle (real world) in cm double distCalibration = 9125.0; double systematicAngleError = -6; double cameraAngleWidth = 40.0; bool seeLandmark = false; double landmarkDist = 0; double landmarkAngle = 0; // given the height of a vertical line in pixels, return its distance from the camera double pixelHeightToDistance(int pixelHeight); // given the distance to the left edge and the right edge of a known rectangle, determines the distance to the center of the rectangle double calculateDist(double leftDist, double rightDist); // given the width of the window in pixels and the horizontal location of the center of the rectangle in pixels returns its angle from the camera double calculateAngle(int rectLocation); bool isRectColor(int r, int g, int b, double hue, double sat, double val); // process the image to find a rectangle landmark and determine its location relative to the camera void processRectangle(IplImage* frame, IplImage* f_p); POINT centroid(int** matrix, int value, int rows, int cols); int connectedComponents(int** matrix, int r, int c, int oldVal, int newVal, int rows, int cols); void setGlobals(); // the code appears at the very bottom of this file char wndname[] = "VideoTool"; char wndname2[] = "Undistorted image"; char wndname3[] = "Painted image"; // 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 detectGreen = false; static bool detectPurple = false; static bool showing_savedImage = false; 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 CvArr* undisMap; // globals for tuning parameters by keyboard... static int blueValue = 255; // 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? { // 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 << ")\n 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; if(isRectColor(pixel[2], pixel[1], pixel[0], hue, sat, val)) cout << "\tMe thinks its Green\n"; else cout << "\tMe thinks it ain't Green\n"; } } // 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); } if (recvbuf[0] == 'r') { detectRed = !detectRed; sprintf(sendbuf, "HI!"); SS.sendout(sendbuf, 80); } if (recvbuf[0] == 's') { int red = 0; if(foundRed) red = 1; sprintf(sendbuf, "%d\n", red); SS.sendout(sendbuf, 80); if(red == 1) cout << "Sent sees red\n"; else cout << "Sent does not see red\n"; } if(recvbuf[0] == 'l') { char yorn; if(seeLandmark) { yorn = 'y'; //sprintf(sendbuf, "%f %f\n", landmarkDist, landmarkAngle); cout << "Sent sees landmark(y)" << endl << "Dist: " << landmarkDist << "\tAngle: " << landmarkAngle << endl << endl; } else { yorn = 'n'; //sprintf(sendbuf, "%f %f\n", -1, 0); cout << "Sent no landmark seen (n)" << endl << endl; cout << "For the record\t" << "Dist: " << landmarkDist << "\tAngle: " << landmarkAngle << endl << endl; } sprintf(sendbuf, "%c %f %f\n", yorn, landmarkDist, landmarkAngle); 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"); double* currentHueMin = &GreenHueMin; double* currentHueMax = &GreenHueMax; double* currentSatMin = &GreenSatMin; double* currentSatMax = &GreenSatMax; double* currentValMin = &GreenValMin; double* currentValMax = &GreenValMax; double* currentValFactorMax = &GreenValFactorMax; double* currentValFactorMin = &GreenValFactorMin; // 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 */ cvNamedWindow( wndname, CV_WINDOW_AUTOSIZE ); cvNamedWindow( wndname2, CV_WINDOW_AUTOSIZE ); cvNamedWindow( wndname3, 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; } // getImageFromFile = true; // added this to work on files when no camera present // 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; //} } // 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 processRectangle(undis_frame,undis_frame_painted); /* if (detectRed == true) { processRed(undis_frame,undis_frame_painted); } if (detectGreen == true) { processGreen(undis_frame,undis_frame_painted); } if (detectPurple == true) { processPurple(undis_frame,undis_frame_painted); } */ /* * 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; GreenGMaxBy += 1; } else if (keyCode == (int)'-') { // if (blueValue > 4) blueValue -= 5; GreenGMaxBy -= 1; } else if (keyCode == (int)'+') { GreenGMax += 5; } else if (keyCode == (int)'_') { GreenGMax -= 5; } else if (keyCode == (int)'1') { if(*currentHueMax - .01 >= -.5) *currentHueMax -= .01; } else if (keyCode == (int)'2') { if(*currentHueMax + .01 <= 1.5) *currentHueMax += .01; } else if (keyCode == (int)'3') { if(*currentSatMax - .01 >= 0) *currentSatMax -= .01; } else if (keyCode == (int)'4') { if(*currentSatMax + .01 <= 1.0) *currentSatMax += .01; } else if (keyCode == (int)'5') { if(*currentValMax - .01 >= 0) *currentValMax -= .01; } else if (keyCode == (int)'6') { if(*currentValMax + .01 <= 1.0) *currentValMax += .01; } else if (keyCode == (int)'!') { if(*currentHueMin - .01 >= -.5) *currentHueMin -= .01; } else if (keyCode == (int)'@') { if(*currentHueMin + .01 <= 1.5) *currentHueMin += .01; } else if (keyCode == (int)'#') { if(*currentSatMin - .01 >= 0) *currentSatMin -= .01; } else if (keyCode == (int)'$') { if(*currentSatMin + .01 <= 1.0) *currentSatMin += .01; } else if (keyCode == (int)'%') { if(*currentValMin - .01 >= 0) *currentValMin -= .01; } else if (keyCode == (int)'^') { if(*currentValMin + .01 <= 1.0) *currentValMin += .01; } else if (keyCode == (int)'0'){ if(*currentValFactorMax +.1 <=3.0) *currentValFactorMax +=.1; } else if (keyCode == (int)'9'){ if(*currentValFactorMax -.1 >=0.0) *currentValFactorMax -=.1; } else if (keyCode == (int)')'){ if(*currentValFactorMin +.1 <=1.0) *currentValFactorMin +=.1; } else if (keyCode == (int)'('){ if(*currentValFactorMin -.1 >=0.0) *currentValFactorMin -=.1; } else if (keyCode == (int)'7'){ if(RedRMin - 5 >= 0) RedRMin -=5; } else if (keyCode == (int)'8'){ if(RedRMin + 5 <= 255) RedRMin +=5; } else if (keyCode == (int)'p') { cout << "*currentHueMax: " << *currentHueMax << " *currentHueMin: " << *currentHueMin << endl; cout << "*currentSatMax: " << *currentSatMax << " *currentSatMin: " << *currentSatMin << endl; cout << "*currentValMax: " << *currentValMax << " *currentValMin: " << *currentValMin << endl; cout << "*currentValFactorMax: " << *currentValFactorMax << "*currentValFactorMin" << *currentValFactorMin << endl; cout << "GreenGMaxBy: " << GreenGMaxBy << endl; cout << "GreenGMax: " << GreenGMax << endl; } else if (keyCode == (int)'r') { // print out the rectangle stats once inside of the processRect func toggle = true; } /* * 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)'z') { detectRed = !detectRed; currentHueMin = &RedHueMin; currentHueMax = &RedHueMax; currentSatMin = &RedSatMin; currentSatMax = &RedSatMax; currentValMin = &RedValMin; currentValMax = &RedValMax; currentValFactorMax = &RedValFactorMax; currentValFactorMin = &RedValFactorMin; //cout << "red mode" << endl; } else if (keyCode == (int)'x') { detectGreen = !detectGreen; currentHueMin = &GreenHueMin; currentHueMax = &GreenHueMax; currentSatMin = &GreenSatMin; currentSatMax = &GreenSatMax; currentValMin = &GreenValMin; currentValMax = &GreenValMax; currentValFactorMax = &GreenValFactorMax; currentValFactorMin = &GreenValFactorMin; //cout << "green mode" << endl; } else if (keyCode == (int)'c') { detectPurple = !detectPurple; currentHueMin = &PurpleHueMin; currentHueMax = &PurpleHueMax; currentSatMin = &PurpleSatMin; currentSatMax = &PurpleSatMax; currentValMin = &PurpleValMin; currentValMax = &PurpleValMax; currentValFactorMax = &PurpleValFactorMax; currentValFactorMin = &PurpleValFactorMin; //cout << "purple/blue mode" << endl; } /* * the spacebar advances the image in the VideoBMPs directory */ else if (keyCode == (int)' ') { if (showing_savedImage == true) { toggle = 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); cout << "image " << i << endl; // 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; } 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 // 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; *s = delta/255.0; 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; } // 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 processGreen(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" uchar* painted2; // 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 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); int matrix[250][250]; assert(xhi - xlow < 250 && yhi - ylow < 250); /*for(int i = 0; i < 25; i++) redTop[i] = redBottom[i] = -2;*/ int y, realy; /*int ** matrix; matrix= new int*[yhi-ylow];//[xhi-xlow]; for(int j = 0; j < yhi-ylow; j++) matrix[j] = new int[xhi-xlow];*/ for (int x = xlow; x RedHueMin && sat < RedSatMax && sat > RedSatMin && val < RedValMax && val > RedValMin) // set hue, and make saturation allowable within a range of the value. if(g>r && g>b && g > GreenGMin && hue < GreenHueMax && hue < GreenHueMax && sat > GreenSatMin && sat < GreenSatMax && val > GreenValMin && val < GreenValMax /*&& sat < val*(GreenValFactorMax) && sat > val*(GreenValFactorMin)*/ ) { painted[0] = 0; // sets blue to 0 painted[1] = 255; // sets green to 0 painted[2] = 0; // sets red to 255 //cout << "y = " << y << "\t x = " << x << endl; //cout << "r = " << y-ylow << "\t x-xlow" << x-xlow << endl; matrix[y-ylow][x-xlow]=1; //cout << "matrix has: " << matrix[y-ylow][x-xlow] << endl; } else { matrix[y-ylow][x-xlow]=0; } } } } void processPurple(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" uchar* painted2; // 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 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); int matrix[250][250]; assert(xhi - xlow < 250 && yhi - ylow < 250); /*for(int i = 0; i < 25; i++) redTop[i] = redBottom[i] = -2;*/ int y, realy; /*int ** matrix; matrix= new int*[yhi-ylow];//[xhi-xlow]; for(int j = 0; j < yhi-ylow; j++) matrix[j] = new int[xhi-xlow];*/ for (int x = xlow; x RedHueMin && sat < RedSatMax && sat > RedSatMin && val < RedValMax && val > RedValMin) // set hue, and make saturation allowable within a range of the value. if((br && b+10>g && hue < PurpleHueMax && hue > PurpleHueMin && sat > PurpleSatMin && /*sat < val*(PurpleValFactorMax) && sat > val*(PurpleValFactorMin) && */val < PurpleValMax && val > PurpleValMin) { painted[0] = 255; // sets blue to 0 painted[1] = 0; // sets green to 0 painted[2] = 0; // sets red to 255 //cout << "y = " << y << "\t x = " << x << endl; //cout << "r = " << y-ylow << "\t x-xlow" << x-xlow << endl; matrix[y-ylow][x-xlow]=1; //cout << "matrix has: " << matrix[y-ylow][x-xlow] << endl; } else { matrix[y-ylow][x-xlow]=0; } } } } // 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" uchar* painted2; // 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 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); int matrix[250][250]; assert(xhi - xlow < 250 && yhi - ylow < 250); int colWidth = 10; int sampleCols = (xhi-xlow)/colWidth; int redTop[25]; int redBottom[25]; int redMiddle[25]; /*for(int i = 0; i < 25; i++) redTop[i] = redBottom[i] = -2;*/ bool foundTop; bool foundBottom; int temp; int temp2; int count; int i; int j; int z; int y, realy; /*int ** matrix; matrix= new int*[yhi-ylow];//[xhi-xlow]; for(int j = 0; j < yhi-ylow; j++) matrix[j] = new int[xhi-xlow];*/ for (int x = xlow; x RedHueMin && sat < RedSatMax && sat > RedSatMin && val < RedValMax && val > RedValMin) // set hue, and make saturation allowable within a range of the value. if(/*r > RedRMin*/r>g && r>b && hue < RedHueMax && hue > RedHueMin && sat > RedSatMin && sat < val*(RedValFactorMax) && sat > val*(RedValFactorMin) && val < RedValMax && val > RedValMin) { painted[0] = 0; // sets blue to 0 painted[1] = 0; // sets green to 0 painted[2] = blueValue; // sets red to 255 //cout << "y = " << y << "\t x = " << x << endl; //cout << "r = " << y-ylow << "\t x-xlow" << x-xlow << endl; matrix[y-ylow][x-xlow]=1; //cout << "matrix has: " << matrix[y-ylow][x-xlow] << endl; } else { matrix[y-ylow][x-xlow]=0; } } } // look through sample columns, trying to find the top yValue of a dense red region, and // then yValue for the end of that dense region. for(i = 0; i < xhi-xlow; i+=colWidth) // col (x's) { foundTop = false; foundBottom = false; for(j = 0; j < yhi-ylow; j++) // row (y's) { if(!foundTop && matrix[j][i] != 0) // haven't found a top yet and this is red { count = 0; // a count of how many of the next 5 pixels are red for(z = j+1; z <= j+5 && z=4) { foundTop = true; redTop[i/colWidth] = j; // stores the y value of the "top" of the red } } // looking for a bottom and this is red else if(foundTop && !foundBottom && matrix[j][i]!=0) { count = 0; // a count of how many of the next 5 pixels are NOT red for(z = j+1; z <= j+5 && z=1 && (yVal - yValOld > 25 || yVal - yValOld < -25)) // this is an outlier break; // ignore it in the stats sumX += xVal; sumY += yVal; sumXY += xVal*yVal; sumXSquared += xVal*xVal; yValOld = yVal; n++; } } // using the equation listed on (http://cne.gmu.edu/modules/dau/stat/regression/linregsn/nreg_4_frm.html) bVal = (sumXY - (sumX * sumY)/double(n)) / double(sumXSquared - (sumX * sumX)/double(n)); aVal = sumY/double(n) - bVal * sumX/double(n); CvPoint linePt1, linePt2; // don't fit a line if there are less than 3 valid points if(n >= 3) { foundRed = true; linePt1.x = xlow; linePt1.y = int(aVal + bVal * xlow); linePt2.x = xhi; linePt2.y = int(aVal + bVal * xhi); // fix video insanity if (getImageAgainFromFile || getImageFromFile) { linePt1.y = imageSize.height - linePt1.y; // reverse if static image linePt2.y = imageSize.height - linePt2.y; // reverse if static image } cvLine(f_p, linePt1, linePt2,CV_RGB(0,255,0),1,8); // 1 == thickness, 8 = connectedness } else foundRed = false; // this is just to see the process (top, middle, and bottom pixels turned green) for(int e = 0; er + GreenGMaxBy && g>b + GreenGMaxBy && g > GreenGMin && g < GreenGMax && hue > GreenHueMin && hue < GreenHueMax && sat > GreenSatMin && sat < GreenSatMax && val > GreenValMin && val < GreenValMax /*&& sat < val*(GreenValFactorMax) && sat > val*(GreenValFactorMin)*/ ); } // process the image to find a rectangle landmark and determine its location relative to the camera void processRectangle(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" uchar* painted2; // 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... // This loops through the pixels in the rectangle specified // by pt4 and pt5 (see setGlobals for their corners) // 320x240 int xlow = 1; int xhi = imageSize.width -1; int ylow = 1; int yhi = imageSize.height-1; /* int xlow = 50; int xhi = 100; int ylow = 50; int yhi = 100; 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); */ /* if(detectGreen) { cout << "xlow: " << xlow << endl; cout << "ylow: " << ylow << endl; cout << "xhi: " << xhi << endl; cout << "yhi: " << yhi << endl; } */ int** matrix = new int*[yhi-ylow]; for(int y = 0; y < yhi-ylow; ++y) { matrix[y] = new int[xhi-xlow]; } int realy; for (int x = xlow; x RedHueMin && sat < RedSatMax && sat > RedSatMin && val < RedValMax && val > RedValMin) // set hue, and make saturation allowable within a range of the value. if(isRectColor(r,g,b,hue,sat,val)) { /* painted[0] = 0; // sets blue to 0 painted[1] = 255; // sets green to 0 painted[2] = 0; // sets red to 255 */ //cout << "y = " << y << "\t x = " << x << endl; //cout << "r = " << y-ylow << "\t x-xlow" << x-xlow << endl; matrix[y-ylow][x-xlow]=1; //cout << "matrix has: " << matrix[y-ylow][x-xlow] << endl; } else { matrix[y-ylow][x-xlow]=0; } } } /* if(detectGreen) { for(int r = 0; r < yhi - ylow; r++) { cout << "r: " << r < bestSize) { bestSize = currentSize; bestLabel = currentLabel; } ++currentLabel; } } } // now we can focus on just the label for the rectangle and get the height of it // we'll count the number of rows which have at least 10 pixels of green in a row int rowCount = 0; //int** temp2 = matrix; for(int r = 0; r < yhi-ylow; ++r) { int greenCount = 0; for(int c = 0; c < xhi-xlow; ++c) { /* if(detectGreen) { cout << "c: " << c << "\t" << matrix[r][c] << endl; } */ bool counted = false; if(matrix[r][c] == bestLabel) { // cout << "up green" << endl; ++greenCount; } else { greenCount = 0; } /* if(detectGreen) { cout << "greenCount: " << greenCount; } */ if(greenCount == 20 && !counted) { ++rowCount; counted = true; } } } POINT center = centroid(matrix, bestLabel, yhi-ylow, xhi-xlow); CvPoint preal; preal.x = center.x + xlow; preal.y = center.y + ylow; if (getImageAgainFromFile || getImageFromFile) { preal.y = imageSize.height - preal.y; // reverse if static image } cvCircle( f_p, preal, 4, CV_RGB(255,0,0), 3 ); if(rowCount < 20) // too small to count (most likely not a rectangle { seeLandmark = false; } else { seeLandmark = true; // visual cue of see landmark cvCircle( f_p, pt4, 10, CV_RGB(255,0,0), /*thickness*/ 1 ); // later try to get 2 rect edges landmarkDist = pixelHeightToDistance(rowCount); landmarkAngle = calculateAngle(center.x); if(toggle) // only display this once per static image { cout << "rowcount: " << rowCount << endl; cout << "dist: " << landmarkDist << endl; cout << "angle: " << landmarkAngle << endl; toggle = false; } } //else } for (int x = xlow; x0 && r+i < rows && c+j > 0 && c+j < cols) dst[r+i][c+j] = 0; } } } } void erode3(int** dst, int** src, int rows, int cols) { assert(dst != src); for(int r = 0; r < rows; ++r) for(int c = 0; c < cols; ++c) dst[r][c] = 1; for(int r = 0; r < rows; ++r) { for(int c = 0; c < cols; ++c) { if(src[r][c] == 0) { for(int i = -3; i <= 3; ++i) for(int j = -3; j <= 3; ++j) if(r+i >0 && r+i < rows && c+j > 0 && c+j < cols) dst[r+i][c+j] = 0; } } } } void dilate(int** dst, int** src, int rows, int cols) { assert(dst != src); for(int r = 0; r < rows; ++r) for(int c = 0; c < cols; ++c) dst[r][c] = 0; for(int r = 0; r < rows; ++r) { for(int c = 0; c < cols; ++c) { if(src[r][c] == 1) { for(int i = -1; i <= 1; ++i) for(int j = -1; j <= 1; ++j) if(r+i >0 && r+i < rows && c+j > 0 && c+j < cols) dst[r+i][c+j] = 1; } } } } POINT centroid(int** matrix, int value, int rows, int cols) { double rsum = 0.0; double csum = 0.0; int num = 0; POINT result; for(int r = 0; r < rows; ++r) for(int c = 0; c < cols; ++c) if(matrix[r][c] == value) { rsum += r; csum += c; ++num; } if(num != 0) { result.y = int(rsum / num); result.x = int(csum / num); } else { result.y = result.x = -1; } return result; } int connectedComponents(int** matrix, int startR, int startC, int oldVal, int newVal, int rows, int cols) { // using x,y as r,c POINT p; POINT temp; p.x = startR; p.y = startC; int count = 0; queue q; // if the start point doesn't have oldVal, then the connected component is trivial (ie nothing) if(matrix[p.x][p.y] == oldVal) q.push(p); while(!q.empty()) { p = q.front(); q.pop(); for(int i = -1; i <= 1; ++i) for(int j = -1; j <= 1; ++j) { temp.x = p.x + i; temp.y = p.y + j; if(temp.x > 0 && temp.x < rows && temp.y > 0 && temp.y < cols) if(matrix[temp.x][temp.y] == oldVal) { matrix[temp.x][temp.y] = newVal; q.push(temp); ++count; } } } return count; } 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; }