Salome HOME
Merge from V6_main_20120808 08Aug12
[modules/geom.git] / src / ShapeRecognition / ShapeRec_FeatureDetector.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File   : ShapeRec_FeatureDetector.cxx
24 // Author : Renaud NEDELEC, Open CASCADE S.A.S.
25
26 #include "ShapeRec_FeatureDetector.hxx"
27 #include <stdio.h>
28 #include "utilities.h"
29
30 using namespace cv;
31
32 //TODO : All the following methods but ComputeContours use the C API of OpenCV while ComputContours
33 // uses the C++ API of the library.
34 // This should be homogenized and preferably by using the C++ API (which is more recent for all the methods
35
36 // The code has to be "cleaned up" too
37
38 /*!
39   Constructor
40   \param theFilename - image to process
41 */
42 ShapeRec_FeatureDetector::ShapeRec_FeatureDetector(): 
43   corners()
44 {
45   cornerCount = 2000;
46   rect=cvRect(0,0,0,0);
47   imagePath = ""; //theFilename;
48   // Store the dimensions of the picture
49   imgHeight = 0;
50   imgWidth  = 0;
51 }
52
53 /*!
54   Sets the path of the image file to be processed
55   \param thePath - Location of the image file 
56 */
57 void ShapeRec_FeatureDetector::SetPath( const std::string& thePath )
58 {
59   imagePath = thePath; 
60   if (imagePath != "")
61   {
62     IplImage* src = cvLoadImage(imagePath.c_str(),CV_LOAD_IMAGE_COLOR);
63     imgHeight = src->height;
64     imgWidth = src->width; 
65   }
66 }
67
68 /*!
69   Computes the corners of the image located at imagePath
70 */
71 void ShapeRec_FeatureDetector::ComputeCorners(){
72   
73   // Parameters for the corner detection
74   double qualityLevel = 0.2;
75   double minDistance = 1;
76  
77   // Images to be used for detection
78   IplImage *eig_img, *temp_img, *src_img_gray;
79   
80   // Load image
81   src_img_gray = cvLoadImage (imagePath.c_str(), CV_LOAD_IMAGE_GRAYSCALE);
82   
83   if ( rect.width > 1 )
84   {
85     // If a ROI as been set use it for detection
86     cvSetImageROI( src_img_gray, rect );
87   }
88   
89   eig_img = cvCreateImage (cvGetSize (src_img_gray), IPL_DEPTH_32F, 1);
90   temp_img = cvCreateImage (cvGetSize (src_img_gray), IPL_DEPTH_32F, 1);
91   corners = (CvPoint2D32f *) cvAlloc (cornerCount * sizeof (CvPoint2D32f));
92   
93   // image height and width
94   imgHeight = src_img_gray->height;
95   imgWidth  = src_img_gray->width;
96
97   // Corner detection using cvCornerMinEigenVal 
98   // (one of the methods available inOpenCV, there is also a cvConerHarris method that can be used by setting a flag in cvGoodFeaturesToTrack)
99   cvGoodFeaturesToTrack (src_img_gray, eig_img, temp_img, corners, &cornerCount, /*quality-level=*/qualityLevel, /*min-distance=*/minDistance);
100   cvFindCornerSubPix (src_img_gray, corners, cornerCount,
101                     cvSize (3, 3), cvSize (-1, -1), cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03));
102
103   cvReleaseImage (&eig_img);
104   cvReleaseImage (&temp_img);
105   cvReleaseImage (&src_img_gray);
106
107 }
108
109 /*!
110   Computes the contours of the image located at imagePath
111 */
112 bool ShapeRec_FeatureDetector::ComputeContours( int detection_method ){
113  
114   // Initialising images
115   Mat src, src_gray;
116   Mat detected_edges;
117   
118   // Read image
119   src = imread( imagePath.c_str() );
120   if( !src.data )
121     return false; 
122   
123   if ( detection_method == CANNY )   // The problem is that with that filter the detector detects double contours
124   {   
125     // Thresholds for Canny detector
126     int lowThreshold = 100;
127     int ratio = 3;
128     int kernel_size = 3; // 3,5 or 7
129     
130     // Convert the image to grayscale
131     if (src.channels() == 3)
132       cvtColor( src, src_gray, CV_BGR2GRAY );
133     else if (src.channels() == 1)
134       src_gray = src;
135   
136     // Reduce noise with a kernel 3x3               
137     blur( src_gray, detected_edges, Size(3,3) );
138     // Canny detector
139     Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size, /*L2gradient =*/true );      
140   }
141   else if ( detection_method == COLORFILTER )
142   {
143     if ( !rect.width > 1 )
144       return false;
145     detected_edges = _colorFiltering();
146   }
147   else if ( detection_method == RIDGE_DETECTOR )  // Method adapted for engineering drawings (e.g. watershed functionnality could be used here cf.OpenCV documentation and samples)
148   {
149     // TODO
150     return false;
151   }
152   _detectAndRetrieveContours( detected_edges );
153   
154   return true;
155   
156 }
157
158 /*!
159   Computes the lines in the image located at imagePath
160 */
161 bool ShapeRec_FeatureDetector::ComputeLines(){
162   MESSAGE("ShapeRec_FeatureDetector::ComputeLines()")
163   // Initialising images
164   Mat src, src_gray, detected_edges, dst;
165   
166   src=imread(imagePath.c_str(), 0);
167   
168   Canny( src, dst, 50, 200, 3 );
169   HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
170   return true;
171   
172 }
173
174 /*!
175   Stores a region of interest given by user in rect
176   \param theRect - Region Of Interest of the image located at imagePath 
177 */
178 void ShapeRec_FeatureDetector::SetROI( const QRect& theRect )
179 {
180   if (!theRect.isEmpty()){
181     rect = cvRect(theRect.x(),theRect.y(),theRect.width(),theRect.height());
182   }
183 }
184
185 /*!
186   Crops the image located at imagePath to the region of interest given by the user via SetROI
187   and stores the result in /tmp
188   \param theRect - Region Of Interest of the image located at imagePath 
189 */
190 std::string ShapeRec_FeatureDetector::CroppImage()
191 {
192   IplImage* src = cvLoadImage(imagePath.c_str(),CV_LOAD_IMAGE_COLOR);
193  
194   cvSetImageROI(src, rect);
195   IplImage* cropped_image = cvCreateImage(cvGetSize(src),
196                                           src->depth,
197                                           src->nChannels);
198   cvCopy(src, cropped_image, NULL);
199   cvResetImageROI(src);
200   
201   cvSaveImage ("/tmp/cropped_image.bmp", cropped_image);
202   
203   return "/tmp/cropped_image.bmp";
204 }
205
206
207 /*!
208   Performs contours detection and store them in contours 
209   \param binaryImg - src image to find contours of 
210 */
211 void ShapeRec_FeatureDetector::_detectAndRetrieveContours( Mat binaryImg )
212 {
213   binaryImg = binaryImg > 1; 
214   int method = CV_CHAIN_APPROX_NONE;
215   findContours( binaryImg, contours, hierarchy,CV_RETR_CCOMP, method);
216   // Other possible approximations CV_CHAIN_APPROX_TC89_KCOS, CV_CHAIN_APPROX_TC89_L1, CV_CHAIN_APPROX_SIMPLE cf. OpenCV documentation 
217   // for precise information
218 }
219
220 /*!
221   Performs color filtering from the image sample contained in the ROI rect of the image 
222   located at imagePath
223   Thresholds the result in order ot obtain a binary image
224   \return binary image resulting from filtering and thersholding
225 */
226 Mat ShapeRec_FeatureDetector::_colorFiltering()
227 {  
228   IplImage* find_image = cvLoadImage(imagePath.c_str(),CV_LOAD_IMAGE_COLOR);
229   // Reduce noise with a kernel 3x3               
230   cvSmooth( find_image, find_image, CV_GAUSSIAN, 3, 3 );
231   
232   if ( !rect.width > 1 )
233     return Mat(find_image);
234   
235   // Crop the image to build an histogram from the selected part
236   cvSetImageROI(find_image, rect);
237   IplImage* test_image = cvCreateImage(cvGetSize(find_image),
238                                        find_image->depth,
239                                        find_image->nChannels);
240   cvCopy(find_image, test_image, NULL);
241   cvResetImageROI(find_image);
242   
243   IplImage* test_hsv = cvCreateImage(cvGetSize(test_image),8,3);
244   IplImage* h_plane = cvCreateImage( cvGetSize(test_image), 8, 1 );
245   IplImage* s_plane = cvCreateImage( cvGetSize(test_image), 8, 1 );
246   CvHistogram* hist;
247
248   cvCvtColor(test_image, test_hsv, CV_BGR2HSV);
249   
250   cvCvtPixToPlane(test_hsv, h_plane, s_plane, 0, 0);
251   IplImage* planes[] = { h_plane, s_plane };
252   
253   //create hist
254   int hbins = 30, sbins = 32;                        // TODO think to the best values here
255   int   hist_size[] = { hbins, sbins };
256   float hranges[] = { 0, 180 };
257   float sranges[] = { 0, 255 };
258   float* ranges[] = { hranges, sranges };
259   hist = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);
260   
261   //calculate hue /saturation histogram
262   cvCalcHist(planes, hist, 0 ,0);
263
264 //   // TEST print of the histogram for debugging
265 //   IplImage* hist_image = cvCreateImage(cvSize(320,300),8,3);
266 //   
267 //   //draw hist on hist_test image.
268 //   cvZero(hist_image);
269 //   float max_value = 0;
270 //   cvGetMinMaxHistValue(hist, 0 , &max_value, 0, 0);
271 //   int bin_w = hist_image->width/size_hist;
272 //   for(int i = 0; i < size_hist; i++ )
273 //   {
274 //     //prevent overflow
275 //     int val = cvRound( cvGetReal1D(hist->bins,i)*hist_image->
276 //     height/max_value);
277 //     CvScalar color = CV_RGB(200,0,0);
278 //     //hsv2rgb(i*180.f/size_hist);
279 //     cvRectangle( hist_image, cvPoint(i*bin_w,hist_image->height),
280 //     cvPoint((i+1)*bin_w,hist_image->height - val),
281 //     color, -1, 8, 0 );
282 //   }
283 //  
284 //    
285 //   cvNamedWindow("hist", 1); cvShowImage("hist",hist_image);
286   
287   
288   //calculate back projection of hue and saturation planes of input image
289   IplImage* backproject = cvCreateImage(cvGetSize(find_image), 8, 1);
290   IplImage* binary_backproject = cvCreateImage(cvGetSize(find_image), 8, 1);
291   IplImage* find_hsv = cvCreateImage(cvGetSize(find_image),8,3);
292   IplImage* find_hplane = cvCreateImage(cvGetSize(find_image),8,1);
293   IplImage* find_splane = cvCreateImage(cvGetSize(find_image),8,1);
294   
295   cvCvtColor(find_image, find_hsv, CV_BGR2HSV);
296   cvCvtPixToPlane(find_hsv, find_hplane, find_splane, 0, 0);
297   IplImage* find_planes[] = { find_hplane, find_splane };
298   cvCalcBackProject(find_planes, backproject, hist);
299   
300   // Threshold in order to obtain binary image
301   cvThreshold(backproject, binary_backproject, 1, 255, CV_THRESH_BINARY);  // NOTE it would be good to think about the best threshold to use (it's 1 for now)
302   cvReleaseImage(&test_image);
303   cvReleaseImage(&test_hsv);
304   cvReleaseImage(&h_plane);
305   cvReleaseImage(&s_plane);
306   cvReleaseImage(&find_image);
307   cvReleaseImage(&find_hsv);
308   cvReleaseImage(&find_hplane);
309   cvReleaseImage(&find_splane);
310   cvReleaseImage(&backproject);
311   
312   return Mat(binary_backproject);
313 }