Beispiel #1
0
        public void BoardAnalyse()
        {
            Console.WriteLine("xxxxxxxxxxxxxxx");

            TicTacToe t = new TicTacToe(
                "X.X\n" +
                "O.O\n" +
                "X..\n");

            RowAnalysis r = t.analyse_board(1);

            Console.WriteLine(t.ToString());
            Console.WriteLine(r.ToString(false));
            Console.WriteLine(r.result[0].ToString());

            //
            Assert.That(r.result.Capacity, Is.EqualTo(8));

            Assert.That(r.result[0].rank, Is.EqualTo(1f));
            Assert.That(r.result[0].position, Is.EqualTo(0));
            Assert.That(r.result[0].direction, Is.EqualTo(0));
            Assert.That(r.result[0].index, Is.EqualTo(0));
            Assert.That(r.result[0].suggestion, Is.EqualTo(1));

            Assert.That(r.result[1].rank, Is.EqualTo(1.0f / 3));
            Assert.That(r.result[1].position, Is.EqualTo(0));
            Assert.That(r.result[1].direction, Is.EqualTo(45));
            Assert.That(r.result[1].index, Is.EqualTo(0));
            Assert.That(r.result[1].suggestion, Is.EqualTo(2));

            Assert.That(r.result[2].rank, Is.EqualTo(1.0f / 3));
            Assert.That(r.result[2].position, Is.EqualTo(0));
            Assert.That(r.result[2].direction, Is.EqualTo(45));
            Assert.That(r.result[2].index, Is.EqualTo(0));
            Assert.That(r.result[2].suggestion, Is.EqualTo(2));



            /*Assert.That(r.result[1].rank, Is.EqualTo(1));
             * Assert.That(r.result[2].rank, Is.EqualTo(0.999f));
             * Assert.That(r.result[7].rank, Is.EqualTo(0));
             *
             * Assert.That(r.result[0].rank, Is.EqualTo(1));
             * Assert.That(r.result[1].rank, Is.EqualTo(1));
             * Assert.That(r.result[2].rank, Is.EqualTo(0.999f));
             * Assert.That(r.result[7].rank, Is.EqualTo(0));
             */
        }
Beispiel #2
0
        public void TestAnalysis()
        {
            int[] row = { 1, 1, 0 };

            RowAnalysis r = new RowAnalysis(1, 3);

            r.analyse(row, 0, 0);
            //Console.WriteLine(r.ToString());
            Assert.That(r.result[0].index, Is.EqualTo(0));
            Assert.That(r.result[0].suggestion, Is.EqualTo(2));
            Assert.That(r.result[0].rank, Is.EqualTo(1));
            Assert.That(r.result[0].position, Is.EqualTo(0));
            Assert.That(r.result[0].direction, Is.EqualTo(0));

            r = new RowAnalysis(2, 3);
            r.analyse(row, 1, 90);
            Console.WriteLine(r.ToString());
            Assert.That(r.result[0].index, Is.EqualTo(0));
            Assert.That(r.result[0].suggestion, Is.EqualTo(2));
            Assert.That(r.result[0].rank, Is.EqualTo(0.999f));
            Assert.That(r.result[0].position, Is.EqualTo(1));
            Assert.That(r.result[0].direction, Is.EqualTo(90));
        }
