从图像中找出小缺陷

find small defects from the image

我有一张图像,每个角包含四个圆圈,其中一个圆圈中存在缺陷,请问如何识别它。我有很多带有大大小小的缺陷的图像。 我正在调整图像大小,因为图像大小 1920*1080960*540。 我试过对图像进行标准化,然后应用阈值和精巧的边缘,但我不知道如何识别或识别图像。 我正在使用 OpenCV C++,我是初学者,所以我不知道要应用哪种算法。

我的图片是:你可以清楚地看到缺陷出现在左上角的圆圈中,其他圆圈都没有缺陷。

精明后我的形象是这样的:

到目前为止,我已经尝试了以下代码:

        //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);

为什么要采用调整大小和模糊等过程? 我认为他们的工作方向是掩盖缺陷。

切出四个区域中的每一个并对其进行logPolar(),我认为将帮助您找到缺陷。


这是使用 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();

社区机器人说“不清楚”,所以我试着把这个故事写成代码。 结果,这段代码的结果看起来很差。 需要考虑检测部分。

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;
}
  • 准确定位圆心(找到孔的轮廓很容易);

  • 展开图像以拉直圆环;

  • 在小 windows 上使用模板匹配来检测与干净部分的偏差。或者,只是图像差异。

备注:

  • 拉直360°以上,确保模板处处试穿;

  • 要获得模板,一个好的方法是将展开图像上的所有轮廓平均,然后重复它以获得方形图像。