//returns score & score after removing erroenously small rows and cols (null if segmentation algorithm doesn't support this)
        private static Tuple<double, double?> EvaluateByNumRowsAndCols(List<WordsearchImage> wordsearchImages, SegmentationAlgorithm segAlgorithm)
        {
            DefaultLog.Info("Evaluating Wordsearch Image Segmentation by number of rows and cols returned . . .");

            int numCorrect = 0;
            int numCorrectRemoveSmallRowsAndCols = 0;

            //Test the algorithm on each Wordsearch Image
            foreach(WordsearchImage wordsearchImage in wordsearchImages)
            {
                //Register an interest in the Bitmap of the Wordsearch Image
                wordsearchImage.RegisterInterestInBitmap();

                Segmentation proposedSegmentation = segAlgorithm.Segment(wordsearchImage.Bitmap);

                //Keep track of the number of correct
                if(proposedSegmentation.NumRows == wordsearchImage.Rows && 
                    proposedSegmentation.NumCols == wordsearchImage.Cols)
                {
                    numCorrect++;
                }

                //If this segmentation algorithm performs its segmentation with start & end character indices & can therefore have
                //  small rows and cols removed, do it
                if(segAlgorithm is SegmentationAlgorithmByStartEndIndices)
                {
                    Segmentation segRemoveSmallRowsAndCols = proposedSegmentation.RemoveSmallRowsAndCols();

                    if(segRemoveSmallRowsAndCols.NumRows == wordsearchImage.Rows &&
                        segRemoveSmallRowsAndCols.NumCols == wordsearchImage.Cols)
                    {
                        numCorrectRemoveSmallRowsAndCols++;
                    }
                }

                //Clean Up
                wordsearchImage.DeregisterInterestInBitmap();
            }

            DefaultLog.Info("Returned {0}/{1} Wordsearch Segmentations Correctly", numCorrect, wordsearchImages.Count);

            double score = (double)numCorrect / wordsearchImages.Count;
            double? scoreRemoveSmallRowsAndCols = null;
            if(segAlgorithm is SegmentationAlgorithmByStartEndIndices)
            {
                scoreRemoveSmallRowsAndCols = (double)numCorrectRemoveSmallRowsAndCols / wordsearchImages.Count;
                DefaultLog.Info("Returned {0}/{1} Wordsearch Segmentations Correctly after removing small rows and cols", 
                    numCorrectRemoveSmallRowsAndCols, wordsearchImages.Count);
            }

            DefaultLog.Info("Wordsearch Image Segmentation Evaluation Completed");

            return Tuple.Create(score, scoreRemoveSmallRowsAndCols);
        }
Esempio n. 2
0
        internal static WordsearchSolutionEvaluator EvaluateWordsearchBitmap(Bitmap wordsearchBitmap, string[] wordsToFind,
            Dictionary<string, List<WordPosition>> correctSolutions, SegmentationAlgorithm segmentationAlgorithm, 
            bool segmentationRemoveSmallRowsAndCols, SegmentationMethod segmentationMethod,
            Classifier probabilisticRotationCorrectionClassifier, Classifier classifier, Solver wordsearchSolver)
        {
            /*
             * Wordsearch Segmentation
             */
            Segmentation segmentation = segmentationAlgorithm.Segment(wordsearchBitmap);

            //Remove erroneously small rows and columns from the segmentation if that option is specified
            if(segmentationRemoveSmallRowsAndCols)
            {
                segmentation = segmentation.RemoveSmallRowsAndCols();
            }

            /*
             * Wordsearch Rotation Correction
             */
            WordsearchRotation originalRotation;

            //If we're using fixed row & col width
            if (segmentationMethod == SegmentationMethod.FixedWidth)
            {
                originalRotation = new WordsearchRotation(wordsearchBitmap, segmentation.NumRows, segmentation.NumCols);
            }
            else //Otherwise we're using varied row/col width segmentation, use the Segmentation object
            {
                originalRotation = new WordsearchRotation(wordsearchBitmap, segmentation);
            }

            WordsearchRotation rotatedWordsearch = WordsearchRotationCorrection.CorrectOrientation(originalRotation, probabilisticRotationCorrectionClassifier);

            Bitmap rotatedImage = rotatedWordsearch.Bitmap;

            //If the wordsearch has been rotated
            if (rotatedImage != wordsearchBitmap)
            {
                //Update the segmentation

                //If the wordsearch rotation won't have been passed a segmentation
                if (segmentationMethod == SegmentationMethod.FixedWidth)
                {
                    //Make a new fixed width segmentation from the WordsearchRotation
                    segmentation = new Segmentation(rotatedWordsearch.Rows, rotatedWordsearch.Cols,
                        rotatedImage.Width, rotatedImage.Height);
                }
                else
                {
                    //Use the rotated segmentation 
                    segmentation = rotatedWordsearch.Segmentation;
                }
            }

            /*
             * Classification
             */

            //Split image up into individual characters
            Bitmap[,] rawCharImgs = null;

            //If we're using fixed row & col width
            if (segmentationMethod == SegmentationMethod.FixedWidth)
            {
                ResizeBicubic resize = new ResizeBicubic(Constants.CHAR_WITH_WHITESPACE_WIDTH * segmentation.NumCols,
                    Constants.CHAR_WITH_WHITESPACE_HEIGHT * segmentation.NumRows);
                Bitmap resizedImage = resize.Apply(rotatedImage);

                rawCharImgs = SplitImage.Grid(resizedImage, segmentation.NumRows, segmentation.NumCols);

                //Resized image no longer required
                resizedImage.Dispose();
            }
            else //Otherwise we're using varied row/col width segmentation
            {
                rawCharImgs = SplitImage.Segment(rotatedImage, segmentation);

                //If the Segmentation Method is to resize the raw char imgs, resize them
                if (segmentationMethod == SegmentationMethod.VariedWidthWithResize)
                {
                    ResizeBicubic resize = new ResizeBicubic(Constants.CHAR_WITH_WHITESPACE_WIDTH, Constants.CHAR_WITH_WHITESPACE_HEIGHT);

                    for (int i = 0; i < rawCharImgs.GetLength(0); i++)
                    {
                        for (int j = 0; j < rawCharImgs.GetLength(1); j++)
                        {
                            //Only do the resize if it isn't already that size
                            if (rawCharImgs[i, j].Width != Constants.CHAR_WITH_WHITESPACE_WIDTH
                                || rawCharImgs[i, j].Height != Constants.CHAR_WITH_WHITESPACE_HEIGHT)
                            {
                                Bitmap orig = rawCharImgs[i, j];

                                rawCharImgs[i, j] = resize.Apply(orig);

                                //Remove the now unnecessary original/not resized image
                                orig.Dispose();
                            }
                        }
                    }
                }
            }

            //Full sized rotated image no longer required
            rotatedImage.Dispose();

            //Get the part of the image that actually contains the character (without any whitespace)
            Bitmap[,] charImgs = CharImgExtractor.ExtractAll(rawCharImgs);

            //Raw char img's are no longer required
            rawCharImgs.ToSingleDimension().DisposeAll();

            //Perform the classification on all of the images (returns probabilities for each possible class)
            double[][][] classifierOutput = classifier.Classify(charImgs);

            //Actual images of the characters are no longer required
            charImgs.ToSingleDimension().DisposeAll();

            /*
             * Solve Wordsearch
             */
            Solution solution = wordsearchSolver.Solve(classifierOutput, wordsToFind);

            /*
             * Evaluate the Proposed Solution
             */
            WordsearchSolutionEvaluator evaluator = new WordsearchSolutionEvaluator(solution, correctSolutions);

            return evaluator;
        }