Beispiel #3
0
        public static readonly int PIXEL_BYTE_WIDTH     = 4; // determined by PixelFormat.Format32bppArgb; http://www.bobpowell.net/lockingbits.htm
        public override void DoWork()
        {
            /* TODO: OPTIMIZATIONS:
             * - compute surfaceNoiseLevel based on image analysis
             * - make debug output to log optional
             * - surface/transition decorations (biggest problem is that there can be a variable number...only for first edge to start?)
             * - for marked image, save decorations...don't copy/paint_on image
             */

            DateTime startTime = DateTime.Now;

            TestExecution().LogMessageWithTimeFromTrigger("[" + Name + "] started at " + startTime + Environment.NewLine);

            int edgeLocation = -1;

            int minSurfaceSize        = Math.Max(1, (int)mMinSurfaceSize.ValueAsLong());
            int surfaceNoiseThreshold = (int)mSurfaceNoiseThreshold.ValueAsLong();

            int startXOffset;
            int startYOffset;
            int searchDirOffset;
            int searchDirIncrement;
            int numSearchDirRows;
            int searchDirXIncrement;
            int searchDirYIncrement;
            int rowWidth;
            int rowDirXIncrement;
            int rowDirYIncrement;

            switch (mSearchDirection)
            {
            case Direction.Down:
                startXOffset        = mSearchArea.Left;
                startYOffset        = mSearchArea.Top;
                numSearchDirRows    = mSearchArea.Bottom - mSearchArea.Top + 1;
                searchDirXIncrement = 0;
                searchDirYIncrement = 1;
                rowWidth            = mSearchArea.Right - mSearchArea.Left + 1;
                rowDirXIncrement    = 1;
                rowDirYIncrement    = 0;
                searchDirOffset     = startYOffset;
                searchDirIncrement  = 1;
                break;

            case Direction.Up:
                startXOffset        = mSearchArea.Left;
                startYOffset        = mSearchArea.Bottom;
                numSearchDirRows    = mSearchArea.Bottom - mSearchArea.Top + 1;
                searchDirXIncrement = 0;
                searchDirYIncrement = -1;
                rowWidth            = mSearchArea.Right - mSearchArea.Left + 1;
                rowDirXIncrement    = 1;
                rowDirYIncrement    = 0;
                searchDirOffset     = startYOffset;
                searchDirIncrement  = -1;
                break;

            case Direction.Right:
                startXOffset        = mSearchArea.Left;
                startYOffset        = mSearchArea.Top;
                numSearchDirRows    = mSearchArea.Right - mSearchArea.Left + 1;
                searchDirXIncrement = 1;
                searchDirYIncrement = 0;
                rowWidth            = mSearchArea.Bottom - mSearchArea.Top + 1;
                rowDirXIncrement    = 0;
                rowDirYIncrement    = 1;
                searchDirOffset     = startXOffset;
                searchDirIncrement  = 1;
                break;

            case Direction.Left:
                startXOffset        = mSearchArea.Right;
                startYOffset        = mSearchArea.Top;
                numSearchDirRows    = mSearchArea.Right - mSearchArea.Left + 1;
                searchDirXIncrement = -1;
                searchDirYIncrement = 0;
                rowWidth            = mSearchArea.Bottom - mSearchArea.Top + 1;
                rowDirXIncrement    = 0;
                rowDirYIncrement    = 1;
                searchDirOffset     = startXOffset;
                searchDirIncrement  = -1;
                break;

            case Direction.NotDefined:
                throw new ArgumentException("Search direction not defined for " + Name);

            default:
                throw new ArgumentException("Unexpected search direction for " + Name);
            }

            if (mSourceImage.Bitmap == null)
            {
                TestExecution().LogMessage("ERROR: source image for '" + Name + "' does not exist.");
            }
            else
            {
                Bitmap     sourceBitmap     = SourceImage.Bitmap;
                BitmapData sourceBitmapData = null;

                int    x = 0;
                int    y = 0;
                double sumIntensityWithinRow = 0;
                double sumVariationWithinRow = 0;
                //double overallSumIntensity = 0;
                //double overallSumVariation = 0;

                RowAnalysis[] rows                 = new RowAnalysis[numSearchDirRows];
                int[]         rowValues            = new int[rowWidth];
                bool          analyzedSuccessfully = false;
                try
                {
                    sourceBitmapData = sourceBitmap.LockBits(new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, PIXEL_FORMAT);
                    int sourceStride       = sourceBitmapData.Stride;
                    int sourceStrideOffset = sourceStride - (sourceBitmapData.Width * PIXEL_BYTE_WIDTH);

                    unsafe // see http://www.codeproject.com/csharp/quickgrayscale.asp?df=100&forumid=293759&select=2214623&msg=2214623
                    {
                        byte *sourcePointer;
                        byte *sourcePointer2;

                        int variation;
                        for (int searchDirIndex = 0; searchDirIndex < numSearchDirRows; searchDirIndex++)
                        {
                            RowAnalysis rowAnalysis = new RowAnalysis(searchDirIndex);
                            rows[searchDirIndex] = rowAnalysis;
                            //if (searchDirIndex > 0)
                            //{
                            //    rowAnalysis.OverallAverageIntensityBeforeRow = overallSumIntensity / searchDirIndex;
                            //    rowAnalysis.OverallVariationBeforeRow  = overallSumVariation / searchDirIndex;
                            //}
                            sumIntensityWithinRow = 0;
                            sumVariationWithinRow = 0;
                            for (int widthIndex = 0; widthIndex < rowWidth; widthIndex++)
                            {
                                x = startXOffset + (searchDirIndex * searchDirXIncrement) + (widthIndex * rowDirXIncrement);
                                y = startYOffset + (searchDirIndex * searchDirYIncrement) + (widthIndex * rowDirYIncrement);

                                sourcePointer  = (byte *)sourceBitmapData.Scan0;                                                    // init to first byte of image
                                sourcePointer += (y * sourceStride) + (x * PIXEL_BYTE_WIDTH);                                       // adjust to current point
                                pixelGrayValue = (int)(0.3 * sourcePointer[2] + 0.59 * sourcePointer[1] + 0.11 * sourcePointer[0]); // Then, add 30% of the red value, 59% of the green value, and 11% of the blue value, together. .... These percentages are chosen due to the different relative sensitivity of the normal human eye to each of the primary colors (less sensitive to green, more to blue).
                                // http://www.bobpowell.net/grayscale.htm
                                // https://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=440425&SiteID=1

                                // check pixel "behind"
                                sourcePointer2     = sourcePointer - (searchDirXIncrement * PIXEL_BYTE_WIDTH) - (searchDirYIncrement * sourceStride);
                                prevPixelGrayValue = (int)(0.3 * sourcePointer2[2] + 0.59 * sourcePointer2[1] + 0.11 * sourcePointer2[0]); // Then, add 30% of the red value, 59% of the green value, and 11% of the blue value, together. .... These percentages are chosen due to the different relative sensitivity of the normal human eye to each of the primary colors (less sensitive to green, more to blue).

                                //pixelColor = mSourceImage.Image.GetPixel(x, y);
                                //pixelGrayValue = (int)((pixelColor.R + pixelColor.G + pixelColor.B) / 3.0);
                                //prevPixelColor = mSourceImage.Image.GetPixel(x - searchDirXIncrement, y - searchDirYIncrement);
                                //prevPixelGrayValue = (int)((prevPixelColor.R + prevPixelColor.G + prevPixelColor.B) / 3.0);
                                variation              = pixelGrayValue - prevPixelGrayValue;
                                sumVariationWithinRow += variation;
                                sumIntensityWithinRow += pixelGrayValue;
                                rowValues[widthIndex]  = variation;
                            }
                            rowAnalysis.AverageIntensity = sumIntensityWithinRow / rowWidth;
                            rowAnalysis.AverageVariation = sumVariationWithinRow / rowWidth;
                            double sumSqDiffs = 0;
                            for (int widthIndex = 0; widthIndex < rowWidth; widthIndex++)
                            {
                                sumSqDiffs += Math.Pow(rowAnalysis.AverageVariation - rowValues[widthIndex], 2);
                            }
                            rowAnalysis.StdDevOfVariation = Math.Sqrt(sumSqDiffs / rowWidth);
                            if (Math.Abs(rowAnalysis.AverageVariation) > surfaceNoiseThreshold)
                            {
                                rowAnalysis.VariationScore = Math.Abs(rowAnalysis.AverageVariation / rowAnalysis.StdDevOfVariation);
                            }
                            else
                            {
                                rowAnalysis.VariationScore = 0;
                            }
                            //overallSumIntensity += rowAnalysis.AverageIntensity;
                            //overallSumVariation += Math.Abs(rowAnalysis.AverageVariation);
                        }
                        RowAnalysis currentRow;
                        for (int searchDirIndex = 1; searchDirIndex < numSearchDirRows - 1; searchDirIndex++)
                        {
                            currentRow = rows[searchDirIndex];
                            //currentRow.AverageVariation_2Row = (currentRow.AverageVariation + nextRow.AverageVariation) / 2;
                            currentRow.AverageVariation_3Row = (currentRow.AverageVariation + rows[searchDirIndex + 1].AverageVariation + rows[searchDirIndex - 1].AverageVariation) / 3;
                        }
                        TestExecution().LogMessageWithTimeFromTrigger("[" + Name + "] finished analyzing pixels");
                    } // end unsafe block
                    analyzedSuccessfully = true;
                }
                catch (Exception e)
                {
                    TestExecution().LogMessageWithTimeFromTrigger("ERROR: Failure in " + Name + "; msg=" + e.Message + " " + Environment.NewLine + e.StackTrace);
                }
                finally
                {
                    sourceBitmap.UnlockBits(sourceBitmapData);
                }

                if (analyzedSuccessfully)
                {
                    SurfaceSearchState state       = SurfaceSearchState.SearchingForEndOfSurface;
                    List <Surface>     surfaces    = new List <Surface>();
                    List <Transition>  transitions = new List <Transition>();
                    Surface            surface     = new Surface(1);
                    surfaces.Add(surface);
                    Transition transition = null;
                    surface.StartPos = 0;
                    int conseqQuietRows = 0;
                    for (int searchDirIndex = 0; searchDirIndex < numSearchDirRows; searchDirIndex++)
                    {
                        switch (state)
                        {
                        case SurfaceSearchState.SearchingForEndOfSurface:
                            if (Math.Abs(rows[searchDirIndex].AverageVariation) > surfaceNoiseThreshold &&
                                Math.Abs(rows[searchDirIndex].AverageVariation) > rows[searchDirIndex].StdDevOfVariation)
                            {
                                if (state == SurfaceSearchState.SearchingForEndOfSurface &&
                                    surface.SurfaceNumber == 1 &&
                                    searchDirIndex - surface.StartPos < minSurfaceSize)
                                {
                                    TestExecution().LogErrorWithTimeFromTrigger("Initial surface too small.  Is an edge at the ROI edge?");
                                    break;
                                }
                                surface.EndPos = searchDirIndex - 1;
                                surface.ComputeValues(rows);
                                surface             = null;
                                transition          = new Transition(transitions.Count + 1);
                                transition.StartPos = searchDirIndex;
                                transitions.Add(transition);
                                state           = SurfaceSearchState.SearchingForEndOfTransition;
                                conseqQuietRows = 0;
                            }
                            break;

                        case SurfaceSearchState.SearchingForEndOfTransition:
                            if (Math.Abs(rows[searchDirIndex].AverageVariation) <= surfaceNoiseThreshold)
                            {
                                conseqQuietRows++;
                            }
                            else
                            {
                                conseqQuietRows = 0;
                            }
                            if (conseqQuietRows >= minSurfaceSize)
                            {
                                transition.EndPos = searchDirIndex - minSurfaceSize;
                                transition.ComputeValues(rows);
                                surface          = new Surface(surfaces.Count + 1);
                                surface.StartPos = transition.EndPos + 1;
                                surfaces.Add(surface);
                                transition      = null;
                                state           = SurfaceSearchState.SearchingForEndOfSurface;
                                conseqQuietRows = 0;
                            }
                            break;
                        }
                    }
                    if (surface != null)
                    {
                        surface.EndPos = numSearchDirRows - 1;
                        surface.ComputeValues(rows);
                        surface = null;
                    }
                    if (transition != null)
                    {
                        transition.EndPos = numSearchDirRows - 1;
                        transition.ComputeValues(rows);
                        transition = null;
                    }

                    foreach (Surface s in surfaces)
                    {
                        TestExecution().LogMessage(String.Format("Found surface from {0,3:0} to {1,3:0}: avgIntensity = {2,8:0.00} (min={3,8:0.00}, max={4,8:0.00}) avgVar = {5,8:0.00} avgAbsVar = {6,8:0.00} ({7,8:0.00}, {8,8:0.00}) sumVar = {9,8:0.00} sumAbsVar = {10,8:0.00}, avgVarScore = {11,8:0.00}",
                                                                 (searchDirOffset + (searchDirIncrement * s.StartPos)),
                                                                 (searchDirOffset + (searchDirIncrement * s.EndPos)),
                                                                 s.AverageIntensity,
                                                                 s.MinIntensity,
                                                                 s.MaxIntensity,
                                                                 s.AverageVariation,
                                                                 s.AverageAbsVariation,
                                                                 s.MinVariation,
                                                                 s.MaxVariation,
                                                                 s.SumVariation,
                                                                 s.SumAbsVariation,
                                                                 s.AverageVariationScore
                                                                 ));
                    }

                    foreach (Transition t in transitions)
                    {
                        TestExecution().LogMessage(String.Format("Found transition from {0,3:0} to {1,3:0}: avgIntensity = {2,8:0.00} (min={3,8:0.00}, max={4,8:0.00}) avgVar = {5,8:0.00} avgAbsVar = {6,8:0.00} ({7,8:0.00}, {8,8:0.00}) sumVar = {9,8:0.00} sumAbsVar = {10,8:0.00}, avgVarScore = {11,8:0.00}",
                                                                 (searchDirOffset + (searchDirIncrement * t.StartPos)),
                                                                 (searchDirOffset + (searchDirIncrement * t.EndPos)),
                                                                 t.AverageIntensity,
                                                                 t.MinIntensity,
                                                                 t.MaxIntensity,
                                                                 t.AverageVariation,
                                                                 t.AverageAbsVariation,
                                                                 t.MinVariation,
                                                                 t.MaxVariation,
                                                                 t.SumVariation,
                                                                 t.SumAbsVariation,
                                                                 t.AverageVariationScore
                                                                 ));
                    }

                    // remove "false"/weak transitions
                    bool removedTransitions = false;
                    for (int tNdx = 0; tNdx < transitions.Count; tNdx++)
                    {
                        if (transitions[tNdx].SumAbsVariation < 2 * surfaceNoiseThreshold)
                        {
                            removedTransitions = true;
                            TestExecution().LogMessage("Deleting transition " + transitions[tNdx].TransitionNumber);
                            if (transitions[tNdx].Size() > 2)
                            {
                                TestExecution().LogMessage("WARNING: removing large 'weak' transition.  Is SurfaceNoiseThreshold too low?");
                            }
                            if (surfaces.Count > tNdx + 1)
                            {
                                TestExecution().LogMessage("Merging surfaces " + surfaces[tNdx].SurfaceNumber + " & " + surfaces[tNdx + 1].SurfaceNumber);
                                surfaces[tNdx].EndPos = surfaces[tNdx + 1].EndPos;
                                surfaces.RemoveAt(tNdx + 1);
                            }
                            else
                            {
                                TestExecution().LogMessage("Merging surface " + surfaces[tNdx].SurfaceNumber + " &  transition " + transitions[tNdx].TransitionNumber);
                                surfaces[tNdx].EndPos = transitions[tNdx].EndPos;
                            }
                            surfaces[tNdx].ComputeValues(rows);
                            transitions.RemoveAt(tNdx); tNdx--; // remove transition, then decrement tNdx since for-loop increments it
                        }
                    }

                    if (removedTransitions)
                    {
                        TestExecution().LogMessage("Results after removals:");
                        foreach (Surface s in surfaces)
                        {
                            TestExecution().LogMessage(String.Format("Found surface from {0,3:0} to {1,3:0}: avgIntensity = {2,8:0.00} (min={3,8:0.00}, max={4,8:0.00}) avgVar = {5,8:0.00} avgAbsVar = {6,8:0.00} ({7,8:0.00}, {8,8:0.00}) sumVar = {9,8:0.00} sumAbsVar = {10,8:0.00}, avgVarScore = {11,8:0.00}",
                                                                     (searchDirOffset + (searchDirIncrement * s.StartPos)),
                                                                     (searchDirOffset + (searchDirIncrement * s.EndPos)),
                                                                     s.AverageIntensity,
                                                                     s.MinIntensity,
                                                                     s.MaxIntensity,
                                                                     s.AverageVariation,
                                                                     s.AverageAbsVariation,
                                                                     s.MinVariation,
                                                                     s.MaxVariation,
                                                                     s.SumVariation,
                                                                     s.SumAbsVariation,
                                                                     s.AverageVariationScore
                                                                     ));
                        }

                        foreach (Transition t in transitions)
                        {
                            TestExecution().LogMessage(String.Format("Found transition from {0,3:0} to {1,3:0}: avgIntensity = {2,8:0.00} (min={3,8:0.00}, max={4,8:0.00}) avgVar = {5,8:0.00} avgAbsVar = {6,8:0.00} ({7,8:0.00}, {8,8:0.00}) sumVar = {9,8:0.00} sumAbsVar = {10,8:0.00}, avgVarScore = {11,8:0.00}",
                                                                     (searchDirOffset + (searchDirIncrement * t.StartPos)),
                                                                     (searchDirOffset + (searchDirIncrement * t.EndPos)),
                                                                     t.AverageIntensity,
                                                                     t.MinIntensity,
                                                                     t.MaxIntensity,
                                                                     t.AverageVariation,
                                                                     t.AverageAbsVariation,
                                                                     t.MinVariation,
                                                                     t.MaxVariation,
                                                                     t.SumVariation,
                                                                     t.SumAbsVariation,
                                                                     t.AverageVariationScore
                                                                     ));
                        }
                    }
                    List <Edge> edges         = new List <Edge>();
                    int         transitionNdx = 0;
                    int         edgeRowNdx;
                    double      greatestEdgeScore        = -1;
                    int         edgeNdxWithGreatestScore = -1;
                    for (int surfaceNdx = 0; surfaceNdx < surfaces.Count - 1; surfaceNdx++)
                    {
                        Edge edge = new Edge(edges.Count + 1);
                        edge.leadingSurface  = surfaces[surfaceNdx];
                        edge.transition      = transitions[transitionNdx]; transitionNdx++;
                        edge.trailingSurface = surfaces[surfaceNdx + 1];
                        edges.Add(edge);
                        if (edge.transition.AverageVariationScore > greatestEdgeScore)
                        {
                            greatestEdgeScore        = edge.transition.AverageVariationScore;
                            edgeNdxWithGreatestScore = edge.EdgeNumber - 1;
                        }

                        edgeRowNdx = edge.FindEdgeBasedOnIntensity(rows);
                        if (edgeRowNdx >= 0)
                        {
                            TestExecution().LogMessage("Found edge by intensity at row " + edgeRowNdx + " (image location=" + (searchDirOffset + (searchDirIncrement * edgeRowNdx)) + ")");
                        }
                        else
                        {
                            TestExecution().LogMessage("WARNING: unable to find edge by intensity from edge " + edge.EdgeNumber);
                        }
                        edgeRowNdx = edge.FindEdgeBasedOnVariation(rows);
                        if (edgeRowNdx >= 0)
                        {
                            TestExecution().LogMessage("Found edge by variation at row " + edgeRowNdx + " (image location=" + (searchDirOffset + (searchDirIncrement * edgeRowNdx)) + ")");
                        }
                        else
                        {
                            TestExecution().LogMessage("WARNING: unable to find edge by variation from edge " + edge.EdgeNumber);
                        }
                    }
                    if (edgeNdxWithGreatestScore >= 0)
                    {
                        Edge edge = edges[edgeNdxWithGreatestScore];
                        switch (mEdgeDetectionMode)
                        {
                        case FindEdgeOriginalDefinition.EdgeDetectionModes.MaxVariation:
                            edgeRowNdx   = edge.FindEdgeBasedOnVariation(rows);
                            edgeLocation = SetEdgeLocation(edgeRowNdx, searchDirOffset, searchDirIncrement);
                            TestExecution().LogMessage("Using edge by variation at row " + edgeRowNdx + " (image location=" + (searchDirOffset + (searchDirIncrement * edgeRowNdx)) + ")");
                            break;

                        case FindEdgeOriginalDefinition.EdgeDetectionModes.SurfaceIntensity:
                            edgeRowNdx   = edge.FindEdgeBasedOnIntensity(rows);
                            edgeLocation = SetEdgeLocation(edgeRowNdx, searchDirOffset, searchDirIncrement);
                            TestExecution().LogMessage("Using edge by intensity at row " + edgeRowNdx + " (image location=" + (searchDirOffset + (searchDirIncrement * edgeRowNdx)) + ")");
                            break;

                        case FindEdgeOriginalDefinition.EdgeDetectionModes.NotDefined:
                            TestExecution().LogErrorWithTimeFromTrigger("EdgeDetectionMode for " + Name + " isn't defined.");
                            break;

                        default:
                            TestExecution().LogErrorWithTimeFromTrigger("Edge Detection Mode '" + mEdgeDetectionMode + "' isn't fully implemented.");
                            break;
                        }
                    }

//                    TestExecution().LogMessage("index | pos  | avgI | avgV | stdDevV | V score");
                    for (int searchDirIndex = 0; searchDirIndex < numSearchDirRows; searchDirIndex++)
                    {
                        RowAnalysis row = rows[searchDirIndex];
                        TestExecution().LogMessage(
                            String.Format("{0,3:0} pos={1,4:0} avgI={2,8:0.00} avgV={3,8:0.00} stdDevV={4,8:0.00} vScore={5,8:0.00} 3RowAvgV={6,8:0.00}",
                                          row.SearchIndex,
                                          (searchDirOffset + (searchDirIncrement * row.SearchIndex)),
                                          row.AverageIntensity,
                                          row.AverageVariation,
                                          row.StdDevOfVariation,
                                          row.VariationScore,
//                            row.AverageVariation_2Row,
                                          row.AverageVariation_3Row
//                            row.OverallAverageIntensityBeforeRow,
//                            row.OverallVariationBeforeRow
                                          ));
                    }
                }
            } // end main block ("else" after all initial setup error checks)
            mEdgeLocation.SetValue(edgeLocation);
            mEdgeLocation.SetIsComplete();
            DateTime doneTime    = DateTime.Now;
            TimeSpan computeTime = doneTime - startTime;

            TestExecution().LogMessageWithTimeFromTrigger(Name + " computed edge at " + edgeLocation);

            if (mAutoSave)
            {
                try
                {
                    string filePath = ((FindEdgeOriginalDefinition)Definition()).AutoSavePath;
                    mSourceImage.Save(filePath, Name, true);
                    TestExecution().LogMessageWithTimeFromTrigger("Snapshot saved");
                }
                catch (ArgumentException e)
                {
                    Project().Window().logMessage("ERROR: " + e.Message);
                    TestExecution().LogErrorWithTimeFromTrigger(e.Message);
                }
                catch (Exception e)
                {
                    Project().Window().logMessage("ERROR: Unable to AutoSave snapshot from " + Name + ".  Ensure path valid and disk not full.  Low-level message=" + e.Message);
                    TestExecution().LogErrorWithTimeFromTrigger("Unable to AutoSave snapshot from " + Name + ".  Ensure path valid and disk not full.");
                }
            }

            TestExecution().LogMessageWithTimeFromTrigger(Name + " finished at " + doneTime + "  | took " + computeTime.TotalMilliseconds + "ms");
        }
