これが私がそれをした方法です:
- グレースケールに変換し、ガウスぼかしを適用してノイズを除去します
- otsu thresholding を適用します。フォアとバックグラウンドを分離するのは非常に良いことです。それについて読む必要があります。
- ハフ円変換を適用して円の候補を見つけます。悲しいことに、これにはかなりの調整が必要です。おそらく流域セグメンテーションがより良い代替手段です
- 候補円から ROI を抽出し、黒と白のピクセルの比率を見つけます。
これが私のサンプル結果です:

元の画像に結果を描画すると、次のようになります。

サンプル コードは次のとおりです (C++ で申し訳ありません)。
void findFilledCircles( Mat& img ){
Mat gray;
cvtColor( img, gray, CV_BGR2GRAY );
/* Apply some blurring to remove some noises */
GaussianBlur( gray, gray, Size(5, 5), 1, 1);
/* Otsu thresholding maximizes inter class variance, pretty good in separating background from foreground */
threshold( gray, gray, 0.0, 255.0, CV_THRESH_OTSU );
erode( gray, gray, Mat(), Point(-1, -1), 1 );
/* Sadly, this is tuning heavy, adjust the params for Hough Circles */
double dp = 1.0;
double min_dist = 15.0;
double param1 = 40.0;
double param2 = 10.0;
int min_radius = 15;
int max_radius = 22;
/* Use hough circles to find the circles, maybe we could use watershed for segmentation instead(?) */
vector<Vec3f> found_circles;
HoughCircles( gray, found_circles, CV_HOUGH_GRADIENT, dp, min_dist, param1, param2, min_radius, max_radius );
/* This is just to draw coloured circles on the 'originally' gray image */
vector<Mat> out = { gray, gray, gray };
Mat output;
merge( out, output );
float diameter = max_radius * 2;
float area = diameter * diameter;
Mat roi( max_radius, max_radius, CV_8UC3, Scalar(255, 255, 255) );
for( Vec3f circ: found_circles ) {
/* Basically we extract the region of the circles, and count the ratio of black pixels (0) and white pixels (255) */
Mat( gray, Rect( circ[0] - max_radius, circ[1] - max_radius, diameter, diameter ) ).copyTo( roi );
float filled_percentage = 1.0 - 1.0 * countNonZero( roi ) / area;
/* If more than half is filled, then maybe it's filled */
if( filled_percentage > 0.5 )
circle( output, Point2f( circ[0], circ[1] ), max_radius, Scalar( 0, 0, 255), 3 );
else
circle( output, Point2f( circ[0], circ[1] ), max_radius, Scalar( 255, 255, 0), 3 );
}
namedWindow("");
moveWindow("", 0, 0);
imshow("", output );
waitKey();
}