Salome HOME
367e73ceeca4003089e6df58d14bbab9d74acae3
[modules/geom.git] / src / ShapeRecognition / ShapeRec_FeatureDetector.cxx
1 // Copyright (C) 2007-2023  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, or (at your option) any later version.
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 // TODO : All the following methods but ComputeContours use the C API of OpenCV while ComputContours
31 // uses the C++ API of the library.
32 // This should be homogenized and preferably by using the C++ API (which is more recent for all the methods
33
34 // The code has to be "cleaned up" too
35
36 /*!
37   Constructor
38   \param theFilename - image to process
39 */
40 ShapeRec_FeatureDetector::ShapeRec_FeatureDetector(): 
41   corners()
42 {
43   cornerCount = 2000;
44   rect=cvRect(0,0,0,0);
45   imagePath = ""; //theFilename;
46   // Store the dimensions of the picture
47   imgHeight = 0;
48   imgWidth  = 0;
49 }
50
51 /*!
52   Sets the path of the image file to be processed
53   \param thePath - Location of the image file 
54 */
55 void ShapeRec_FeatureDetector::SetPath( const std::string& thePath )
56 {
57   imagePath = thePath; 
58   if (imagePath != "")
59   {
60     IplImage* src = cvLoadImage(imagePath.c_str(),CV_LOAD_IMAGE_COLOR);
61     imgHeight = src->height;
62     imgWidth = src->width;
63     cvReleaseImage(&src);
64   }
65 }
66
67 /*!
68   Computes the corners of the image located at imagePath
69 */
70 void ShapeRec_FeatureDetector::ComputeCorners( bool useROI, ShapeRec_Parameters* parameters )
71 {
72   ShapeRec_CornersParameters* aCornersParameters = dynamic_cast<ShapeRec_CornersParameters*>( parameters );
73   if ( !aCornersParameters ) aCornersParameters = new  ShapeRec_CornersParameters();
74
75   // Images to be used for detection
76   IplImage *eig_img, *temp_img, *src_img_gray;
77   
78   // Load image
79   src_img_gray = cvLoadImage (imagePath.c_str(), CV_LOAD_IMAGE_GRAYSCALE);
80   
81   if ( useROI )
82   {
83     // If a ROI as been set use it for detection
84     cvSetImageROI( src_img_gray, rect );
85   }
86   
87   eig_img = cvCreateImage (cvGetSize (src_img_gray), IPL_DEPTH_32F, 1);
88   temp_img = cvCreateImage (cvGetSize (src_img_gray), IPL_DEPTH_32F, 1);
89   corners = (CvPoint2D32f *) cvAlloc (cornerCount * sizeof (CvPoint2D32f));
90   
91   // image height and width
92   imgHeight = src_img_gray->height;
93   imgWidth  = src_img_gray->width;
94
95   // Corner detection using cvCornerMinEigenVal 
96   // (one of the methods available inOpenCV, there is also a cvConerHarris method that can be used by setting a flag in cvGoodFeaturesToTrack)
97   cvGoodFeaturesToTrack (src_img_gray, eig_img, temp_img, corners, &cornerCount, aCornersParameters->qualityLevel, aCornersParameters->minDistance);
98   cvFindCornerSubPix (src_img_gray, corners, cornerCount, cvSize (aCornersParameters->kernelSize, aCornersParameters->kernelSize), cvSize (-1, -1),
99                       cvTermCriteria (aCornersParameters->typeCriteria, aCornersParameters->maxIter, aCornersParameters->epsilon));
100
101   cvReleaseImage (&eig_img);
102   cvReleaseImage (&temp_img);
103   cvReleaseImage (&src_img_gray);
104
105 }
106
107 /*!
108   Computes the contours of the image located at imagePath
109 */
110 bool ShapeRec_FeatureDetector::ComputeContours( bool useROI, ShapeRec_Parameters* parameters )
111
112   // Initialising images
113   cv::Mat src, src_gray;
114   cv::Mat detected_edges;
115   
116   // Read image
117   src = cv::imread( imagePath.c_str() );
118   if( !src.data )
119     return false; 
120   
121   if ( !useROI )   // CANNY: The problem is that with that filter the detector detects double contours
122   {   
123     // Convert the image to grayscale
124     if (src.channels() == 3)
125       cv::cvtColor( src, src_gray, CV_BGR2GRAY );
126     else if (src.channels() == 1)
127       src_gray = src;
128
129     ShapeRec_CannyParameters* aCannyParameters = dynamic_cast<ShapeRec_CannyParameters*>( parameters );
130     if ( !aCannyParameters ) aCannyParameters = new ShapeRec_CannyParameters();
131
132     // Reduce noise              
133     blur( src_gray, detected_edges, cv::Size( aCannyParameters->kernelSize, aCannyParameters->kernelSize ) );
134     // Canny detector
135     Canny( detected_edges, detected_edges, aCannyParameters->lowThreshold, aCannyParameters->lowThreshold * aCannyParameters->ratio,
136            aCannyParameters->kernelSize, aCannyParameters->L2gradient );
137   }
138   else //COLORFILTER
139   {
140     // Load the input image where we want to detect contours
141     IplImage* input_image = cvLoadImage(imagePath.c_str(),CV_LOAD_IMAGE_COLOR);
142
143     ShapeRec_ColorFilterParameters* aColorFilterParameters = dynamic_cast<ShapeRec_ColorFilterParameters*>( parameters );
144     if ( !aColorFilterParameters ) aColorFilterParameters = new ShapeRec_ColorFilterParameters();
145
146     // Reduce noise
147     cvSmooth( input_image, input_image, CV_GAUSSIAN, aColorFilterParameters->smoothSize, aColorFilterParameters->smoothSize );
148   
149     // Crop the image to the selected part only (sample_image)
150     cvSetImageROI(input_image, rect);
151     IplImage* sample_image = cvCreateImage(cvGetSize(input_image),
152                                            input_image->depth,
153                                            input_image->nChannels);
154     cvCopy(input_image, sample_image, NULL);
155     cvResetImageROI(input_image);
156   
157     IplImage* sample_hsv = cvCreateImage( cvGetSize(sample_image),8,3 );
158     IplImage* sample_h_plane  = cvCreateImage( cvGetSize(sample_image), 8, 1 );
159     IplImage* sample_s_plane = cvCreateImage( cvGetSize(sample_image), 8, 1 );
160     CvHistogram* sample_hist;
161
162     cvCvtColor(sample_image, sample_hsv, CV_BGR2HSV);
163   
164     cvSplit(sample_hsv, sample_h_plane, sample_s_plane, 0, 0);
165     IplImage* sample_planes[] = { sample_h_plane, sample_s_plane };
166   
167     // Create the hue / saturation histogram of the SAMPLE image.
168     // This histogramm will be representative of what is the zone
169     // we want to find the frontier of. Indeed, the sample image is meant to 
170     // be representative of this zone
171     float hranges[] = { 0, 180 };
172     float sranges[] = { 0, 256 };
173     float* ranges[] = { hranges, sranges };
174     sample_hist = cvCreateHist( 2, aColorFilterParameters->histSize, aColorFilterParameters->histType, ranges );
175   
176     //calculate hue /saturation histogram
177     cvCalcHist(sample_planes, sample_hist, 0 ,0);
178
179 //   // TEST print of the histogram for debugging
180 //   IplImage* hist_image = cvCreateImage(cvSize(320,300),8,3);
181 //   
182 //   //draw hist on hist_test image.
183 //   cvZero(hist_image);
184 //   float max_value = 0;
185 //   cvGetMinMaxHistValue(hist, 0 , &max_value, 0, 0);
186 //   int bin_w = hist_image->width/size_hist;
187 //   for(int i = 0; i < size_hist; i++ )
188 //   {
189 //     //prevent overflow
190 //     int val = cvRound( cvGetReal1D(hist->bins,i)*hist_image->
191 //     height/max_value);
192 //     CvScalar color = CV_RGB(200,0,0);
193 //     //hsv2rgb(i*180.f/size_hist);
194 //     cvRectangle( hist_image, cvPoint(i*bin_w,hist_image->height),
195 //     cvPoint((i+1)*bin_w,hist_image->height - val),
196 //     color, -1, 8, 0 );
197 //   }
198 //  
199 //    
200 //   cvNamedWindow("hist", 1); cvShowImage("hist",hist_image);
201   
202   
203     // Calculate the back projection of hue and saturation planes of the INPUT image
204     // by mean of the histogram of the SAMPLE image.
205     //
206     // The pixels which (h,s) coordinates correspond to high values in the histogram
207     // will have high values in the grey image result. It means that a pixel of the INPUT image 
208     // which is more probably in the zone represented by the SAMPLE image, will be whiter 
209     // in the back projection.
210     IplImage* backproject = cvCreateImage(cvGetSize(input_image), 8, 1);
211     IplImage* binary_backproject = cvCreateImage(cvGetSize(input_image), 8, 1);
212     IplImage* input_hsv = cvCreateImage(cvGetSize(input_image),8,3);
213     IplImage* input_hplane = cvCreateImage(cvGetSize(input_image),8,1);
214     IplImage* input_splane = cvCreateImage(cvGetSize(input_image),8,1);
215   
216     // Get hue and saturation planes of the INPUT image
217     cvCvtColor(input_image, input_hsv, CV_BGR2HSV);
218     cvSplit(input_hsv, input_hplane, input_splane, 0, 0);
219     IplImage* input_planes[] = { input_hplane, input_splane };
220     
221     // Compute the back projection
222     cvCalcBackProject(input_planes, backproject, sample_hist);
223   
224     // Threshold in order to obtain a binary image
225     cvThreshold(backproject, binary_backproject, aColorFilterParameters->threshold, aColorFilterParameters->maxThreshold, CV_THRESH_BINARY);
226     cvReleaseImage(&sample_image);
227     cvReleaseImage(&sample_hsv);
228     cvReleaseImage(&sample_h_plane);
229     cvReleaseImage(&sample_s_plane);
230     cvReleaseImage(&input_image);
231     cvReleaseImage(&input_hsv);
232     cvReleaseImage(&input_hplane);
233     cvReleaseImage(&input_splane);
234     cvReleaseImage(&backproject);
235
236 #if CV_MAJOR_VERSION == 3
237     detected_edges = cv::cvarrToMat(binary_backproject);
238 #else
239     detected_edges = cv::Mat(binary_backproject);
240 #endif
241   }
242   // else if ( detection_method == RIDGE_DETECTOR )  // Method adapted for engineering drawings (e.g. watershed functionality could be used here cf.OpenCV documentation and samples)
243   // {
244   //   // TODO
245   //   return false;
246   // }
247
248   //  _detectAndRetrieveContours( detected_edges, parameters->findContoursMethod );
249   detected_edges = detected_edges > 1;
250   findContours( detected_edges, contours, hierarchy, CV_RETR_CCOMP, parameters->findContoursMethod);
251
252   return true;
253   
254 }
255
256 /*!
257   Computes the lines in the image located at imagePath
258 */
259 bool ShapeRec_FeatureDetector::ComputeLines(){
260   MESSAGE("ShapeRec_FeatureDetector::ComputeLines()");
261   // Initialising images
262   cv::Mat src, src_gray, detected_edges, dst;
263   
264   src=cv::imread(imagePath.c_str(), 0);
265   
266   Canny( src, dst, 50, 200, 3 );
267   HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
268   return true;
269   
270 }
271
272 /*!
273   Stores a region of interest given by user in rect
274   \param theRect - Region Of Interest of the image located at imagePath 
275 */
276 void ShapeRec_FeatureDetector::SetROI( const QRect& theRect )
277 {
278   if (!theRect.isEmpty()){
279     rect = cvRect(theRect.x(),theRect.y(),theRect.width(),theRect.height());
280   }
281 }
282
283 /*!
284   Crops the image located at imagePath to the region of interest given by the user via SetROI
285   and stores the result in /tmp
286   \param theRect - Region Of Interest of the image located at imagePath 
287 */
288 std::string ShapeRec_FeatureDetector::CroppImage()
289 {
290   IplImage* src = cvLoadImage(imagePath.c_str(),CV_LOAD_IMAGE_COLOR);
291  
292   cvSetImageROI(src, rect);
293   IplImage* cropped_image = cvCreateImage(cvGetSize(src),
294                                           src->depth,
295                                           src->nChannels);
296   cvCopy(src, cropped_image, NULL);
297   cvResetImageROI(src);
298   
299   cvSaveImage ("/tmp/cropped_image.bmp", cropped_image);
300   
301   cvReleaseImage(&src);
302   cvReleaseImage(&cropped_image);
303   
304   return "/tmp/cropped_image.bmp";
305 }
306
307 /*!
308   \class ShapeRec_CornersParameters
309   \brief Parameters for the corners detection 
310 */
311 ShapeRec_CornersParameters::ShapeRec_CornersParameters()
312 {
313   qualityLevel = 0.2;
314   minDistance = 1;
315   typeCriteria = CV_TERMCRIT_ITER | CV_TERMCRIT_EPS;
316   maxIter = 20;
317   epsilon = 0.03;
318 }
319 ShapeRec_CornersParameters::~ShapeRec_CornersParameters()
320 {
321 }
322
323 /*!
324   \class ShapeRec_Parameters
325   \brief Parameters for the contour/corners detection 
326 */
327 ShapeRec_Parameters::ShapeRec_Parameters()
328 {
329   kernelSize = 3;
330   findContoursMethod = CV_CHAIN_APPROX_NONE;
331 }
332 ShapeRec_Parameters::~ShapeRec_Parameters()
333 {
334 }
335
336 /*!
337   \class ShapeRec_CannyParameters
338   \brief Parameters for the contour detection 
339 */
340 ShapeRec_CannyParameters::ShapeRec_CannyParameters()
341 {
342   lowThreshold = 100; // is used for edge linking.
343   ratio = 3;          // lowThreshold*ratio is used to find initial segments of strong edges
344   L2gradient = true;  // norm L2 or L1
345 }
346
347 ShapeRec_CannyParameters::~ShapeRec_CannyParameters()
348 {
349 }
350
351 /*!
352   \class ShapeRec_ColorFilterParameters
353   \brief Parameters for the contour detection 
354 */
355 ShapeRec_ColorFilterParameters::ShapeRec_ColorFilterParameters()
356 {
357   smoothSize = 3;           // The parameter of the smoothing operation, the aperture width. Must be a positive odd number
358   histSize = new int[2];    // array of the histogram dimension sizes
359   histSize[0] = 30;         // hbins
360   histSize[1] = 32;         // sbins
361   histType = CV_HIST_ARRAY; // histogram representation format
362   threshold = 128;          // threshold value
363   maxThreshold = 255;       // maximum value to use with the THRESH_BINARY thresholding types
364 }
365
366 ShapeRec_ColorFilterParameters::~ShapeRec_ColorFilterParameters()
367 {
368 }