Beispiel #4
0
        public static readonly int PIXEL_BYTE_WIDTH     = 4; // determined by PixelFormat.Format32bppArgb; http://www.bobpowell.net/lockingbits.htm
        public override void DoWork()
        {
            TestExecution().LogMessageWithTimeFromTrigger(Name + " started");
            Stopwatch watch = new Stopwatch();

            watch.Start();

            int    transitionLocation = -1;
            double transitionScore    = -1;

            if (mPrerequisite != null && !mPrerequisite.ValueAsBoolean())
            {
                TestExecution().LogMessageWithTimeFromTrigger(Name + ": prerequisites not met. Skipping.");
            }
            else
            {
                switch (mSearchDirection)
                {
                case Direction.Down:
                    startXOffset        = mSearchArea.Left;
                    startYOffset        = mSearchArea.Top;
                    numSearchDirRows    = mSearchArea.Bottom - mSearchArea.Top + 1;
                    searchDirXIncrement = 0;
                    searchDirYIncrement = 1;
                    rowWidth            = mSearchArea.Right - mSearchArea.Left + 1;
                    rowDirXIncrement    = 1;
                    rowDirYIncrement    = 0;
                    searchDirOffset     = startYOffset;
                    searchDirIncrement  = 1;
                    break;

                case Direction.Up:
                    startXOffset        = mSearchArea.Left;
                    startYOffset        = mSearchArea.Bottom;
                    numSearchDirRows    = mSearchArea.Bottom - mSearchArea.Top + 1;
                    searchDirXIncrement = 0;
                    searchDirYIncrement = -1;
                    rowWidth            = mSearchArea.Right - mSearchArea.Left + 1;
                    rowDirXIncrement    = 1;
                    rowDirYIncrement    = 0;
                    searchDirOffset     = startYOffset;
                    searchDirIncrement  = -1;
                    break;

                case Direction.Right:
                    startXOffset        = mSearchArea.Left;
                    startYOffset        = mSearchArea.Top;
                    numSearchDirRows    = mSearchArea.Right - mSearchArea.Left + 1;
                    searchDirXIncrement = 1;
                    searchDirYIncrement = 0;
                    rowWidth            = mSearchArea.Bottom - mSearchArea.Top + 1;
                    rowDirXIncrement    = 0;
                    rowDirYIncrement    = 1;
                    searchDirOffset     = startXOffset;
                    searchDirIncrement  = 1;
                    break;

                case Direction.Left:
                    startXOffset        = mSearchArea.Right;
                    startYOffset        = mSearchArea.Top;
                    numSearchDirRows    = mSearchArea.Right - mSearchArea.Left + 1;
                    searchDirXIncrement = -1;
                    searchDirYIncrement = 0;
                    rowWidth            = mSearchArea.Bottom - mSearchArea.Top + 1;
                    rowDirXIncrement    = 0;
                    rowDirYIncrement    = 1;
                    searchDirOffset     = startXOffset;
                    searchDirIncrement  = -1;
                    break;

                case Direction.NotDefined:
                    throw new ArgumentException("Search direction not defined for " + Name);

                default:
                    throw new ArgumentException("Unexpected search direction for " + Name);
                }

                if (SourceImage.Bitmap == null)
                {
                    TestExecution().LogMessage("ERROR: source image for '" + Name + "' does not exist.");
                }
                //else if (mSurfaceNoiseThreshold_PercentOfSharpestTransition.ValueAsDecimal() < 0)
                //{
                //    TestExecution().LogMessage("ERROR: SurfaceNoiseThreshold_PercentOfSharpestTransition for '" + Name + "' can not be negative.");
                //}
                else if (mSearchArea.Left < 0 || mSearchArea.Right >= mSourceImage.Width || mSearchArea.Left > mSearchArea.Right || mSearchArea.Top < 0 || mSearchArea.Bottom >= mSourceImage.Height || mSearchArea.Top > mSearchArea.Bottom)
                {
                    TestExecution().LogMessage("ERROR: Invalid SearchArea for '" + Name + "'.");
                }
                else
                {
                    int surfaceNoiseThreshold = (int)mSurfaceNoiseThreshold_Min.ValueAsLong();

                    int    x = 0;
                    int    y = 0;
                    double sumIntensityWithinRow = 0;

                    RowAnalysis[] rows = new RowAnalysis[numSearchDirRows];
                    bool          analyzedSuccessfully = false;

                    if (true)
                    {
                        try
                        {
                            RowAnalysis rowAnalysis;
                            RowAnalysis prevRow = null;
                            for (int searchDirIndex = 0; searchDirIndex < numSearchDirRows; searchDirIndex++)
                            {
                                rowAnalysis           = new RowAnalysis(searchDirIndex);
                                rows[searchDirIndex]  = rowAnalysis;
                                sumIntensityWithinRow = 0;
                                for (int widthIndex = 0; widthIndex < rowWidth; widthIndex++)
                                {
                                    x = startXOffset + (searchDirIndex * searchDirXIncrement) + (widthIndex * rowDirXIncrement);
                                    y = startYOffset + (searchDirIndex * searchDirYIncrement) + (widthIndex * rowDirYIncrement);

                                    pixelGrayValue = mSourceImage.GetGrayValue(x, y);

                                    sumIntensityWithinRow += pixelGrayValue;
                                }
                                rowAnalysis.AverageIntensity = sumIntensityWithinRow / rowWidth;
                                if (prevRow != null)
                                {
                                    rowAnalysis.VariationFromPrevRow = rowAnalysis.AverageIntensity - prevRow.AverageIntensity;
                                }
                                prevRow = rowAnalysis;
                            }

                            // average neighboring rows together to supress noise and highlight multi-row transitions
                            RowAnalysis currentRow;
                            double      largestVariation        = 0;
                            int         rowWithLargestVariation = -1;
                            for (int searchDirIndex = 1; searchDirIndex < numSearchDirRows - 1; searchDirIndex++)
                            {
                                currentRow = rows[searchDirIndex];
                                currentRow.AverageVariation_3Row = (currentRow.VariationFromPrevRow + rows[searchDirIndex + 1].VariationFromPrevRow + rows[searchDirIndex - 1].VariationFromPrevRow) / 3;
                                double abs_AvgVar_3Row = Math.Abs(currentRow.AverageVariation_3Row);
                                if ((mTransitionTypeSelectionFilter == FindTransitionDefinition.TransitionTypeSelectionFilters.NotDefined ||
                                     (mTransitionTypeSelectionFilter == FindTransitionDefinition.TransitionTypeSelectionFilters.DarkToBright && currentRow.AverageVariation_3Row > 0) ||
                                     (mTransitionTypeSelectionFilter == FindTransitionDefinition.TransitionTypeSelectionFilters.BrightToDark && currentRow.AverageVariation_3Row < 0)
                                     ) &&
                                    abs_AvgVar_3Row > surfaceNoiseThreshold &&
                                    abs_AvgVar_3Row > largestVariation)// &&
                                //(!ensureStraightTransition || TransitionIsStraight(currentRow)))
                                {
                                    largestVariation        = abs_AvgVar_3Row;
                                    rowWithLargestVariation = searchDirIndex;
                                }
                            }

                            // Compute threshold to weed out noise (TODO: train this over a series of tests in "learn" mode? we need a way to determine if no transition present)
                            // option1: use 60% of biggest variation
                            // option2: run transitions thru a value group and weed out bottom chunk
                            //surfaceNoiseThreshold = Math.Max(surfaceNoiseThreshold, (int)(largestVariation * (mSurfaceNoiseThreshold_PercentOfSharpestTransition.ValueAsDecimal() / 100.0)));
                            //TestExecution().LogMessageWithTimeFromTrigger("[" + Name + "] surfaceNoiseThreshold=" + surfaceNoiseThreshold + "    (largestVariation=" + largestVariation + ")");
                            TestExecution().LogMessageWithTimeFromTrigger("[" + Name + "] finished analyzing pixels");

                            if (rowWithLargestVariation >= 0)
                            {
                                transitionLocation = searchDirOffset + (rowWithLargestVariation * searchDirIncrement);
                                transitionScore    = rows[rowWithLargestVariation].AverageVariation_3Row; // referencing array vs largestVariation since largestVariation is Math.Abs()
                            }
                            analyzedSuccessfully = true;
                        }
                        catch (Exception e)
                        {
                            TestExecution().LogMessageWithTimeFromTrigger("ERROR: Failure in " + Name + "; msg=" + e.Message + " " + Environment.NewLine + e.StackTrace);
                        }
                    }
                    else // use old pointer-based pixel access instead...
                    {
                        Bitmap sourceBitmap = SourceImage.Bitmap;
                        try
                        {
                            sourceBitmapData   = sourceBitmap.LockBits(new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, PIXEL_FORMAT);
                            sourceStride       = sourceBitmapData.Stride;
                            sourceStrideOffset = sourceStride - (sourceBitmapData.Width * PIXEL_BYTE_WIDTH);

                            unsafe // see http://www.codeproject.com/csharp/quickgrayscale.asp?df=100&forumid=293759&select=2214623&msg=2214623
                            {
                                byte *      sourcePointer;
                                RowAnalysis rowAnalysis;
                                RowAnalysis prevRow = null;
                                for (int searchDirIndex = 0; searchDirIndex < numSearchDirRows; searchDirIndex++)
                                {
                                    rowAnalysis           = new RowAnalysis(searchDirIndex);
                                    rows[searchDirIndex]  = rowAnalysis;
                                    sumIntensityWithinRow = 0;
                                    for (int widthIndex = 0; widthIndex < rowWidth; widthIndex++)
                                    {
                                        x = startXOffset + (searchDirIndex * searchDirXIncrement) + (widthIndex * rowDirXIncrement);
                                        y = startYOffset + (searchDirIndex * searchDirYIncrement) + (widthIndex * rowDirYIncrement);

                                        sourcePointer  = (byte *)sourceBitmapData.Scan0;                                                    // init to first byte of image
                                        sourcePointer += (y * sourceStride) + (x * PIXEL_BYTE_WIDTH);                                       // adjust to current point
                                        pixelGrayValue = (int)(0.3 * sourcePointer[2] + 0.59 * sourcePointer[1] + 0.11 * sourcePointer[0]); // Then, add 30% of the red value, 59% of the green value, and 11% of the blue value, together. .... These percentages are chosen due to the different relative sensitivity of the normal human eye to each of the primary colors (less sensitive to green, more to blue).
                                        // http://www.bobpowell.net/grayscale.htm
                                        // https://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=440425&SiteID=1

                                        sumIntensityWithinRow += pixelGrayValue;
                                    }
                                    rowAnalysis.AverageIntensity = sumIntensityWithinRow / rowWidth;
                                    if (prevRow != null)
                                    {
                                        rowAnalysis.VariationFromPrevRow = rowAnalysis.AverageIntensity - prevRow.AverageIntensity;
                                    }
                                    prevRow = rowAnalysis;
                                }

                                // average neighboring rows together to supress noise and highlight multi-row transitions
                                RowAnalysis currentRow;
                                double      largestVariation        = 0;
                                int         rowWithLargestVariation = -1;
                                for (int searchDirIndex = 1; searchDirIndex < numSearchDirRows - 1; searchDirIndex++)
                                {
                                    currentRow = rows[searchDirIndex];
                                    currentRow.AverageVariation_3Row = (currentRow.VariationFromPrevRow + rows[searchDirIndex + 1].VariationFromPrevRow + rows[searchDirIndex - 1].VariationFromPrevRow) / 3;
                                    double abs_AvgVar_3Row = Math.Abs(currentRow.AverageVariation_3Row);
                                    if ((mTransitionTypeSelectionFilter == FindTransitionDefinition.TransitionTypeSelectionFilters.NotDefined ||
                                         (mTransitionTypeSelectionFilter == FindTransitionDefinition.TransitionTypeSelectionFilters.DarkToBright && currentRow.AverageVariation_3Row > 0) ||
                                         (mTransitionTypeSelectionFilter == FindTransitionDefinition.TransitionTypeSelectionFilters.BrightToDark && currentRow.AverageVariation_3Row < 0)
                                         ) &&
                                        abs_AvgVar_3Row > surfaceNoiseThreshold &&
                                        abs_AvgVar_3Row > largestVariation)// &&
                                    //(!ensureStraightTransition || TransitionIsStraight(currentRow)))
                                    {
                                        largestVariation        = abs_AvgVar_3Row;
                                        rowWithLargestVariation = searchDirIndex;
                                    }
                                }

                                // Compute threshold to weed out noise (TODO: train this over a series of tests in "learn" mode? we need a way to determine if no transition present)
                                // option1: use 60% of biggest variation
                                // option2: run transitions thru a value group and weed out bottom chunk
                                //surfaceNoiseThreshold = Math.Max(surfaceNoiseThreshold, (int)(largestVariation * (mSurfaceNoiseThreshold_PercentOfSharpestTransition.ValueAsDecimal() / 100.0)));
                                //TestExecution().LogMessageWithTimeFromTrigger("[" + Name + "] surfaceNoiseThreshold=" + surfaceNoiseThreshold + "    (largestVariation=" + largestVariation + ")");
                                TestExecution().LogMessageWithTimeFromTrigger("[" + Name + "] finished analyzing pixels");

                                if (rowWithLargestVariation >= 0)
                                {
                                    transitionLocation = searchDirOffset + (rowWithLargestVariation * searchDirIncrement);
                                    transitionScore    = rows[rowWithLargestVariation].AverageVariation_3Row; // referencing array vs largestVariation since largestVariation is Math.Abs()
                                }
                            } // end unsafe block
                            analyzedSuccessfully = true;
                        }
                        catch (Exception e)
                        {
                            TestExecution().LogMessageWithTimeFromTrigger("ERROR: Failure in " + Name + "; msg=" + e.Message + " " + Environment.NewLine + e.StackTrace);
                        }
                        finally
                        {
                            if (sourceBitmapData != null)
                            {
                                sourceBitmap.UnlockBits(sourceBitmapData);
                            }
                        }
                    } // end pixel access method selection

                    if (analyzedSuccessfully)
                    {
                        if (transitionLocation >= 0)
                        {
                            if (mSearchDirection == Direction.Left || mSearchDirection == Direction.Right)
                            {
                                mTransitionMarker.SetStartX(transitionLocation);
                                mTransitionMarker.SetEndX(transitionLocation);
                                mTransitionMarker.SetStartY(mSearchArea.Top);
                                mTransitionMarker.SetEndY(mSearchArea.Bottom);
                            }
                            else
                            {
                                mTransitionMarker.SetStartY(transitionLocation);
                                mTransitionMarker.SetEndY(transitionLocation);
                                mTransitionMarker.SetStartX(mSearchArea.Left);
                                mTransitionMarker.SetEndX(mSearchArea.Right);
                            }
                            mTransitionMarker.SetIsComplete();
                        }

                        if (mVerboseOutput)
                        {
                            for (int searchDirIndex = 0; searchDirIndex < numSearchDirRows; searchDirIndex++)
                            {
                                RowAnalysis row = rows[searchDirIndex];
                                TestExecution().LogMessage(
                                    String.Format("{0,3:0} pos={1,4:0} avgI={2,8:0.00} rowV={3,8:0.00} stdDevV={4,8:0.00} vScore={5,8:0.00} 3RowAvgV={6,8:0.00}",
                                                  row.SearchIndex,
                                                  (searchDirOffset + (searchDirIncrement * row.SearchIndex)),
                                                  row.AverageIntensity,
                                                  row.VariationFromPrevRow,
                                                  row.StdDevOfVariation,
                                                  row.VariationScore,
                                                  //                            row.AverageVariation_2Row,
                                                  row.AverageVariation_3Row
                                                  //                            row.OverallAverageIntensityBeforeRow,
                                                  //                            row.OverallVariationBeforeRow
                                                  ));
                            }
                        }
                    }
                } // end main block ("else" after all initial setup error checks)
            }
            mTransitionLocation.SetValue(transitionLocation);
            mTransitionLocation.SetIsComplete();
            if (mTransitionScore != null)
            {
                mTransitionScore.SetValue(transitionScore);
                mTransitionScore.SetIsComplete();
            }

            TestExecution().LogMessageWithTimeFromTrigger(Name + " computed transition at " + transitionLocation + " with a score of " + transitionScore);

            if (mAutoSave)
            {
                try
                {
                    string filePath = ((FindTransitionDefinition)Definition()).AutoSavePath;
                    mSourceImage.Save(filePath, Name, true);
                    TestExecution().LogMessageWithTimeFromTrigger("Snapshot saved");
                }
                catch (ArgumentException e)
                {
                    Project().Window().logMessage("ERROR: " + e.Message);
                    TestExecution().LogErrorWithTimeFromTrigger(e.Message);
                }
                catch (Exception e)
                {
                    Project().Window().logMessage("ERROR: Unable to AutoSave snapshot from " + Name + ".  Ensure path valid and disk not full.  Low-level message=" + e.Message);
                    TestExecution().LogErrorWithTimeFromTrigger("Unable to AutoSave snapshot from " + Name + ".  Ensure path valid and disk not full.");
                }
            }

            watch.Stop();
            TestExecution().LogMessageWithTimeFromTrigger(Name + " took " + watch.ElapsedMilliseconds + "ms  (" + watch.ElapsedTicks + " ticks)");
        }