简体   繁体   English

使用 OpenCV 查找正确填充的答案气泡

[英]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.我正在设计一个答题卡记分器,目前只剩下 1 个主要问题需要处理,而且它的答案是气泡。 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 .人们可以用各种方式填充这些填充答案气泡,我尝试过使用 Cv2.HoughCircles() 但它不会拾取奇怪的圆圈,因为如果它太小或太大你必须指定一个半径它不会选择他们起来HoughCircles 尝试的例子 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.如果我能够至少获得所有圆圈,我可能会在找到空白范围后使用 Cv2.CountNonZero() 来考虑答案好/坏。 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.我清理了有圆圈的 adobe 模板。 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.在此之后,我可以使用 Cv2.CountNonZero() 来确定白色像素的最小/最大值,方法是用 3 个不同版本的测试图像更改测试图像,这些版本具有空气泡、全部填充和无效的气泡。 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.这可能有问题,但我相信我以后可以处理它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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