Esempio n. 3
0
        //Method to extract a Bitmap of the best match for a wordsearch in an image using a specified Wordsearch Segmentation Algorithm
        //returns null if no Wordsearch candidate could be found in the image
        public static Tuple<List<IntPoint>, Bitmap> ExtractBestWordsearch(Bitmap image, SegmentationAlgorithm segAlg, bool removeSmallRowsAndCols)
        {
            double blobMinDimensionDbl = BLOB_MIN_DIMENSION_PERCENTAGE / 100;
            int minWidth = (int)Math.Ceiling(image.Width * blobMinDimensionDbl); //Round up, so that the integer comaprison minimum will always be correct
            int minHeight = (int)Math.Ceiling(image.Height * blobMinDimensionDbl);

            List<List<IntPoint>> quads = ShapeFinder.Quadrilaterals(image, minWidth, minHeight);

            //Check that there are some quads found to search through for the best wordsearch candidate
            if(quads.Count != 0)
            {
                double bestScore = double.NegativeInfinity;
                List<IntPoint> bestCoords = null;
                Bitmap bestBitmap = null;

                //Search for the Bitmap that yields the best score
                foreach (List<IntPoint> quad in quads)
                {
                    //Extract the Bitmap of this quad
                    QuadrilateralTransformation quadTransform = new QuadrilateralTransformation(quad);
                    Bitmap quadBitmap = quadTransform.Apply(image);

                    //Score this wordsearch candidate
                    double score;
                    //If an InvalidRowsAndCols Exception gets throw by the segmentation 
                    //  (due to there being 0 rows/cols in the returned segmentation)
                    //  this is obviously not a good wordsearch candidate
                    try
                    {
                        Segmentation segmentation = segAlg.Segment(quadBitmap);
                        
                        //If removing erroneously small rows and cols before scoring the segmentation, do so now
                        if(removeSmallRowsAndCols)
                        {
                            segmentation = segmentation.RemoveSmallRowsAndCols();
                        }

                        CandidateScorer scorer = new CandidateScorer(segmentation);
                        score = scorer.WordsearchRecognitionScore;
                    }
                    catch(InvalidRowsAndColsException)
                    {
                        //This is slightly better than the default score of Negative Infinity as any candidate
                        //  (even one with no rows or cols found in it) is better than no candidate whatsoever
                        score = double.MinValue;
                    }

                    //If this score is better than the previous best (don't 
                    //  override equal scores as the list is size ordered and 
                    //  we'll default to the biggest wordsearch as being better)
                    if(score > bestScore)
                    {
                        bestScore = score;
                        bestCoords = quad;

                        //Dispose of the previously best Bitmap resource
                        if(bestBitmap != null)
                        {
                            bestBitmap.Dispose();
                        }
                        //Update the ptr to the new one
                        bestBitmap = quadBitmap;
                    }
                    else
                    {
                        //Clean up
                        quadBitmap.Dispose();
                    }
                }

                return Tuple.Create(bestCoords, bestBitmap);
            }
            else //Otherwise there are no quads to search through
            {
                return null;
            }
        }