Krupali Mistry
Krupali Mistry

Reputation: 644

find small defects from the image

I have an image which contains four circles in each corner and there is a defect present in one of the circles, so how to identify it. I have a lot of image with small and big defects. I am resizing the image as the image is of size 1920*1080 to size 960*540. I have tried normalizing the image, then applying threshold and canny edge but I don't know how to identify or recognize the image. I am working with OpenCV C++ and I am beginner so I don't know which algorithm to apply.

My image is :And you can clearly see that the defect is present in the top left circle and all other circles are without defects.

enter image description here

After canny my image looks like this: enter image description here

Up till now I have tried the below code :

        //resize image
        Mat r_img;
        resize(img, r_img, Size(img.cols / 2, img.rows / 2), 0, 0, INTER_LANCZOS4);

        Mat gfilter;
        cv::GaussianBlur(r_img, gfilter, Size(5, 5),2, 2);

        //convert to grayscale
        Mat gray_img;
        cv::cvtColor(gfilter, gray_img, COLOR_BGR2GRAY);

        double min, max;
        cv::minMaxLoc(gray_img, &min, &max);
        float sub = min;
        float mult = 255.0f / (float)(max - sub);
        cv::Mat normalized = gray_img - sub;
        normalized = mult * normalized;
        cv::imshow("normalized", normalized);

        cv::Mat mask;
        cv::threshold(normalized, mask, 127, 255, THRESH_BINARY)
        
        Mat canny;
        cv::Canny(normalized, canny, 50, 150, 3);

Upvotes: 1

Views: 285

Answers (2)

fana
fana

Reputation: 1890

Why you employing the processes such as resizing and blurring? I think they work in the direction of obscuring defects.

Cut out each of the four areas and logPolar() them, will help you to find defects, I think.


This is simple sample using warpPolar().

//Circle Area Image.
//  In this sample code, this image is created at here.
//  In real, you must cut out the circle region from your source image.
cv::Mat TheSrcCircleImg = cv::Mat::zeros( 101, 101, CV_8UC3 );
cv::Point Center{ 50,50 };  //also, you must estimate this point.
{
    //Draw some cielces 
    cv::circle( TheSrcCircleImg, Center, 48, cv::Scalar(8,96,16), -1 );
    cv::circle( TheSrcCircleImg, Center, 35, cv::Scalar(0,128,0), -1 );
    cv::circle( TheSrcCircleImg, Center, 22, cv::Scalar(255,32,0), 6 );
    cv::circle( TheSrcCircleImg, Center, 29, cv::Scalar(100,100,100), 1 );
    cv::circle( TheSrcCircleImg, Center, 10, cv::Scalar(0,0,0), -1 );
    //Add "defects"
    cv::line( TheSrcCircleImg, cv::Point(70,65), cv::Point(96,55), cv::Scalar(0,255,255), 2 );
    cv::line( TheSrcCircleImg, cv::Point(45,32), cv::Point(30,30), cv::Scalar(0,255,255), 1 );
}
cv::imshow( "The Src", TheSrcCircleImg );

//I found that arguments of logPolar() is not explained in reference manual.
//So, I used werpPolar() intstead.
cv::Mat ResultImg;
cv::warpPolar( TheSrcCircleImg, ResultImg, cv::Size(50,360), Center, 50, cv::INTER_LINEAR+cv::WARP_POLAR_LINEAR );
cv::imshow( "Result", ResultImg );
cv::waitKey();

The Community Bot said "unclear", so I tried to write this story as code. As a result, result of this code seems poor. It will be necessary to consider the detection part.

std::vector< std::vector< cv::Point> > FindOutlines( const cv::Mat &SrcImg8U1C )
{
    cv::Mat BinImg;
    cv::threshold( SrcImg8U1C, BinImg, 16, 255, cv::THRESH_BINARY );
    std::vector< std::vector< cv::Point> > Contours;
    cv::findContours( BinImg, Contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE );
    return Contours;
}

