简体   繁体   中英

Finding Correctly Filled Answer Bubbles with OpenCV

I'm designing an answer sheet scorer and currently only have 1 major issue left to deal with and its the answer bubbles. People can fill these in all sorts of ways Filled Answer Bubbles , I have tried using Cv2.HoughCircles() but it doesn't pick up the weird circles and since you have to specific a radius if its too small or too big it wont pick them up Example of HoughCircles attempt . If I was able to at least get all the circles I could probably use Cv2.CountNonZero() after finding the range of white space to consider an answer good/bad. Any help is appreciated.

Here's the portion that makes them.

//Gray Image
Mat GrayImage = new Mat();
Cv2.CvtColor(startingImage, GrayImage, ColorConversionCodes.BGR2GRAY);
//Making clear
Mat thresholdImage = new Mat();
Cv2.Threshold(GrayImage, thresholdImage, 100, 255, ThresholdTypes.BinaryInv);
Mat guassianBlurImage = new Mat();
Cv2.GaussianBlur(thresholdImage, guassianBlurImage, new OpenCvSharp.Size(5, 5), 0);

Mat cannyImage = new Mat();
int lower = (int)Math.Max(0, (1.0 - 0.33) * 126);
int upper = (int)Math.Min(255, (1.0 + 0.33) * 126);
            Cv2.Canny(guassianBlurImage, cannyImage, lower, upper);

//Finding the Question circles
Mat copy = guassianBlurImage.Clone();
//Image (gray), type, dp, minDist, param1, param2, minRadius, maxRadius
var circles = Cv2.HoughCircles(copy, HoughModes.Gradient, 1, 10, 1, 25, 13, 18);

//Just so we can see the circles
Foreach (var cir in circles)
{
    //Debug.Print(cir.Radius.ToString());
    Cv2.Circle(startingImage, (int)cir.Center.X, (int)cir.Center.Y, (int)cir.Radius, Scalar.Green, 4);
}

I cleaned up my adobe template which had the circles. They were spaced wrongly so I fixed that. This then got me better images of each singular bubble using my custom method to tile the image. Below is how I call my method and a small example of what it produces:

List<Mat> questionMats = new List<Mat>();
utils.TileImage(WarpThresholdImage, 3, 8, false, questionMats);
List<Mat> bubbleMats = new List<Mat>();
int n = 0;
foreach (var mat in questionMats)
{
utils.TileImage(mat, 8, 1, false, bubbleMats, "bubble" + n);
                    n++;
}

在此处输入图像描述

After this I am able to determine the min/max of white pixels using Cv2.CountNonZero() kind of jankly by changing the test image with 3 different versions which have empty bubbles, all filled, and ones that are invalid. I used the following code.

//Sample each bubble get nonzero count find min and max for normal versions of filled and unfilled. Filter based on results
int min = 20000;
int max = 0;
/* These represent the test I did to confirm ranges
 * lowerNonZeroUnFilled = 849;
 * upperNonZeroUnFilled = 1328;
 * lowerNonZeroNormalFilled = 643;
 * upperNonZeroNormalFilled = 1261;
 * lowerNonZeroBadFilled = 602;
 * upperNonZeroBadFilled = 2201; 
 */
for (int i = 0; i < bubbleMats.Count(); i++)
{
    int total = Cv2.CountNonZero(bubbleMats[i]);
    //Empty Spaces
    if (total == 0) { }
    // 600 is the lowest value a filled circle will be
    if(total > 600)
    {
        if(total < min)
        {
            min = total;
        }
        if(max < total)
        {
            max = total;
        }
    //Cv2.ImShow("Bubble" + i + "-" + total, bubbleMats[i]);
    }
}

So this gets me what I want. This probably has issues but I'm confident I can deal with it later on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM