python 我想识别这个斑点激光图像的圆圈

im9ewurl  于 2023-05-27  发布在  Python
关注(0)|答案(1)|浏览(156)

我想提取两个圆,并想知道这个圆是否同心。
我参考了一些网站和chatGPT做了一些代码,但是有限制。
我想画一个“圆”,而不是“椭圆”。
下面是图片(它是环形红色激光束)和我的算法。

import cv2
    import numpy as np

    # Load the Image
    img = cv2.imread("covered_2.jpg", cv2.IMREAD_GRAYSCALE)

    # Blur the Image
    img_blur = cv2.GaussianBlur(img, (5, 5), 0)

    # Image Binerize
    _, img_thresh = cv2.threshold(img_blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # Set the kernal size for closing
    kernel = np.ones((15, 15), np.uint8)

    #Closing the Image
    img_close = cv2.morphologyEx(img_thresh, cv2.MORPH_CLOSE, kernel)
    contours, _ = cv2.findContours(img_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    max_contour = max(contours, key=cv2.contourArea)

    # Extract the outer ellipse
    ellipse = cv2.fitEllipse(max_contour)
    center, axes, angle = ellipse
    major_axis, minor_axis = axes
    img_ellipse = np.zeros_like(img)
    cv2.ellipse(img_ellipse, ellipse, 255, 2)

    # Finding the end points of the major axis(?)
    cos_angle = np.cos(np.deg2rad(angle))
    sin_angle = np.sin(np.deg2rad(angle))
    x1 = int(center[0] - 0.5 * major_axis * sin_angle)
    y1 = int(center[1] + 0.5 * major_axis * cos_angle)
    x2 = int(center[0] + 0.5 * major_axis * sin_angle)
    y2 = int(center[1] - 0.5 * major_axis * cos_angle)

    # Finding the end points of the minor axis(?)
    x3 = int(center[0] - 0.5 * minor_axis * cos_angle)
    y3 = int(center[1] - 0.5 * minor_axis * sin_angle)
    x4 = int(center[0] + 0.5 * minor_axis * cos_angle)
    y4 = int(center[1] + 0.5 * minor_axis * sin_angle)

    # Draw lines on the new image
    img_lines = np.zeros_like(img)
    cv2.line(img_lines, (x1, y1), (x2, y2), 255, 2)
    cv2.line(img_lines, (x3, y3), (x4, y4), 255, 2)
    cv2.line(img, (x1, y1), (x2, y2), 255, 2)
    cv2.line(img, (x3, y3), (x4, y4), 255, 2)
    cv2.ellipse(img, ellipse, 255, 2)

    # Show image in the new windows
    cv2.imshow('Input Image', img)
    cv2.imshow('Binary Image', img_thresh)
    cv2.imshow('Closed Image', img_close)
    cv2.imshow('Fitted Ellipse', img_ellipse)
    cv2.imshow('Extracted Lines', img_lines)

    # Save the Image
    cv2.imwrite('edge_detect_circle.jpg', img)
    cv2.imwrite('edge_detect_circle_close.jpg', img_close)
    cv2.imwrite('edge_detect_ellipse_close.jpg', img_ellipse)
    cv2.imwrite('edge_detect_lines_close.jpg', img_lines)
    cv2.waitKey()
    cv2.destroyAllWindows()

最终的目标是补偿这个斑点图像,并绘制2个圆圈,并弄清楚它们是否同心。
原始图像是这样的:环形激光束,斑点

我试着指定一些区域并使其颜色反转以找到内部椭圆。然而,这并没有奏效。
此外,我尝试了一些霍夫循环变换,但它也没有工作。
作为最后一种方法,我使用Canny边缘检测来找出所画直线的中心,这也不起作用。

l7wslrjt

l7wslrjt1#

为什么使用fitEllipse
如果你想要(不是椭圆),为什么不适合
如果OpenCV不提供圆拟合(我认为是这样),请自己实现它。(“如何”将被发现时,只是谷歌。我这样做的关键字“圆拟合最小二乘”)
我用简单最小二乘法试了一下,得到了下面的拟合结果。

如你所见,结果中心位置并不完全相同。但我不能从这个结果做最终判断,因为我不知道需要多大的差异来确定这些圆不是同心圆。
这个问题被标记为[python],但我不是Python用户,所以我在C++中尝试了一下。
注意:边缘提取步骤与您的步骤不同。(我用的是最简单的方法。)因此,如果您将圆拟合到轮廓数据,结果可能会有所不同。

//Circle fitting (Simple Least Square)
void FitCircle( const std::vector<cv::Point> &Ps, cv::Point2f &C, float &r )
{
    cv::Mat A( Ps.size(), 3, CV_32F );
    cv::Mat B( Ps.size(), 1, CV_32F );
    for( int i=0; i<Ps.size(); ++i )
    {
        const auto &P = Ps[i];
        A.at<float>( i,0 ) = P.x;
        A.at<float>( i,1 ) = P.y;
        A.at<float>( i,2 ) = 1;
        B.at<float>( i ) = P.x*P.x + P.y*P.y;
    }
    cv::Mat X;
    cv::solve( A,B, X, cv::DECOMP_SVD );
    C.x = X.at<float>(0) * 0.5f;
    C.y = X.at<float>(1) * 0.5f;
    r = sqrt( X.at<float>(2) + C.x*C.x + C.y*C.y );
}

int main()
{
    //Load image as gray-scale
    cv::Mat SrcImg = cv::imread( "Ring.png", cv::IMREAD_GRAYSCALE );
    if( SrcImg.empty() )return 0;

    //Extract two edge groups (corresponding to two circles)
    std::vector< cv::Point > Edge[2];
    {
        //Extract all edge points
        std::vector< cv::Point > Points;
        {
            cv::Mat Tmp[2];
            cv::threshold( SrcImg, Tmp[0], 128, 255, cv::THRESH_BINARY );
            cv::morphologyEx( SrcImg, Tmp[1], cv::MORPH_GRADIENT, cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3,3) ) );
            cv::threshold( Tmp[1], Tmp[1], 100, 255, cv::THRESH_BINARY );
            cv::bitwise_and( Tmp[0], Tmp[1], Tmp[0] );
            cv::findNonZero( Tmp[0], Points );
        }

        //Split edge points to 2 group.
        cv::Point2f Center;
        float Radius;
        cv::minEnclosingCircle( Points, Center, Radius );
            //Here, the scale value 0.6 below is heuristic value for me.
            //But, proper scale can be given with your knowledge, I think.
        float SqDistThresh = std::pow( Radius * 0.6f, 2.0f );

        Edge[0].reserve( Points.size() );
        Edge[1].reserve( Points.size() );
        for( const auto &P : Points )
        {
            float xx = P.x - Center.x;
            float yy = P.y - Center.y;
            Edge[ xx*xx + yy*yy < SqDistThresh ].push_back( P );
        }
    }
    
    //Visualize 2 groups, fit circle for each and draw the result
    cv::Mat Show;
    cv::cvtColor( SrcImg, Show, cv::COLOR_GRAY2BGR );
    Show *= 0.2;
    cv::Vec3b Col[2] = { {0,255,0}, {0,0,255} };
    for( int i=0; i<2; ++i )
    {
        for( const auto &P : Edge[i] )
        {   Show.at<cv::Vec3b>(P) = Col[i]*0.5; }

        cv::Point2f Center;
        float Radius;
        FitCircle( Edge[i], Center, Radius );

        cv::circle( Show, Center, cvRound(Radius), Col[i] );
        cv::drawMarker( Show, Center, Col[i], cv::MARKER_CROSS );

        std::cout << Center << std::endl;
    }
    cv::imshow( "Show", Show );
    cv::waitKey();
    return 0;
}

相关问题