英文:
Removing Noise and Detecting Circle in Spotlight Using OpenCV
问题
我一直在尝试使用OpenCV在聚光灯图像中检测圆圈,并且有各种我正在处理的图片,通常看起来像这四张图片之一:
经过一些图像处理(使用阈值、模糊化),我已经让图片看起来像这样:
然而,尝试使用HoughCircles函数,即使在玩弄了一段时间后似乎也无法正常工作。在使用HoughCircles时,我是不是忽略了什么,或者有没有比使用HoughCircles更好的方法来检测这些圆圈?
我的当前代码:
import cv2 as cv 
import numpy as np 
img = cv.imread('image.jpg', cv.IMREAD_GRAYSCALE)
img = cv.medianBlur(img,33)
assert img is not None, "file could not be read, check with os.path.exists()"
image = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv.THRESH_BINARY,73,2)
blur = cv.blur(image,(5,5))
circles = cv.HoughCircles(blur,cv.HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=30)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
    # draw the outer circle
    cv.circle(circles,(i[0],i[1]),i[2],(0,255,0),2)
    # draw the center of the circle
    cv.circle(circles,(i[0],i[1]),2,(0,0,255),3)
cv.imshow('Objects Detected',blur)
cv.waitKey(0)
英文:
I have been trying to detect circles in spotlight images using OpenCV and have a variety of pictures I am working with, generally looking something like these 4 images:
Following some image processing (using a threshold, blurring) I have gotten the images to look something like this:

However, trying to use the HoughCircles function, even after playing around with it for a while seems to not work. Is there something that I am glossing over while using Hough Cicles, or is there a better way to detect the circles than using HoughCircles?
My current code:
import cv2 as cv 
import numpy as np 
img = cv.imread('image.jpg', cv.IMREAD_GRAYSCALE)
img = cv.medianBlur(img,33)
assert img is not None, "file could not be read, check with os.path.exists()"
image = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv.THRESH_BINARY,73,2)
blur = cv.blur(image,(5,5))
circles = cv.HoughCircles(blur,cv.HOUGH_GRADIENT,1,20,
                            param1=50,param2=30,minRadius=30)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
    # draw the outer circle
    cv.circle(circles,(i[0],i[1]),i[2],(0,255,0),2)
    # draw the center of the circle
    cv.circle(circles,(i[0],i[1]),2,(0,0,255),3)
cv.imshow('Objects Detected',blur)
cv.waitKey(0)
答案1
得分: 1
。结果的示例(注意:我使用了缩小尺寸的图像作为输入):
- 阈值 = 167
 - 二值化结果以下图中的红色部分表示。
 
英文:
If you can't use fix threshold value, one of the simplest way is to try possible thresholds and choose the one that looks good.
In following sample code(C++), threshold value are tried in ascending order until the binalization result shape became "enoughly circle".
//Circle fitting (Simple Least Square)
void FitCircle( const std::vector<cv::Point> &Ps, cv::Point2f &C, float &r )
{
	cv::Mat A( (int)Ps.size(), 3, CV_32F );
	cv::Mat B( (int)Ps.size(), 1, CV_32F );
	for( int i=0; i<Ps.size(); ++i )
	{
		const auto &P = Ps[i];
		A.at<float>( i,0 ) = (float)P.x;
		A.at<float>( i,1 ) = (float)P.y;
		A.at<float>( i,2 ) = 1.0f;
		B.at<float>( i ) = (float)(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( "SpotLight.png", cv::IMREAD_GRAYSCALE );
	if( SrcImg.empty() )return 0;
	cv::imshow( "Src", SrcImg );
	//Decide threshold value range [Min,Max) to try
	int Min=0, Max=255;
	{
		unsigned int Hist[256] = { 0 };
		for( int y=0; y<SrcImg.rows; ++y )
		{
			const unsigned char *p = SrcImg.ptr<unsigned char>(y);
			for( int x=0; x<SrcImg.cols; ++x, ++p )
			{	++Hist[ *p ];	}
		}
		{//Decide Min
			unsigned int Thresh = cvRound( SrcImg.rows * SrcImg.cols * 0.9 );
			unsigned int Sum = 0;
			while( Sum < Thresh ){	Sum += Hist[Min];	++Min;	}
		}
		//Decide Max
		while( Hist[Max]==0 ){	--Max;	}
	}
	
	//Try for each threshold
	for( int Thresh=Min; Thresh<Max-1; ++Thresh )
	{
		//Binalize
		cv::Mat Bin;
		cv::threshold( SrcImg, Bin, Thresh, 255, cv::THRESH_BINARY );
		//Fit circle to largest contour
		//to evaluate how close the binalization result shape is to a circle
		cv::Point2f Center;
		float Radius;
		double FittingErr = 0;
		{
			//Extract Largest Contour
			std::vector< std::vector<cv::Point> > Conts;
			cv::findContours( Bin, Conts, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE );
			if( Conts.empty() )continue;
			int iLargestCont = 0;
			double MaxArea = 0;
			for( int i=0; i<Conts.size(); ++i )
			{
				double Area = cv::contourArea( Conts[i] );
				if( Area >= MaxArea ){	MaxArea = Area;	iLargestCont = i;	}
			}
			//Fit circle to the Contour and evaluate the fitting result
			FitCircle( Conts[iLargestCont], Center, Radius );
			for( const auto &P : Conts[iLargestCont] )
			{
				double dx = (double)P.x - Center.x;
				double dy = (double)P.y - Center.y;
				double e = fabs( sqrt(dx*dx + dy*dy) - Radius );
				FittingErr += e;
			}
			FittingErr /= Conts[iLargestCont].size();
		}
		//Check if Contour shape is "enoughly circle"
		if( FittingErr <= 0.5 )
		{	//Show Result
			cv::Mat Show;
			std::cout << "Thresh = " << Thresh << std::endl;
			cv::cvtColor( SrcImg, Show, cv::COLOR_GRAY2BGR );
			Show.setTo( cv::Scalar(0,0,255), Bin );
			cv::imshow( "Result", Show );
			cv::waitKey();
			return 0;
		}
	}
	//
	std::cout << "(All try Failed)" << std::endl;
	return 0;
}
Result of this sample (Note : I used shrinked size image to input) :
- Threshold value = 167
 - Binalized Result is represented as Red color part in below.
 
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。






评论