├── .gitignore ├── CMakeLists.txt ├── images ├── defect-1.jpg ├── defect-2.jpg ├── defect-3.jpg └── pcb.jpg └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(defect-detect) 3 | add_definitions(-std=c++14) 4 | find_package(OpenCV REQUIRED) 5 | include_directories(${OpenCV_INCLUDE_DIRS}) 6 | add_executable(defect-detect main.cpp) 7 | find_library(PROGRAM_OPTIONS boost_program_options) 8 | find_library(FILESYSTEM boost_filesystem) 9 | find_library(BOOST_SYSTEM boost_system) 10 | target_link_libraries(defect-detect ${OpenCV_LIBS} ${PROGRAM_OPTIONS} ${FILESYSTEM} ${BOOST_SYSTEM}) 11 | 12 | #Move the images to the build directory 13 | file(GLOB IMAGES "images/*") 14 | file(COPY ${IMAGES} DESTINATION ${CMAKE_BINARY_DIR}) 15 | -------------------------------------------------------------------------------- /images/defect-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jysandy/defect-detect/19a6df9c5c6f73fa626801e438b4606fa6260d63/images/defect-1.jpg -------------------------------------------------------------------------------- /images/defect-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jysandy/defect-detect/19a6df9c5c6f73fa626801e438b4606fa6260d63/images/defect-2.jpg -------------------------------------------------------------------------------- /images/defect-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jysandy/defect-detect/19a6df9c5c6f73fa626801e438b4606fa6260d63/images/defect-3.jpg -------------------------------------------------------------------------------- /images/pcb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jysandy/defect-detect/19a6df9c5c6f73fa626801e438b4606fa6260d63/images/pcb.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | bool defect_exists(const cv::Mat& input, const cv::Mat& reference, cv::Mat& marked_image); 8 | 9 | int main(int argc, char** argv) 10 | { 11 | using std::cout; 12 | using std::string; 13 | namespace po = boost::program_options; 14 | namespace fs = boost::filesystem; 15 | 16 | auto desc = po::options_description("Allowed options"); 17 | desc.add_options() 18 | ("help", "produce help message") 19 | ("input,i", po::value(), "input to be tested") 20 | ("reference,r", po::value(), "reference image"); 21 | 22 | auto args_map = po::variables_map(); 23 | po::store(po::parse_command_line(argc, argv, desc), args_map); 24 | po::notify(args_map); 25 | 26 | if (args_map.count("help") 27 | || !args_map.count("input") 28 | || !args_map.count("reference")) 29 | { 30 | cout << desc << "\n"; 31 | return 1; 32 | } 33 | 34 | if (!fs::exists(fs::path(args_map["reference"].as()))) 35 | { 36 | cout << "File " << args_map["reference"].as() << " does not exist!\n"; 37 | return 1; 38 | } 39 | 40 | if (!fs::exists(fs::path(args_map["input"].as()))) 41 | { 42 | cout << "File " << args_map["input"].as() << " does not exist!\n"; 43 | return 1; 44 | } 45 | 46 | auto reference_image = cv::imread(args_map["reference"].as()); 47 | auto input_image = cv::imread(args_map["input"].as()); 48 | auto marked_image = cv::Mat(); 49 | 50 | if (defect_exists(input_image, reference_image, marked_image)) 51 | { 52 | cout << "Defect found\n"; 53 | cv::imshow("Defect found!", marked_image); 54 | cv::waitKey(0); 55 | cv::imwrite("defect.jpg", marked_image); 56 | } 57 | else 58 | { 59 | cout << "No defects found\n"; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | bool defect_exists(const cv::Mat& input, const cv::Mat& reference, cv::Mat& marked_image) 66 | { 67 | cv::Mat gray_input; 68 | cv::Mat gray_reference; 69 | //Convert to grayscale -> histeq -> thresholding -> closing 70 | 71 | cv::cvtColor(input, gray_input, CV_RGB2GRAY); 72 | cv::cvtColor(reference, gray_reference, CV_RGB2GRAY); 73 | 74 | cv::equalizeHist(gray_input, gray_input); 75 | cv::equalizeHist(gray_reference, gray_reference); 76 | 77 | auto threshold = 150; 78 | cv::threshold(gray_input, gray_input, threshold, 255, cv::THRESH_BINARY); 79 | cv::threshold(gray_reference, gray_reference, threshold, 255, cv::THRESH_BINARY); 80 | 81 | auto strel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); 82 | cv::morphologyEx(gray_input, gray_input, cv::MORPH_CLOSE, strel); 83 | cv::morphologyEx(gray_reference, gray_reference, cv::MORPH_CLOSE, strel); 84 | 85 | //XOR to detect differences 86 | cv::Mat xor_result; 87 | cv::bitwise_xor(gray_input, gray_reference, xor_result); 88 | //Get rid of noise 89 | cv::medianBlur(xor_result, xor_result, 3); 90 | 91 | //If there are non-zero pixels 92 | if (cv::countNonZero(xor_result) > 1) 93 | { 94 | //Outline the defects in red. 95 | auto output = input.clone(); 96 | 97 | //First color the defect outlines black. 98 | cv::Mat defect_mask = 255 - xor_result; 99 | cv::cvtColor(defect_mask, defect_mask, CV_GRAY2RGB); 100 | cv::bitwise_and(output, defect_mask, output); 101 | 102 | //Change the white pixels in the outline to red. 103 | auto color_xor = cv::Mat(); 104 | auto dilation_strel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); 105 | cv::dilate(xor_result, color_xor, dilation_strel, cv::Point(-1, -1)); 106 | cv::cvtColor(color_xor, color_xor, CV_GRAY2RGB); 107 | for (auto pixel = color_xor.begin(); pixel != color_xor.end(); pixel++) 108 | { 109 | if ((*pixel)[0] > 0 110 | || (*pixel)[1] > 0 111 | || (*pixel)[2] > 0) 112 | { 113 | *pixel = cv::Vec3b(0, 0, 255); 114 | } 115 | } 116 | 117 | //Blend the masked output and the red outline. 118 | cv::addWeighted(output, 0.4, color_xor, 1.0, 0, output); 119 | 120 | marked_image = output; 121 | return true; 122 | } 123 | else 124 | { 125 | return false; 126 | } 127 | } 128 | --------------------------------------------------------------------------------