//Test Process for 1 Circle Region
void Proc( const cv::Mat &SrcImg8U1C, const std::vector<cv::Point> &Outline, const std::string &ShowWndName )
{
    cv::RotatedRect FittingResult = cv::fitEllipse( Outline );
    cv::Rect Region = cv::boundingRect( Outline );
    FittingResult.center.x -= Region.x;
    FittingResult.center.y -= Region.y;

    cv::Mat RegionImg;
    cv::Mat ShowImg;
    {
        cv::equalizeHist( SrcImg8U1C(Region), RegionImg );
        cv::cvtColor( RegionImg, ShowImg, cv::COLOR_GRAY2BGR );
        ShowImg *= 0.5;
        cv::ellipse( ShowImg, FittingResult, cv::Scalar(0,0,255) );
        cv::drawMarker( ShowImg, FittingResult.center, cv::Scalar(0,255,0), cv::MARKER_CROSS, 8 );
    }

    const double radius = (FittingResult.size.width + FittingResult.size.height) * 0.25;
    cv::Mat LPImg;
    cv::warpPolar( RegionImg, LPImg, cv::Size(cvRound(radius),180), FittingResult.center, radius, cv::INTER_LINEAR+cv::WARP_POLAR_LINEAR );

    cv::Mat DetectionMask = cv::Mat::zeros( LPImg.size(), CV_8U );
    {//Detection based on edge direction. (looks Poor)
        cv::Mat SobelX, SobelY;
        cv::Sobel( LPImg, SobelX, CV_32F, 1,0 );
        cv::Sobel( LPImg, SobelY, CV_32F, 0,1 );
        const float SqMagThresh = 200 * 200;
        const float DetectionRateThresh = 0.5f;
        for( int y=0; y<LPImg.rows; ++y )
        {
            const float *pSX = SobelX.ptr<float>(y);
            const float *pSY = SobelY.ptr<float>(y);
            unsigned char *pD = DetectionMask.ptr<unsigned char>(y);
            for( int x=0; x<LPImg.cols; ++x, ++pSX,++pSY,++pD )
            {
                if(  (*pSX * *pSX) + (*pSY * *pSY) < SqMagThresh )continue;
                if( fabs(*pSY) >= fabs(*pSX)*DetectionRateThresh ){ *pD = 255;  }
            }
        }
        cv::Mat Morph;
        cv::morphologyEx( DetectionMask, Morph, cv::MORPH_CLOSE, cv::Mat() );
        cv::morphologyEx( Morph, Morph, cv::MORPH_OPEN, cv::Mat() );
        cv::bitwise_and( Morph, DetectionMask, DetectionMask );
    }
    {//Draw Result : This inverse warp calculation is from Reference Manual.
        const double Kangle = DetectionMask.rows / CV_2PI;
        for( int y=0; y<DetectionMask.rows; ++y )
        {
            const double angleRad = y / Kangle;
            const double Cos = cos(angleRad);
            const double Sin = sin(angleRad);
            const unsigned char *pD = DetectionMask.ptr<unsigned char>(y);
            for( int x=0;x<DetectionMask.cols; ++x, ++pD )
            {
                if( !(*pD) )continue;
                double Klin = DetectionMask.cols / radius;
                double magnitude = x / Klin;
                int x_reg = cvRound( FittingResult.center.x + magnitude*Cos );
                int y_reg = cvRound( FittingResult.center.y + magnitude*Sin );
                ShowImg.at<cv::Vec3b>(y_reg,x_reg) = cv::Vec3b(0,255,255);
            }
        }
    }
    cv::imshow( ShowWndName, ShowImg );
}

int main()
{
    //The Image "ciecles.png" is copied from this Question.
    cv::Mat SrcImg = cv::imread( "circles.png", cv::IMREAD_GRAYSCALE );
    if( SrcImg.empty() )return 0;
    std::vector< std::vector< cv::Point> > Outlines = FindOutlines( SrcImg );
    for( size_t i=0; i<Outlines.size(); ++i ){  Proc( SrcImg, Outlines[i], std::string("Result")+char('0'+i) ); }
    cv::waitKey();
    return 0;
}

Upvotes: 4

user1196549
user1196549

Reputation:

  • locate the circles centers accurately (finding the outlines of the holes is easy);

  • unwind the images to straighten the rings;

  • use template matching on small windows to detect deviations from the clean part. Alternatively, just image differencing.

Remarks:

  • straighten on more than 360° to make sure that the template is tried everywhere;

  • to obtain the template, a good method can be to average all the profiles along the unwound image, then to repeat it to get a squarish image.

Upvotes: 0

Related Questions