private void btn_processomrsheet_Click(object sender, EventArgs e)
        {
            //empty the resultlist if it is not empty
            if (resultlist.Count > 0)
            {
                resultlist.Clear();
                dgv_omrresult.Rows.Clear();
            }

            if (this.resultlist_dupmerge.Count > 0)
            {
                resultlist_dupmerge.Clear();
            }

            if (list_queslocation.Count > 0)
            {
                list_queslocation.Clear();
            }

            // Scale the image to lower proportions while maintaining aspect ratio (for performance enhancements)
            Bitmap scaledsheet = ImageProcessor.ScaleImage(picbox_displayomrsheet.Image, 900, 1300);

            picbox_displayomrsheet.Image = scaledsheet;
            ts = new TimeSpan(DateTime.Now.Ticks);
            logOutputTerminal("Initializing OMR Extraction Process");

            // Applying Image Pre-Processing
            logOutputTerminal("Applying Pre-Processing on the image");
            Bitmap omrpreprocess_bmp = ImageProcessor.ApplyPreProcessing((Bitmap)scaledsheet, 160);

            picbox_displayomrsheet.Image = omrpreprocess_bmp;
            showTimeStamp("Image Pre-Processing Completed");

            //Apply OMR Extraction
            showTimeStamp("Image Flattening Started");
            Bitmap extractedsheet = OmrProcessor.ExtractPaperFromFlattened(omrpreprocess_bmp, scaledsheet, 5, 5, true);

            picbox_displayomrsheet.Image = extractedsheet;
            showTimeStamp("Image Flattening Completed");

            if (extractedsheet == null)
            {
                MessageBox.Show("Quadrilateral transformation on the preprocess omr sheet failed due to incorrect blob detection", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            //Apply Resizing on the quadrilateral transformed image and bring it to a fixed proportion and resolution
            Bitmap resizedsheet = ImageProcessor.ResizeImage(extractedsheet, 600, 849, 72);

            picbox_displayomrsheet.Image = resizedsheet;

            //Apply preprocessing once again on the extracted sheet
            showTimeStamp("Second PreProcessing Started");
            Bitmap extracted_preprocess = ImageProcessor.ApplyPreProcessing(resizedsheet, 190);

            showTimeStamp("Second PreProcessing Completed");
            List <AForge.Imaging.Blob> bubblesblobs = OmrProcessor.ExtractBubbleCorrespondingBlobs(extracted_preprocess, 8, 8);

            showTimeStamp("Bubble Detection Completed");

            try
            {
                //Generate XML file containing blobs(i.e filled bubbles) information(position, area, center of gravity, fullness etc)
                XMLReaderWriter.WriteNewBubblesDataXML(bubblesblobs, "BubbleData.xml");
            }
            catch (System.Xml.XmlException)
            {
                MessageBox.Show("Failed to Generate a Bubble XML file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            showTimeStamp("Bubble XML Generation Completed");

            if (picbox_displayomrsheet.SizeMode == PictureBoxSizeMode.AutoSize)
            {
                picbox_displayomrsheet.SizeMode = PictureBoxSizeMode.StretchImage;
            }

            /***debug purpose only***/
            //Bitmap bm = new Bitmap(resizedsheet.Size.Width, resizedsheet.Size.Height);
            //Graphics g = Graphics.FromImage(bm);
            //g.DrawImage(resizedsheet, new Rectangle(0, 0, resizedsheet.Width, resizedsheet.Height));
            //Pen redpen = new Pen(Color.Red, 2);

            //foreach (AForge.Imaging.Blob blob in bubblesblobs)
            //{
            //    g.DrawEllipse(redpen, blob.Rectangle.X, blob.Rectangle.Y, blob.Image.Width, blob.Image.Height);
            //}
            //picbox_displayomrsheet.Image = bm;
            /***debug purpose only***/

            Rectangle[] Blocks = new Rectangle[]
            {
                new Rectangle(52, 384, 99, 454),
                new Rectangle(196, 384, 99, 454),
                new Rectangle(340, 384, 99, 454),
                new Rectangle(484, 384, 99, 454)
            };

            //answer fetching algorithm starts

            List <AForge.Imaging.Blob>[] blocksfitbubbles = new List <AForge.Imaging.Blob> [Blocks.Length];
            for (int i = 0; i < Blocks.Length; i++)
            {
                blocksfitbubbles[i] = new List <AForge.Imaging.Blob>();
            }
            //Re-position each blob in corresponding answerblocks array
            foreach (AForge.Imaging.Blob blob in bubblesblobs)
            {
                bool flag = false;
                for (int i = 0; i < Blocks.Length; i++)
                {
                    if (Blocks[i].Contains(new Point((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y)))
                    {
                        blocksfitbubbles[i].Add(blob);
                        flag = true;
                        break;
                    }
                }
                if (flag)
                {
                    continue;
                }
            }

            //identify each bubble in block correspond to which question no and which option(a,b,c,d)
            //for identification of question no, slice the blocks and identify each bubbles falls in which slice
            //for identification of option(a,b,c,d) measure the difference in horizontal length from blob.rectangle.X to its corresponding block rectangle.X

            List <Rectangle>[] blockslices = new List <Rectangle> [Blocks.Length];
            int totalquestionsperblock     = 25;

            for (int i = 0; i < Blocks.Length; i++)
            {
                blockslices[i] = new List <Rectangle>();
            }

            for (int i = 0; i < Blocks.Length; i++)
            {
                Rectangle[] singleblockslices = SliceBlock(Blocks[i], totalquestionsperblock);
                foreach (Rectangle slice in singleblockslices)
                {
                    blockslices[i].Add(slice);
                }
            }

            for (int i = 0; i < Blocks.Length; i++)
            {
                foreach (AForge.Imaging.Blob blob in blocksfitbubbles[i])
                {
                    bool flag = false;
                    foreach (Rectangle slice in blockslices[i])
                    {
                        if (slice.Contains(new Point((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y)))
                        {
                            //bubble is present in this slice
                            //get corresponding question no and its checked option
                            int questionno = ((int)(slice.Bottom - Blocks[i].Top) / slice.Height) + (i * totalquestionsperblock);
                            //MessageBox.Show("Ques: " + questionno + Environment.NewLine + "Bubble X: " + blob.CenterOfGravity.X.ToString() + Environment.NewLine + "Bubble Y: " + blob.CenterOfGravity.Y.ToString());

                            int parallelans_hordiff   = (int)(slice.Width / 4);
                            int ans_hordist_fromslice = blob.Rectangle.Left - slice.Left;

                            int answerchecked = 0;
                            for (int k = 1; k <= 4; k++)
                            {
                                if ((ans_hordist_fromslice > (parallelans_hordiff * (k - 1))) && (ans_hordist_fromslice < (parallelans_hordiff * (k))))
                                {
                                    answerchecked = k;
                                }
                            }
                            resultlist.Add(Tuple.Create(questionno, answerchecked));
                            list_queslocation.Add(Tuple.Create(questionno, slice.Location));
                            //MessageBox.Show("Ques: " + questionno + Environment.NewLine + "Answer: " + answerchecked);
                            flag = true;
                            break;
                        }
                    }
                    if (flag)
                    {
                        continue;
                    }
                }
            }
            //answer fetching algorithm ends

            this.resultlist_dupmerge = resultlist.GroupBy(x => x.Item1, x => x.Item2).ToList();

            foreach (var group in resultlist_dupmerge)
            {
                DataGridViewRow dgrow = new DataGridViewRow();
                dgrow.CreateCells(dgv_omrresult);
                dgrow.Cells[0].Value = group.Key.ToString();

                string ans = "";
                foreach (var element in group)
                {
                    if (element == 1)
                    {
                        ans += "A";
                    }
                    else if (element == 2)
                    {
                        if (ans.Length >= 1)
                        {
                            ans += " ";
                        }
                        ans += "B";
                    }
                    else if (element == 3)
                    {
                        if (ans.Length >= 1)
                        {
                            ans += " ";
                        }
                        ans += "C";
                    }
                    else if (element == 4)
                    {
                        if (ans.Length >= 1)
                        {
                            ans += " ";
                        }
                        ans += "D";
                    }
                    else
                    {
                        ans = "-";
                    }
                }
                dgrow.Cells[1].Value = ans;
                dgv_omrresult.Rows.Add(dgrow);
            }
        }
Beispiel #2
0
        public static Bitmap ExtractPaperFromFlattened(Bitmap bitmap, Bitmap originalimage, int minblobwidth, int minblobheight, bool applyrotation)
        {
            Bitmap   bm = new Bitmap(bitmap.Size.Width, bitmap.Size.Height);
            Graphics g  = Graphics.FromImage(bm);

            g.DrawImage(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
            Pen redpen = new Pen(Color.Red, 2);

            AForge.Math.Geometry.SimpleShapeChecker detectshape = new AForge.Math.Geometry.SimpleShapeChecker();

            if (applyrotation)
            {
                //rotate the image in case it is not properly oriented
                // lock the image
                BitmapData bitmapdata_rot = bitmap.LockBits(
                    new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                    ImageLockMode.ReadWrite, bitmap.PixelFormat);

                BlobCounter blobCounter_rot = new BlobCounter();

                blobCounter_rot.FilterBlobs = true;
                blobCounter_rot.MinHeight   = minblobheight;
                blobCounter_rot.MinWidth    = minblobwidth;

                blobCounter_rot.ProcessImage(bitmapdata_rot);
                bitmap.UnlockBits(bitmapdata_rot);

                Blob[] blob_objects_rot = blobCounter_rot.GetObjects(bitmap, false);

                try
                {
                    foreach (Blob blob in blob_objects_rot)
                    {
                        List <IntPoint> edgePoints = blobCounter_rot.GetBlobsEdgePoints(blob);

                        //detect rotated paper based on left edge rectangular mark
                        List <IntPoint> cornerPoints;
                        if (detectshape.IsQuadrilateral(edgePoints, out cornerPoints))
                        {
                            if (detectshape.CheckPolygonSubType(cornerPoints) == AForge.Math.Geometry.PolygonSubType.Rectangle)
                            {
                                if ((blob.Fullness > 0.7) && (((double)blob.Image.Width / (double)bitmap.Size.Width > 0.025) && ((double)blob.Image.Width / (double)bitmap.Size.Width < 0.037)) && (((double)blob.Image.Height / (double)bitmap.Size.Height > 0.005) && ((double)blob.Image.Height / (double)bitmap.Size.Height < 0.013)))
                                {
                                    // A------p------B      Suppose these are the Coordinates of the Rectangle (image)
                                    // |  2   |  1   |      A,B,C,D are vertices and 1,2,3,4 are quadrants
                                    // s------O------q      p,q,r,s midpoints
                                    // |  3   |  4   |      O is the center of the rectangle
                                    // D------r------C      let the blob be denoted by BL

                                    System.Drawing.Point cent_O = new System.Drawing.Point(bitmap.Size.Width / 2, bitmap.Size.Height / 2);

                                    GraphicsUnit         units = GraphicsUnit.Point;
                                    System.Drawing.Point pnt_A = new System.Drawing.Point((int)bitmap.GetBounds(ref units).X, (int)bitmap.GetBounds(ref units).Y);
                                    System.Drawing.Point pnt_B = new System.Drawing.Point(bitmap.Size.Width, (int)bitmap.GetBounds(ref units).Y);
                                    System.Drawing.Point pnt_C = new System.Drawing.Point(bitmap.Size.Width, bitmap.Size.Height);
                                    System.Drawing.Point pnt_D = new System.Drawing.Point((int)bitmap.GetBounds(ref units).X, bitmap.Size.Height);

                                    System.Drawing.Point midpnt_P = new System.Drawing.Point((pnt_A.X + pnt_B.X) / 2, (pnt_A.Y + pnt_B.Y) / 2);
                                    System.Drawing.Point midpnt_Q = new System.Drawing.Point((pnt_B.X + pnt_C.X) / 2, (pnt_B.Y + pnt_C.Y) / 2);
                                    System.Drawing.Point midpnt_R = new System.Drawing.Point((pnt_C.X + pnt_D.X) / 2, (pnt_C.Y + pnt_D.Y) / 2);
                                    System.Drawing.Point midpnt_S = new System.Drawing.Point((pnt_A.X + pnt_D.X) / 2, (pnt_A.Y + pnt_D.Y) / 2);

                                    System.Drawing.Point blob_CENGRAV = new System.Drawing.Point((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y);

                                    if (ImageProcessor.IsPointInsideRegion(bitmap, pnt_A, midpnt_P, cent_O, midpnt_S, blob_CENGRAV))
                                    {
                                        //do nothing...image is properly oriented
                                        break; //terminate the loop
                                    }
                                    else if (ImageProcessor.IsPointInsideRegion(bitmap, midpnt_P, pnt_B, midpnt_Q, cent_O, blob_CENGRAV))
                                    {
                                        //blob is present in quadrant 1
                                        //rotate image by three CW
                                        bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
                                        originalimage.RotateFlip(RotateFlipType.Rotate270FlipNone);
                                        break;
                                    }
                                    else if (ImageProcessor.IsPointInsideRegion(bitmap, cent_O, midpnt_Q, pnt_C, midpnt_R, blob_CENGRAV))
                                    {
                                        //blob is present in quadrant 4
                                        //rotate image by two CW
                                        bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
                                        originalimage.RotateFlip(RotateFlipType.Rotate180FlipNone);
                                        break;
                                    }
                                    else if (ImageProcessor.IsPointInsideRegion(bitmap, cent_O, midpnt_R, pnt_D, midpnt_S, blob_CENGRAV))
                                    {
                                        //blob is present in quadrant 3
                                        //rotate image by one CW
                                        bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
                                        originalimage.RotateFlip(RotateFlipType.Rotate90FlipNone);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                catch (ArgumentException) { MessageBox.Show("Bilinear Rotational Transformation Failed"); }
            }
            //After applying rotational process, extract the blobs once again to find the circular edge markers

            // lock the image
            BitmapData bitmapdata = bitmap.LockBits(
                new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadWrite, bitmap.PixelFormat);

            // Finding Blobs in the Bitmap image
            BlobCounter blobCounter = new BlobCounter();

            blobCounter.FilterBlobs = true;
            blobCounter.MinHeight   = minblobheight;
            blobCounter.MinWidth    = minblobwidth;

            blobCounter.ProcessImage(bitmapdata);
            Blob[] blobs = blobCounter.GetObjectsInformation();
            bitmap.UnlockBits(bitmapdata);

            //Rectangle[] rects = blobCounter.GetObjectsRectangles();
            Blob[] blob_objects = blobCounter.GetObjects(bitmap, false);

            // Paper Detection happens through the detection of the edge-markers placed on all four
            // edges of the OMR sheet

            List <IntPoint> quad = new List <IntPoint>(); // Store sheet corner locations (if anyone is detected )

            try
            {
                foreach (Blob blob in blob_objects)
                {
                    //detect edge circles
                    if ((double)blob.Rectangle.Width / blob.Rectangle.Height < 1.4 &&
                        (double)blob.Rectangle.Width / blob.Rectangle.Height > .6) // filters out blobs having insanely wrong aspect ratio
                    {
                        List <IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blob);

                        //detect circles only
                        if (detectshape.IsCircle(edgePoints))
                        {
                            //detect filled circles only
                            //the ratio of width(blob)/width(bitmap) should be in range of 0.031 - 0.042
                            if ((blob.Fullness > 0.7) && (((double)blob.Image.Width / (double)bitmap.Width) > 0.031) && (((double)blob.Image.Width / (double)bitmap.Width) < 0.042))
                            {
                                //g.DrawRectangle(redpen, blob.Rectangle);
                                quad.Add(new IntPoint((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y));
                            }
                        }
                    }
                }

                // filter out if wrong blobs pretend to be our blobs.
                if (quad.Count == 4)
                {
                    if (!((quad[0].DistanceTo(quad[1]) / quad[0].DistanceTo(quad[2]) > 0.5) &&
                          (quad[0].DistanceTo(quad[1]) / quad[0].DistanceTo(quad[2]) < 1.5)))
                    {
                        quad.Clear();
                    }
                    else
                    {
                        //rearrange the edge coordinates, if not in proper manner
                        while ((quad[0].X > quad[1].X) || (quad[1].Y > quad[3].Y) || (quad[2].X > quad[3].X) || (quad[0].Y > quad[2].Y))
                        {
                            if (quad[0].X > quad[1].X)
                            {
                                IntPoint tmp = quad[0];
                                quad[0] = quad[1];
                                quad[1] = tmp;
                            }

                            if (quad[1].Y > quad[3].Y)
                            {
                                IntPoint tmp = quad[1];
                                quad[1] = quad[3];
                                quad[3] = tmp;
                            }

                            if (quad[2].X > quad[3].X)
                            {
                                IntPoint tmp = quad[3];
                                quad[3] = quad[2];
                                quad[2] = tmp;
                            }

                            if (quad[0].Y > quad[2].Y)
                            {
                                IntPoint tmp = quad[0];
                                quad[0] = quad[2];
                                quad[2] = tmp;
                            }
                        }
                    }
                }

                if (quad.Count != 4)
                {
                    //call recursively as the sheet is not detected properly
                    //todo: try altering the blob height and width, threshold etc, when program does not find edge markers
                }
                else
                {
                    //rearrange the edges coordinates

                    //  0----1
                    //  |    |
                    //  2----3

                    //interchange 2 and 3
                    IntPoint tp2 = quad[3];
                    quad[3] = quad[2];
                    quad[2] = tp2;

                    //sort the edges for wrap operation
                    QuadrilateralTransformation wrap = new QuadrilateralTransformation(quad, 2100, 2970);
                    wrap.UseInterpolation        = false;
                    wrap.AutomaticSizeCalculaton = true;
                    Bitmap wrappedoriginal = wrap.Apply(originalimage);
                    return(wrappedoriginal);
                }
            }
            catch (ArgumentException) { MessageBox.Show("No Blobs Found"); }
            return(null);
        }