├── README.md └── defectDetection.cpp /README.md: -------------------------------------------------------------------------------- 1 | # defect-detection(DRAFT codes) 2 | detect the defects with OpenCV API (equalizeHist, GaussianBlur, threshold, findContours) after trying Canny and HoughLines. 3 | -------------------------------------------------------------------------------- /defectDetection.cpp: -------------------------------------------------------------------------------- 1 | #define _USE_MATH_DEFINES 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "Defect.h" 12 | #include 13 | 14 | using namespace cv; 15 | using namespace std; 16 | 17 | void processImage(Mat& image, double g_thresh = 50, int mode = -1); 18 | vector > getContours(Mat image); 19 | void displayResult(Mat image, vector> contours, String outputImageName, double field_width, double defect_size); 20 | bool isAllWhite(Mat image, int r); 21 | void refineDefectSize(Mat image, Defect& defect, int y0, int x0); 22 | 23 | float g_thresh, g_alpha; 24 | 25 | int main(int argc, char* argv[]) 26 | { 27 | // load the image 28 | string inputImageName, outputImageName; 29 | double defect_size, field_width; 30 | if (argc != 5 && argc != 6 && argc != 7) 31 | { 32 | cout << "Usage: DefectDetection.exe [(default is 3)] [(default is 50)]" << endl; 33 | return -1; 34 | } else { 35 | inputImageName = argv[1]; //"..\\data\\real\\1.bmp"; 36 | outputImageName = argv[2]; //"..\\result\\1.bmp"; 37 | field_width = atof(argv[3]); 38 | defect_size = atof(argv[4]); 39 | g_alpha = (argc == 6) ? atof(argv[5]) : 3; 40 | g_thresh = (argc == 7) ? atof(argv[6]) : 50.0; 41 | } 42 | 43 | // 0. 44 | cout << "Loading and processing image "<< inputImageName < > contours = getContours(temp); 57 | 58 | //String outputImageName = inputImageName.substr(0, inputImageName.rfind(".")) + ".bmp"; 59 | displayResult(image, contours, outputImageName, field_width, defect_size); 60 | 61 | return 0; 62 | } 63 | 64 | void sharpen(Mat src, Mat & dst){ 65 | Mat kernel = (Mat_(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); 66 | filter2D(src, dst, -1, kernel); 67 | } 68 | 69 | void processImage(Mat& image, double g_thresh, int mode) 70 | { 71 | string info; 72 | 73 | if (image.channels() == 3) 74 | { 75 | cvtColor(image, image, CV_BGR2GRAY); 76 | } 77 | Mat element = getStructuringElement(MORPH_ELLIPSE, Size(5,5)); 78 | double minVal, maxVal; 79 | minMaxLoc(image, &minVal, &maxVal); 80 | image.convertTo(image, CV_8UC1, 255 / (maxVal-minVal), -minVal * 255 / (maxVal-minVal)); 81 | 82 | switch (mode){ 83 | case 99: 84 | #ifdef _DEBUG 85 | imshow("original", image); 86 | waitKey(); 87 | #endif 88 | //for(int i=0; i<1; i++) 89 | //morphologyEx(image, image, MORPH_CLOSE, element);//--MORPH_OPEN, --MORPH_BLACKHAT, --MORPH_TOPHAT, -MORPH_GRADIENT, MORPH_CLOSE 90 | 91 | #ifdef _DEBUG 92 | imshow("morph", image); 93 | waitKey(); 94 | #endif 95 | //equalizeHist(image, image); 96 | #ifdef _DEBUG 97 | imshow("equalization", image); 98 | waitKey(); 99 | #endif 100 | //GaussianBlur(image, image, Size(3, 3), 1); 101 | #ifdef _DEBUG 102 | imshow("after blurred", image); 103 | waitKey(); 104 | #endif 105 | //sharpen(image, image); 106 | #ifdef _DEBUG 107 | imshow("after sharpening", image); 108 | waitKey(); 109 | #endif 110 | 111 | /* 112 | Laplacian(image, image, -1, 3); 113 | imshow("after Laplace", image); 114 | waitKey(); 115 | */ 116 | 117 | threshold(image, image, 0, 255, CV_THRESH_BINARY_INV|CV_THRESH_OTSU); 118 | #ifdef _DEBUG 119 | imshow("after OTSU", image); 120 | waitKey(); 121 | #endif 122 | info = "Use OTSU binarilization"; 123 | break; 124 | case 1: 125 | equalizeHist(image, image); 126 | GaussianBlur(image, image, Size(5, 5), 3); 127 | threshold(image, image, 0, 255, CV_THRESH_BINARY_INV|CV_THRESH_OTSU); 128 | info = "Use OTSU binarilization"; 129 | break; 130 | case 2: 131 | Canny(image, image, 40, 160, 3, true); 132 | info = "Use Canny edge detection"; 133 | break; 134 | case 3: 135 | adaptiveThreshold(image, image, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 5, 5); 136 | info = "Use adaptive thresholding"; 137 | break; 138 | case 0: 139 | equalizeHist(image, image); 140 | GaussianBlur(image, image, Size(5, 5), 3); 141 | threshold(image, image, g_thresh, 255, CV_THRESH_BINARY_INV); 142 | info = "Use fine thresholding"; 143 | break; 144 | default: 145 | equalizeHist(image, image); 146 | GaussianBlur(image, image, Size(9,9), 5); 147 | sharpen(image, image); 148 | threshold(image, image, g_thresh, 255, CV_THRESH_BINARY_INV); 149 | info = "Use coarse thresholding"; 150 | break; 151 | } 152 | 153 | //bitwise_xor(image, Scalar(255), image); 154 | #ifdef _DEBUG 155 | cout< > getContours(Mat image) 161 | { 162 | vector > contours; 163 | findContours(image, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); //CV_RETR_EXTERNAL 164 | return contours; 165 | } 166 | 167 | bool compDefect(const Defect& df1, const Defect& df2) 168 | { 169 | return df2 < df1; 170 | } 171 | 172 | 173 | void blobDetection(Mat image, Defect& defect, int y0, int x0){ 174 | /* 175 | // Blob detector 176 | vector keypoints; 177 | SimpleBlobDetector::Params params; 178 | params.filterByArea = true; 179 | params.minArea = 10;// 0.5*area; 180 | params.maxArea = 1.5*area; 181 | 182 | SimpleBlobDetector blobDetector(params); 183 | blobDetector.create("SimpleBlob"); 184 | blobDetector.detect(image, keypoints); 185 | drawKeypoints(image, keypoints, image, Scalar(255, 0, 0)); 186 | 187 | 188 | float radius = diameter/2; 189 | float minDist = radius; 190 | int minId = -1; 191 | for (int i = 0; i < keypoints.size(); i++){ 192 | Point2f dist = Point2f(keypoints[i].pt.y - image.rows / 2, keypoints[i].pt.x - image.cols / 2); 193 | float temp = norm(dist); 194 | if (norm(dist) < minDist){ 195 | minDist = norm(dist); 196 | minId = i; 197 | } 198 | } 199 | imwrite("..\\data\\result\\test_result.png", image); 200 | namedWindow("result", 1); 201 | imshow("result", image); 202 | waitKey(); 203 | return (minId == -1) ? diameter : keypoints[minId].size; 204 | */ 205 | 206 | // find contours 207 | //resize(patch, patch, Size(1010, 1010), CV_INTER_LINEAR); 208 | 209 | processImage(image, g_thresh, 99); 210 | Mat temp; 211 | image.copyTo(temp); 212 | vector > contours = getContours(temp); 213 | ///////////////////////////////////////////////////////////////////////////////////////////////////// 214 | 215 | float r; 216 | Point2f c; 217 | Point2f refined_center = Point2f(image.cols/2.0, image.rows/2.0); 218 | float refined_radius = defect.getDiameter()/2; 219 | 220 | /* 221 | // find the largest defect 222 | int max_index = 0; 223 | double max_area = 0; 224 | for (int i = 0; i < contours.size(); i++) { 225 | double length = arcLength(contours[i], true); 226 | double area = contourArea(contours[i]); 227 | if (area > max_area) { 228 | max_area = area; 229 | max_index = i; 230 | } 231 | } 232 | */ 233 | float minDist = refined_radius;// /2; 234 | int minId = -1; 235 | 236 | float maxRadius = refined_radius; 237 | 238 | for (int i = 0; i < contours.size(); i++){ 239 | minEnclosingCircle(contours[i], c, r); 240 | Point2f dist = Point2f(c.y - image.rows / 2.0, c.x - image.cols / 2.0); 241 | //float temp = norm(Point(3,4)); 242 | if (norm(dist) < minDist && r < defect.getDiameter() && r > maxRadius){ 243 | //minDist = norm(dist); 244 | maxRadius = r; 245 | minId = i; 246 | refined_radius = r; 247 | refined_center = c; 248 | } 249 | } 250 | 251 | double refined_diameter = 2 * refined_radius; 252 | //cout << int(image.at(int(center.y), int(center.x))) << endl; 253 | 254 | //CvRect rect = boundingRect(contours[i]); 255 | //CvBox2D box = minAreaRect(contours[i]); 256 | 257 | ostringstream ss; 258 | ss << defect.getId(); 259 | 260 | 261 | Mat result; 262 | cvtColor(image, result, CV_GRAY2BGR); 263 | imwrite(string("..\\data\\result\\") + string(ss.str()) + string(".png"), result); 264 | cout< " << refined_diameter << endl; 265 | namedWindow("result", 1);; 266 | circle(result, cvPointFrom32f(refined_center), cvRound(refined_radius), CV_RGB(255, 0, 0)); 267 | imshow("result", result); 268 | waitKey(); 269 | defect.setDiameter(refined_diameter); 270 | defect.setCenter(Point2f(refined_center.x + x0, refined_center.y + y0)); 271 | } 272 | 273 | bool isAllWhite(Mat image, int r){ 274 | int count = 0; 275 | int cx = int(image.cols/2.0+0.5), cy = int(image.rows/2.0+0.5); 276 | for(int i=cx-r; i<=cx+r; i++){ 277 | for(int j=cy-r; j<=cy+r; j++){ 278 | if(i<0 || i>=image.rows || j<0 || j>=image.cols) return false; 279 | if(norm(Point2f(i-cx, j-cy)) >= r) continue; 280 | if(image.at(i,j) == 0) { 281 | count ++; 282 | //return false; 283 | } 284 | if(count>g_alpha*r) return false; 285 | } 286 | } 287 | return true; 288 | } 289 | 290 | void refineDefectSize(Mat image, Defect& defect, int y0, int x0){ 291 | Mat result; 292 | image.copyTo(result); 293 | //------------------------------------------------------------------------------------------------------------------ 294 | processImage(image, g_thresh, 99); 295 | float r; 296 | float refined_radius, radius; 297 | refined_radius = radius = defect.getDiameter()/2; 298 | for(int iter = 0; iter<31; iter++){ 299 | r = radius*(1.0 + iter*0.1); 300 | if(!isAllWhite(image, r)) continue; 301 | refined_radius = r; 302 | } 303 | 304 | double refined_diameter = 2 * refined_radius; 305 | 306 | #ifdef _DEBUG 307 | cout< " << refined_diameter << endl; 308 | 309 | ostringstream ss; 310 | ss << defect.getId(); 311 | 312 | cvtColor(result, result, CV_GRAY2BGR); 313 | 314 | imwrite(string("..\\data\\result\\") + string(ss.str()) + string(".png"), result); 315 | 316 | namedWindow("result", 1); 317 | circle(result, Point2f(image.cols/2.0, image.rows/2.0), refined_radius, CV_RGB(255, 0, 0)); 318 | imshow("result", result); 319 | waitKey(); 320 | #endif 321 | 322 | defect.setDiameter(refined_diameter); 323 | } 324 | 325 | void displayResult(Mat image, vector> contours, String outputImageName, double field_width, double defect_size) 326 | { 327 | Mat result; 328 | cvtColor(image, result, CV_GRAY2BGR); 329 | int count = 0; 330 | 331 | // find all detects larger than the threshold specified by the argument defect_size 332 | cout << "Detecting defects larger than " << defect_size << " mm ..." << endl; 333 | double scale_factor = field_width / image.cols; 334 | vector defects; 335 | for (int i = 0; i < int(contours.size()); i++) { 336 | double length = arcLength(contours[i], true); 337 | double area = contourArea(contours[i]); 338 | 339 | Point2f center; 340 | float radius; 341 | minEnclosingCircle(contours[i], center, radius); 342 | 343 | //float ans = bfs(image, center); 344 | float diameter = 2 * radius; 345 | if (diameter * scale_factor < defect_size) continue; 346 | 347 | 348 | //cout << "diameter = " << diameter << endl; 349 | //float ans = bfs(image, center, diameter); 350 | 351 | defects.push_back(Defect(count, center, diameter, area)); 352 | 353 | // refine diameter 354 | int row = int(center.y+0.5), col = int(center.x+0.5); 355 | if(row - 50 >= 0 && row + 51 <= image.rows && col - 50 >= 0 && col + 51 <= image.cols){ 356 | Mat patch(image, Range(row - 50, row + 51), Range(col - 50, col + 51)); 357 | //blobDetection(patch, defects.back(), row, col); 358 | refineDefectSize(patch, defects.back(), row, col); 359 | diameter = defects.back().getDiameter(); 360 | } 361 | count++; 362 | 363 | circle(result, cvPointFrom32f(center), 0.5 * diameter, CV_RGB(255, 0, 0)); 364 | } 365 | cout << count << " defects are detected" << endl; 366 | 367 | sort(defects.begin(), defects.end(), compDefect); 368 | 369 | for (int i = 0; i < defects.size(); i++) 370 | { 371 | float diameter = defects[i].getDiameter(); 372 | Point2f center = defects[i].getCenter(); 373 | 374 | ostringstream ss,ss1; 375 | //ss.precision(3); 376 | //ss.setf(ios::fixed); 377 | ss << setiosflags(ios::fixed) << setprecision(3) << (diameter * scale_factor); 378 | ss1 << i+1; 379 | bool showTextBottom = center.y < 100; 380 | bool showTextRight = image.cols - center.x > 300; 381 | int offset_vert = 2 * showTextBottom - 1; 382 | if (showTextRight) { 383 | putText(result, string(ss1.str()) + ": ", Point(center.x + diameter/2 + 5, center.y + offset_vert * diameter/2 + showTextBottom * 25), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(0, 0, 255)); 384 | putText(result, string(ss.str()) + "mm", Point(center.x + diameter/2 + 45, center.y + offset_vert * diameter/2 + showTextBottom * 25), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(255, 0, 0)); 385 | } 386 | else { 387 | putText(result, string(ss1.str()) + ": ", Point(center.x - diameter/2 - 190, center.y + offset_vert * diameter/2 + showTextBottom * 25), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(0, 0, 255)); 388 | putText(result, string(ss.str()) + "mm", Point(center.x - diameter/2 - 150, center.y + offset_vert * diameter/2 + showTextBottom * 25), CV_FONT_HERSHEY_COMPLEX, 1, CV_RGB(255, 0, 0)); 389 | } 390 | cout << "defect " << (i+1) << ": size(diameter) is " << string(ss.str()) << "mm" << endl; 391 | } 392 | 393 | imwrite(outputImageName, result); 394 | cout << "SRC:End" << endl; 395 | } 396 | --------------------------------------------------------------------------------