예제 #1
0
        // Persumed to be rotated correctly (but not yet cropped)
        public static byte[] Read(byte[,] imageData)
        {
            byte[,] array = new byte[19, 4];

            if (imageData != null)
            {
                if (imageData.GetLength(0) > 0 && imageData.GetLength(1) > 0)
                {
                    imageData = Processing.FloatToByte(Processing.Gaussian(imageData));
                    imageData = Processing.Binarize(imageData, 200);

                    int minBlack = Math.Min(4, Math.Min(imageData.GetLength(0) / 19, imageData.GetLength(1) / 4));

                    Point p1 = new Point(-1, -1);
                    Point p2 = new Point(-1, -1);

                    for (int i = 0; i < (imageData.GetLength(0) / 2); i++)
                    {
                        int sum1 = 0;
                        int sum2 = 0;
                        for (int j = 0; j < imageData.GetLength(1); j++)
                        {
                            sum1 += imageData[i, j] > 0 ? 0 : 1;
                            sum2 += imageData[imageData.GetLength(0) - i - 1, j] > 0 ? 0 : 1;
                        }
                        if (p1.X == -1 && sum1 >= minBlack)
                        {
                            p1.X = i;
                        }
                        if (p2.X == -1 && sum2 >= minBlack)
                        {
                            p2.X = imageData.GetLength(0) - i - 1;
                        }
                        if (p1.X != -1 && p2.X != -1)
                        {
                            break;
                        }
                    }

                    for (int j = 0; j < (imageData.GetLength(1) / 2); j++)
                    {
                        int sum1 = 0;
                        int sum2 = 0;
                        for (int i = 0; i < imageData.GetLength(0); i++)
                        {
                            sum1 += imageData[i, j] > 0 ? 0 : 1;
                            sum2 += imageData[i, imageData.GetLength(1) - j - 1] > 0 ? 0 : 1;
                        }
                        if (p1.Y == -1 && sum1 >= minBlack)
                        {
                            p1.Y = j;
                        }
                        if (p2.Y == -1 && sum2 >= minBlack)
                        {
                            p2.Y = imageData.GetLength(1) - j - 1;
                        }
                        if (p1.Y != -1 && p2.Y != -1)
                        {
                            break;
                        }
                    }

                    if (p1.X != -1 && p1.Y != -1 && p2.X != -1 && p2.Y != -1)
                    {
                        imageData = Processing.Crop(imageData, new Rectangle(p1.X, p1.Y, p2.X - p1.X + 1, p2.Y - p1.Y + 1));

                        int blockWidth  = (int)Math.Floor((double)imageData.GetLength(0) / 19);
                        int blockHeight = (int)Math.Floor((double)imageData.GetLength(1) / 4);

                        int newWidth  = blockWidth * 19;
                        int newHeight = blockHeight * 4;

                        imageData = Processing.Resize(imageData, newWidth, newHeight);
                        int threshold = (blockWidth * blockHeight) / 2;

                        for (int j = 0; j < 4; j++)
                        {
                            for (int i = 0; i < 19; i++)
                            {
                                double sum = 0;
                                for (int k = 0; k < blockHeight; k++)
                                {
                                    for (int l = 0; l < blockWidth; l++)
                                    {
                                        sum += (int)imageData[(i * blockWidth) + l, (j * blockHeight) + k] > 1 ? 1 : 0;
                                    }
                                }
                                array[i, j] = sum < threshold ? (byte)0 : (byte)1;
                            }
                        }
                    }
                    else
                    {
                        Logger.LogLow("Could not crop barcode.");
                    }
                }
            }
            return(ReadFrom2DArray(array));
        }
예제 #2
0
 public static byte[] Read(Image image)
 {
     return(Read(Processing.ImageToByte(image)));
 }
