$search
00001 /*************************************************************************** 00002 * FastMatchTemplate.cpp 00003 * 00004 * 00005 * Copyright 2010 Tristen Georgiou 00006 * tristen_georgiou@hotmail.com 00007 ****************************************************************************/ 00008 00009 /* 00010 * This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU General Public License 00021 * along with this program; if not, write to the Free Software 00022 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00023 */ 00024 00025 #include <math.h> 00026 #include "fast_match_template/FastMatchTemplate.h" 00027 00028 //============================================================================= 00029 // Assumes that source image exists and numDownPyrs > 1, no ROIs for either 00030 // image, and both images have the same depth and number of channels 00031 bool 00032 FastMatchTemplate( const Mat& source, 00033 const Mat& target, 00034 vector<Point>* foundPointsList, 00035 vector<double>* confidencesList, 00036 int matchPercentage, 00037 bool findMultipleTargets, 00038 int numMaxima, 00039 int numDownPyrs, 00040 int searchExpansion ) 00041 { 00042 // make sure that the template image is smaller than the source 00043 if(target.size().width > source.size().width || 00044 target.size().height > source.size().height) 00045 { 00046 printf( "\nSource image must be larger than target image.\n" ); 00047 return false; 00048 } 00049 00050 if(source.depth() != target.depth()) 00051 { 00052 printf( "\nSource image and target image must have same depth.\n" ); 00053 return false; 00054 } 00055 00056 if(source.channels() != target.channels()) 00057 { 00058 printf("\nSource image and target image must have same number of channels.\n" ); 00059 return false; 00060 } 00061 00062 Size sourceSize = source.size(); 00063 Size targetSize = target.size(); 00064 00065 // create copies of the images to modify 00066 Mat copyOfSource = source.clone(); 00067 Mat copyOfTarget = target.clone(); 00068 00069 // down pyramid the images 00070 for(int ii = 0; ii < numDownPyrs; ii++) 00071 { 00072 // start with the source image 00073 sourceSize.width = (sourceSize.width + 1) / 2; 00074 sourceSize.height = (sourceSize.height + 1) / 2; 00075 00076 Mat smallSource(sourceSize, source.type()); 00077 pyrDown(copyOfSource, smallSource); 00078 00079 // prepare for next loop, if any 00080 copyOfSource = smallSource.clone(); 00081 00082 // next, do the target 00083 targetSize.width = (targetSize.width + 1) / 2; 00084 targetSize.height = (targetSize.height + 1) / 2; 00085 00086 Mat smallTarget(targetSize, target.type()); 00087 pyrDown(copyOfTarget, smallTarget); 00088 00089 // prepare for next loop, if any 00090 copyOfTarget = smallTarget.clone(); 00091 } 00092 00093 // perform the match on the shrunken images 00094 Size smallTargetSize = copyOfTarget.size(); 00095 Size smallSourceSize = copyOfSource.size(); 00096 00097 Size resultSize; 00098 resultSize.width = smallSourceSize.width - smallTargetSize.width + 1; 00099 resultSize.height = smallSourceSize.height - smallTargetSize.height + 1; 00100 00101 Mat result(resultSize, CV_32FC1); 00102 matchTemplate(copyOfSource, copyOfTarget, result, CV_TM_CCOEFF_NORMED); 00103 00104 // find the top match locations 00105 Point* locations = NULL; 00106 MultipleMaxLoc(result, &locations, numMaxima); 00107 00108 // search the large images at the returned locations 00109 sourceSize = source.size(); 00110 targetSize = target.size(); 00111 00112 // create a copy of the source in order to adjust its ROI for searching 00113 for(int currMax = 0; currMax < numMaxima; currMax++) 00114 { 00115 // transform the point to its corresponding point in the larger image 00116 locations[currMax].x *= (int)pow(2.0f, numDownPyrs); 00117 locations[currMax].y *= (int)pow(2.0f, numDownPyrs); 00118 locations[currMax].x += targetSize.width / 2; 00119 locations[currMax].y += targetSize.height / 2; 00120 00121 const Point& searchPoint = locations[currMax]; 00122 00123 // if we are searching for multiple targets and we have found a target or 00124 // multiple targets, we don't want to search in the same location(s) again 00125 if(findMultipleTargets && !foundPointsList->empty()) 00126 { 00127 bool thisTargetFound = false; 00128 00129 int numPoints = foundPointsList->size(); 00130 for(int currPoint = 0; currPoint < numPoints; currPoint++) 00131 { 00132 const Point& foundPoint = (*foundPointsList)[currPoint]; 00133 if(abs(searchPoint.x - foundPoint.x) <= searchExpansion * 2 && 00134 abs(searchPoint.y - foundPoint.y) <= searchExpansion * 2) 00135 { 00136 thisTargetFound = true; 00137 break; 00138 } 00139 } 00140 00141 // if the current target has been found, continue onto the next point 00142 if(thisTargetFound) 00143 { 00144 continue; 00145 } 00146 } 00147 00148 // set the source image's ROI to slightly larger than the target image, 00149 // centred at the current point 00150 Rect searchRoi; 00151 searchRoi.x = searchPoint.x - (target.size().width) / 2 - searchExpansion; 00152 searchRoi.y = searchPoint.y - (target.size().height) / 2 - searchExpansion; 00153 searchRoi.width = target.size().width + searchExpansion * 2; 00154 searchRoi.height = target.size().height + searchExpansion * 2; 00155 00156 // make sure ROI doesn't extend outside of image 00157 if(searchRoi.x < 0) 00158 { 00159 searchRoi.x = 0; 00160 } 00161 00162 if(searchRoi.y < 0) 00163 { 00164 searchRoi.y = 0; 00165 } 00166 00167 if((searchRoi.x + searchRoi.width) > (sourceSize.width - 1)) 00168 { 00169 int numPixelsOver 00170 = (searchRoi.x + searchRoi.width) - (sourceSize.width - 1); 00171 00172 searchRoi.width -= numPixelsOver; 00173 } 00174 00175 if((searchRoi.y + searchRoi.height) > (sourceSize.height - 1)) 00176 { 00177 int numPixelsOver 00178 = (searchRoi.y + searchRoi.height) - (sourceSize.height - 1); 00179 00180 searchRoi.height -= numPixelsOver; 00181 } 00182 00183 Mat searchImage = Mat(source, searchRoi); 00184 00185 // perform the search on the large images 00186 resultSize.width = searchRoi.width - target.size().width + 1; 00187 resultSize.height = searchRoi.height - target.size().height + 1; 00188 00189 result = Mat(resultSize, CV_32FC1); 00190 matchTemplate(searchImage, target, result, CV_TM_CCOEFF_NORMED); 00191 00192 // find the best match location 00193 double minValue, maxValue; 00194 Point minLoc, maxLoc; 00195 minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc); 00196 maxValue *= 100; 00197 00198 // transform point back to original image 00199 maxLoc.x += searchRoi.x + target.size().width / 2; 00200 maxLoc.y += searchRoi.y + target.size().height / 2; 00201 00202 if(maxValue >= matchPercentage) 00203 { 00204 // add the point to the list 00205 foundPointsList->push_back(maxLoc); 00206 confidencesList->push_back(maxValue); 00207 00208 // if we are only looking for a single target, we have found it, so we 00209 // can return 00210 if(!findMultipleTargets) 00211 { 00212 break; 00213 } 00214 } 00215 } 00216 00217 if(foundPointsList->empty()) 00218 { 00219 printf( "\nTarget was not found to required confidence of %d.\n", 00220 matchPercentage ); 00221 } 00222 00223 delete [] locations; 00224 00225 return true; 00226 } 00227 00228 //============================================================================= 00229 00230 void MultipleMaxLoc(const Mat& image, 00231 Point** locations, 00232 int numMaxima) 00233 { 00234 // initialize input variable locations 00235 *locations = new Point[numMaxima]; 00236 00237 // create array for tracking maxima 00238 float* maxima = new float[numMaxima]; 00239 for(int i = 0; i < numMaxima; i++) 00240 { 00241 maxima[i] = 0.0; 00242 } 00243 00244 Size size = image.size(); 00245 00246 // extract the raw data for analysis 00247 for(int y = 0; y < size.height; y++) 00248 { 00249 for(int x = 0; x < size.width; x++) 00250 { 00251 float data = image.at<float>(y, x); 00252 00253 // insert the data value into the array if it is greater than any of the 00254 // other array values, and bump the other values below it, down 00255 for(int j = 0; j < numMaxima; j++) 00256 { 00257 // require at least 50% confidence on the sub-sampled image 00258 // in order to make this as fast as possible 00259 if(data > 0.5 && data > maxima[j]) 00260 { 00261 // move the maxima down 00262 for(int k = numMaxima - 1; k > j; k--) 00263 { 00264 maxima[k] = maxima[k-1]; 00265 (*locations)[k] = ( *locations )[k-1]; 00266 } 00267 00268 // insert the value 00269 maxima[j] = data; 00270 (*locations)[j].x = x; 00271 (*locations)[j].y = y; 00272 break; 00273 } 00274 } 00275 } 00276 } 00277 00278 delete [] maxima; 00279 } 00280 00281 //============================================================================= 00282 00283 void 00284 DrawFoundTargets(Mat* image, 00285 const Size& size, 00286 const vector<Point>& pointsList, 00287 const vector<double>& confidencesList, 00288 int red, 00289 int green, 00290 int blue) 00291 { 00292 int numPoints = pointsList.size(); 00293 for(int currPoint = 0; currPoint < numPoints; currPoint++) 00294 { 00295 const Point& point = pointsList[currPoint]; 00296 00297 // write the confidences to stdout 00298 printf("\nTarget found at (%d, %d), with confidence = %3.3f %%.\n", 00299 point.x, 00300 point.y, 00301 confidencesList[currPoint]); 00302 00303 // draw a circle at the center 00304 circle(*image, point, 2, CV_RGB(red, green, blue)); 00305 00306 // draw a rectangle around the found target 00307 Point topLeft; 00308 topLeft.x = point.x - size.width / 2; 00309 topLeft.y = point.y - size.height / 2; 00310 00311 Point bottomRight; 00312 bottomRight.x = point.x + size.width / 2; 00313 bottomRight.y = point.y + size.height / 2; 00314 00315 rectangle(*image, topLeft, bottomRight, CV_RGB(red, green, blue)); 00316 } 00317 }