예제 #3
0
        private static List <EllipticalFit> FindPointsByCanny(int numberToFind,
                                                              int currentCross, byte[,] cannyData, int searchSize, ref int searchShift, ref int attempt, ref bool ellipsesValid)
        {
            searchSize = (int)(Math.Round((double)searchSize / 2) * 2); // has to be even
            int shiftX     = searchShift == 1 || searchShift == 3 ? (int)(searchSize / 4) : 0;
            int shiftY     = searchShift == 2 || searchShift == 3 ? (int)(searchSize / 4) : 0;
            int searchJump = searchSize / 2;
            int w          = cannyData.GetLength(0);
            int h          = cannyData.GetLength(1);

            // Create a list with areas to look => cannot be reused due to (even very small) differences in size of scans
            List <Point> searchPoints = new List <Point>();

            for (int x = shiftX; x < w; x += searchJump)
            {
                int searchX = x;
                if (x + searchSize >= w)
                {
                    searchX = w - searchSize;
                    x       = w;
                }
                for (int y = shiftY; y < h; y += searchJump)
                {
                    int searchY = y;
                    if (y + searchSize >= h)
                    {
                        searchY = h - searchSize;
                        y       = h;
                    }
                    searchPoints.Add(new Point(searchX, searchY));
                }
            }

            // Assumed that the paper is oriented right => faster
            searchPoints.Sort(delegate(Point p1, Point p2) { return(Math.Min(w - p1.X, h - p1.Y).CompareTo(Math.Min(w - p2.X, h - p2.Y))); });

            /*
             *
             *
             *
             *
             ********
             */

#if DEBUG
            var img = Processing.ByteToImage(cannyData);
#endif

            // Crop small rectangles from canny
            List <EllipticalFit> ellipseList = new List <EllipticalFit>();
            for (int i = 0; i < searchPoints.Count; i++)
            {
                // Fit an ellipse
                EllipticalFit fit = null;
                RectangleF    boundingRectangle = new RectangleF();
                try
                {
                    var searchRectangle = new Rectangle(searchPoints[i].X, searchPoints[i].Y, searchSize, searchSize);
                    fit = new EllipticalFit(cannyData, 255, searchRectangle);

#if DEBUG
                    using (Graphics g = Graphics.FromImage(img))
                    {
                        g.DrawRectangle(new Pen(Color.Yellow), searchRectangle);
                    }
#endif

                    // Empty search areas are already ignored in fit
                    boundingRectangle = fit.BoundingRectangle();

                    // If a valid ellipse is found
                    if (!double.IsNaN(boundingRectangle.X) && boundingRectangle.X > 0 &&
                        !double.IsNaN(boundingRectangle.Y) && boundingRectangle.Y > 0 &&
                        boundingRectangle.Width < searchSize && boundingRectangle.Height < searchSize &&
                        boundingRectangle.Width > .25 * searchSize && boundingRectangle.Height > .25 * searchSize &&
                        boundingRectangle.Width > 15 && boundingRectangle.Height > 15)
                    {
                        var    observed   = Processing.Crop(cannyData, searchRectangle);
                        var    moments    = Processing.Moments(observed);
                        double squareness = Math.Min((double)boundingRectangle.Width, (double)boundingRectangle.Height) / Math.Max((double)boundingRectangle.Width, (double)boundingRectangle.Height);

                        // Ellipse has to be more or less round, based on enough points and have some variance in each direction
                        if (squareness > .9 && moments[0] >= 8 && moments[3] > 0 && moments[4] > 0)
                        {
                            // Fill the ellipse with floodfill
                            var observedFilled = Processing.FloodFill(observed, 1, new PointF(
                                                                          boundingRectangle.X - searchRectangle.X + (boundingRectangle.Width / 2),
                                                                          boundingRectangle.Y - searchRectangle.Y + (boundingRectangle.Height / 2)));

                            var momentsFilled = Processing.Moments(observedFilled);

                            if (momentsFilled[1] > (boundingRectangle.Width / 2) && momentsFilled[1] < searchRectangle.Width - (boundingRectangle.Width / 2) &&
                                momentsFilled[2] > (boundingRectangle.Height / 2) && momentsFilled[2] < searchRectangle.Height - (boundingRectangle.Height / 2))
                            {
                                // Create a white circle of similar size
                                var expectedFilled = Processing.WhiteCircle(observedFilled.GetLength(0), observedFilled.GetLength(1), new RectangleF(
                                                                                boundingRectangle.X - searchRectangle.X, boundingRectangle.Y - searchRectangle.Y, boundingRectangle.Width, boundingRectangle.Height));

                                // Compare the result of the floodfill with the white circle
                                fit.DifferenceFromCircle = Processing.AverageDifference(observedFilled, expectedFilled);

                                // Only if ellipse was closed they will be similar
                                if (fit.DifferenceFromCircle < .1)
                                {
                                    ellipseList = AddWithoutOverlap(ellipseList, fit);
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogLow(ex.Message);
                }
                if (ellipseList.Count == numberToFind)
                {
                    break;
                }
            }

            ellipsesValid = EllipsePointsAreValid(Program.Test, ellipseList); // Count and colinearity have to be correct

            /*
             * using (Graphics g = Graphics.FromImage(img))
             * {
             *  for (int i = 0; i < ellipseList.Count; i++)
             *  {
             *      g.DrawLine(new Pen(Color.Blue, 2), ellipseList[i].MidPoint().X - 15, ellipseList[i].MidPoint().Y, ellipseList[i].MidPoint().X + 15, ellipseList[i].MidPoint().Y);
             *      g.DrawLine(new Pen(Color.Blue, 2), ellipseList[i].MidPoint().X, ellipseList[i].MidPoint().Y - 15, ellipseList[i].MidPoint().X, ellipseList[i].MidPoint().Y + 15);
             *  }
             * }
             * img.Save(Program.UserSettings.defaultDirectory + "\\img\\"
             + Convert.ToString(DateTime.Now.Minute)
             + " - " + Convert.ToString(DateTime.Now.Second)
             + " - " + Convert.ToString(DateTime.Now.Millisecond)
             + " - " + currentCross
             + " - " + searchSize
             + " - " + searchShift
             + " - " + attempt
             + " - " + ellipseList.Count
             + " - " + ellipsesValid
             + ".bmp");
             */

            Logger.LogHigh(
                "Attempt:\t" + attempt +
                "\tCanny:\t" + currentCross +
                "\tSearch size:\t" + searchSize +
                "\tSearch shift:\t" + searchShift +
                "\tEllipses found:\t" + ellipseList.Count +
                "\tEllipses valid:\t" + ellipsesValid);

            // If not all ellipses are found, retry with shifted searcharea
            if (ellipseList.Count != numberToFind && searchShift < 3)
            {
                searchShift++;
                attempt++;
                ellipseList = FindPointsByCanny(numberToFind, currentCross, cannyData, searchSize, ref searchShift, ref attempt, ref ellipsesValid);
            }

            return(ellipseList);
        }
예제 #4
0
        public void CalculateInitialItemAltsCheckedState(int pageNumber, float sure, float doubt)
        {
            if (ItemAltsCheckedState == null)
            {
                ItemAltsCheckedState = new ItemCheckedState[Program.Test.Paper.Blocks.X, Program.Test.Paper.Blocks.Y];
            }
            var itemAlternativeOnLocation = Program.Test.Pages[pageNumber].ItemAlternativeOnLocation();

            for (int i = 0; i < Program.Test.Paper.Blocks.X; i++)
            {
                for (int j = 0; j < Program.Test.Paper.Blocks.Y; j++)
                {
                    if (itemAlternativeOnLocation[i, j])
                    {
                        var crop = Processing.Crop(ScannedImage, ItemAltRectangle(i, j));
                        int w    = crop.GetLength(0);
                        int h    = crop.GetLength(1);

                        double val = 0;
                        Single w2  = (Single)(w - 1) / 2;
                        Single h2  = (Single)(h - 1) / 2;
                        Single max = w2 * w2 * h2 * h2;
                        double old = 0;
                        double nw  = 0;
                        for (int x = 0; x < w; x++)
                        {
                            for (int y = 0; y < h; y++)
                            {
                                val += crop[x, y] * (max - ((x - w2) * (x - w2) * (y - h2) * (y - h2)));;
                                old += crop[x, y];
                                nw  += crop[x, y] * (max - ((x - w2) * (x - w2) * (y - h2) * (y - h2)));
                            }
                        }
                        val /= 2.55f * ((w * h * max) - (
                                            (w * (w - 1) * (w + 1) / 12) *
                                            (h * (h - 1) * (h + 1) / 12)
                                            ));
                        ColorDistribution.Add(val);
                        ItemCheckedState state = ItemCheckedState.Unknown;

                        if (val < sure)
                        {
                            state = ItemCheckedState.Checked;
                        }
                        else if (val < doubt)
                        {
                            state = ItemCheckedState.Doubt;
                        }
                        else
                        {
                            state = ItemCheckedState.Unchecked;
                        }
                        ItemAltsCheckedState[i, j] = state;
                    }
                    else
                    {
                        ItemAltsCheckedState[i, j] = ItemCheckedState.Unavailable;
                    }
                }
            }
        }
예제 #5
0
        private static List <PointF> CalibrationPoints(Test test, byte[,] image, int searchSize)
        {
            List <PointF> calPoints     = new List <PointF>();
            bool          solutionFound = false;

            int max        = 0;
            int indexOfMax = 0;

            for (int i = 0; i < 100; i++)
            {
                if (Settings.SuccessFullCanny[i] > max)
                {
                    max        = Settings.SuccessFullCanny[i];
                    indexOfMax = i;
                }
            }

            int currentCross = indexOfMax;

            if (currentCross == 0)
            {
                currentCross = Program.UserSettings.calibrationCannyStart;
            }

            int attempt      = 1; // Canny -> SearchSize -> SearchShift
            int cannyAttempt = 1; // Don't start at zero, otherwise adds zero to currentCross first time

            // Try different levels of canny edge detection until the number of points matches
            int[] best       = { -1, -1 };
            int[] secondBest = { -1, -1 };
            bool  goingDown  = false;
            bool  goingUp    = false;

            while (!solutionFound && currentCross >= 0 && currentCross < 100 && cannyAttempt <= Program.UserSettings.maximumCannyAttempts)
            {
                calPoints.Clear();
                var cannyData = Processing.EdgeDetection(image, currentCross);

                // Due to edgedetection, cannyData is a bit smaller than original!
                float correctionX = (float)(image.GetLength(0) - cannyData.GetLength(0)) / 2;
                float correctionY = (float)(image.GetLength(1) - cannyData.GetLength(1)) / 2;

                int   searchSizeDiffCount = 0;
                int[] searchSizeDiff      = { 0, 2, -4, 6, -8, 10, -12, 14, -16, 18, -20, 22 };

                List <EllipticalFit> ellipsesFound = new List <EllipticalFit>();
                bool ellipsesValid = false;
                while (!ellipsesValid && searchSizeDiffCount < searchSizeDiff.GetLength(0))
                {
                    int adjustedSearchSize = searchSize + searchSizeDiff[searchSizeDiffCount];
                    int searchShift        = 0;
                    ellipsesFound = FindPointsByCanny(test.Paper.CalibrationCircles.X + test.Paper.CalibrationCircles.Y - 1,
                                                      currentCross, cannyData, adjustedSearchSize, ref searchShift, ref attempt, ref ellipsesValid);
                    searchSizeDiffCount++;
                    attempt++;
                }

                if (ellipsesValid)
                {
                    foreach (var fit in ellipsesFound)
                    {
                        var mid = fit.MidPoint();
                        calPoints.Add(new PointF(mid.X + correctionX, mid.Y + correctionY));
                    }
                    calPoints.Sort(delegate(PointF pc1, PointF pc2) { return(Distance(pc1, new PointF(0, image.GetLength(1))).CompareTo(Distance(pc2, new PointF(0, image.GetLength(1))))); });

                    /* Results in this order:
                     *      G
                     |
                     |      F
                     |
                     |      E
                     |
                     |  A-B-C-D
                     */
                    Settings.SuccessFullCanny[currentCross]++;
                    solutionFound = true;
                }
                else
                {
                    if (best[0] == -1)
                    {
                        best[0] = currentCross;
                        best[1] = ellipsesFound.Count;
                    }
                    else
                    {
                        if (ellipsesFound.Count > best[1])
                        {
                            secondBest[0] = best[0];
                            secondBest[1] = best[1];
                            best[0]       = currentCross;
                            best[1]       = ellipsesFound.Count;
                        }
                        else if (ellipsesFound.Count > secondBest[1])
                        {
                            secondBest[0] = currentCross;
                            secondBest[1] = ellipsesFound.Count;
                        }
                    }

                    if (secondBest[1] < 0 || best[1] == secondBest[1]) // No specific direction prefered yet -> try both ways
                    {
                        currentCross += (int)Math.Pow(-1, cannyAttempt) * cannyAttempt * 3;
                    }
                    else // One direction favored
                    {
                        if (best[0] < secondBest[0] || goingDown)
                        {
                            currentCross -= 3;
                            goingDown     = true;
                        }
                        else if (best[0] > secondBest[0] || goingUp)
                        {
                            currentCross += 3;
                            goingUp       = true;
                        }
                    }
                    cannyAttempt++;
                }
            }

            return(calPoints);
        }