示例#1
0
        private static double CalculateMinUnaryEdgeEnergy(int edgeIndex, ShapeModel model, ShapeConstraints shapeConstraints, double edgeLength)
        {
            double          bestWidth       = edgeLength * model.GetEdgeParams(edgeIndex).WidthToEdgeLengthRatio;
            EdgeConstraints edgeConstraints = shapeConstraints.EdgeConstraints[edgeIndex];

            bestWidth = MathHelper.Trunc(bestWidth, edgeConstraints.MinWidth, edgeConstraints.MaxWidth);
            return(model.CalculateEdgeWidthEnergyTerm(edgeIndex, bestWidth, edgeLength));
        }
        private void SetTarget(ShapeModel newShapeModel, Size newImageSize)
        {
            if (newShapeModel.Structure.Edges.Count > CacheCapacity)
            {
                throw new InvalidOperationException("Edge count is bigger than cache size. Such shape models are not currently supported.");
            }

            this.freeTermImages  = new LinkedList <Image2D <ObjectBackgroundTerm> >();
            this.cachedEdgeTerms = new LruCache <EdgeDescription, Image2D <ObjectBackgroundTerm> >(CacheCapacity);
            this.cachedEdgeTerms.CacheItemDiscarded += (sender, args) => this.DeallocateImage(args.DiscardedValue);
            this.shapeModel = newShapeModel;
            this.imageSize  = newImageSize;
        }
        private static double CalculateSingleEdgeLowerBound(
            ShapeModel model, ShapeConstraints shapeConstraints, IList<ILengthAngleConstraints> lengthAngleConstraints)
        {
            // TODO: support models with multiple edges but without pairwise constraints
            Debug.Assert(model.Structure.Edges.Count == 1);
            Debug.Assert(lengthAngleConstraints.Count == 1);

            double result;

            // Calculate best possible edge width penalty
            EdgeConstraints edgeConstraints = shapeConstraints.EdgeConstraints[0];
            ShapeEdgeParams edgeParams = model.GetEdgeParams(0);
            Range lengthBoundary = lengthAngleConstraints[0].LengthBoundary;
            Range scaledLengthRange = new Range(lengthBoundary.Left * edgeParams.WidthToEdgeLengthRatio, lengthBoundary.Right * edgeParams.WidthToEdgeLengthRatio);
            Range widthRange = new Range(edgeConstraints.MinWidth, edgeConstraints.MaxWidth);
            if (scaledLengthRange.IntersectsWith(widthRange))
                result = 0;
            else
            {
                result = model.CalculateEdgeWidthEnergyTerm(0, widthRange.Right, lengthBoundary.Left);
                result = Math.Min(result, model.CalculateEdgeWidthEnergyTerm(0, widthRange.Left, lengthBoundary.Right));
            }

            return result;
        }
        public double CalculateLowerBound(Size imageSize, ShapeModel model, ShapeConstraints shapeConstraints)
        {
            if (model == null)
                throw new ArgumentNullException("model");
            if (shapeConstraints == null)
                throw new ArgumentNullException("shapeConstraints");
            if (model.Structure != shapeConstraints.ShapeStructure)
                throw new ArgumentException("Shape model and shape constraints correspond to different shape structures.");

            List<ILengthAngleConstraints> lengthAngleConstraints = CalculateLengthAngleConstraints(shapeConstraints);
            if (model.ConstrainedEdgePairs.Count == 0)
            {
                double lowerBound = CalculateSingleEdgeLowerBound(model, shapeConstraints, lengthAngleConstraints);
                Debug.Assert(lowerBound >= 0);
                return lowerBound;
            }

            // Determine max (scaled) length possible
            double maxRatio1 = (from edgePair in model.ConstrainedEdgePairs
                                select model.GetEdgePairParams(edgePair.Item1, edgePair.Item2).MeanLengthRatio).Max();
            double maxRatio2 = (from edgePair in model.ConstrainedEdgePairs
                                select 1.0 / model.GetEdgePairParams(edgePair.Item1, edgePair.Item2).MeanLengthRatio).Max();
            double maxRatio = Math.Max(maxRatio1, maxRatio2);
            double maxEdgeLength = (new Vector(imageSize.Width, imageSize.Height)).Length;
            double maxScaledLength = maxEdgeLength * maxRatio;

            if (maxScaledLength != this.currentMaxScaledLength)
            {
                this.currentMaxScaledLength = maxScaledLength;
                this.transformPool.Clear();
            }

            this.FreeAllDistanceTransforms();

            // Calculate distance transforms for all the child edges
            List<GeneralizedDistanceTransform2D> childTransforms = new List<GeneralizedDistanceTransform2D>();
            foreach (int edgeIndex in model.IterateNeighboringEdgeIndices(model.RootEdgeIndex))
                childTransforms.Add(CalculateMinEnergiesForAllParentEdges(model, shapeConstraints, model.RootEdgeIndex, edgeIndex, lengthAngleConstraints));

            // Find best overall solution
            double minEnergySum = Double.PositiveInfinity;
            GeneralizedDistanceTransform2D transform = childTransforms[0];
            foreach (int lengthGridIndex in transform.EnumerateInterestGridIndicesX())
            {
                double length = transform.GridIndexToCoordX(lengthGridIndex);
                double minPairwiseEnergy = Double.PositiveInfinity;

                foreach (int angleGridIndex in transform.EnumerateInterestGridIndicesY())
                {
                    double angle = transform.GridIndexToCoordY(angleGridIndex);
                    const double eps = 1e-8;
                    if (angle > Math.PI + eps || angle < -Math.PI - eps)
                        continue;   // Consider only natural angle representations here

                    minPairwiseEnergy = Math.Min(minPairwiseEnergy, CalculateMinPairwiseEdgeEnergy(length, angle, childTransforms));
                }

                double unaryEdgeEnergy = CalculateMinUnaryEdgeEnergy(model.RootEdgeIndex, model, shapeConstraints, length);
                double rootEdgeEnergy = model.CalculateRootEdgeEnergyTerm(length);
                minEnergySum = Math.Min(minEnergySum, minPairwiseEnergy + unaryEdgeEnergy + rootEdgeEnergy);
            }

            Debug.Assert(minEnergySum >= 0);
            return minEnergySum;
        }
        private GeneralizedDistanceTransform2D CalculateMinEnergiesForAllParentEdges(
            ShapeModel model,
            ShapeConstraints shapeConstraints,
            int parentEdgeIndex,
            int currentEdgeIndex,
            IList<ILengthAngleConstraints> lengthAngleConstraints)
        {
            // Calculate child transforms
            List<GeneralizedDistanceTransform2D> childDistanceTransforms = new List<GeneralizedDistanceTransform2D>();
            foreach (int neighborEdgeIndex in model.IterateNeighboringEdgeIndices(currentEdgeIndex))
            {
                // Iterate only through children
                if (neighborEdgeIndex == parentEdgeIndex)
                    continue;

                GeneralizedDistanceTransform2D childTransform = CalculateMinEnergiesForAllParentEdges(
                    model, shapeConstraints, currentEdgeIndex, neighborEdgeIndex, lengthAngleConstraints);
                Debug.Assert(childTransform.IsComputed);
                childDistanceTransforms.Add(childTransform);
            }

            ShapeEdgePairParams pairParams = model.GetEdgePairParams(parentEdgeIndex, currentEdgeIndex);
            GeneralizedDistanceTransform2D transform = this.AllocateDistanceTransform();
            SetupTransformFinitePenaltyRanges(transform, pairParams, lengthAngleConstraints[currentEdgeIndex]);
            SetupTransformInterestRanges(transform, lengthAngleConstraints[parentEdgeIndex]);

            Func<double, double, double> penaltyFunction =
                (scaledLength, shiftedAngle) =>
                {
                    double length = scaledLength / pairParams.MeanLengthRatio;
                    double angle = shiftedAngle + pairParams.MeanAngle;
                    
                    double lengthTolerance = transform.GridStepSizeX / pairParams.MeanLengthRatio;
                    double angleTolerance = transform.GridStepSizeY;
                    if (!lengthAngleConstraints[currentEdgeIndex].InRange(length, lengthTolerance, angle, angleTolerance))
                        return 1e+20;

                    double penalty =
                        CalculateMinUnaryEdgeEnergy(currentEdgeIndex, model, shapeConstraints, length) +
                        CalculateMinPairwiseEdgeEnergy(length, angle, childDistanceTransforms);
                    return penalty;
                };

            transform.Compute(
                0.5 / MathHelper.Sqr(pairParams.LengthDiffDeviation),
                0.5 / MathHelper.Sqr(pairParams.AngleDeviation),
                penaltyFunction);

            return transform;
        }
 private static double CalculateMinUnaryEdgeEnergy(int edgeIndex, ShapeModel model, ShapeConstraints shapeConstraints, double edgeLength)
 {
     double bestWidth = edgeLength * model.GetEdgeParams(edgeIndex).WidthToEdgeLengthRatio;
     EdgeConstraints edgeConstraints = shapeConstraints.EdgeConstraints[edgeIndex];
     bestWidth = MathHelper.Trunc(bestWidth, edgeConstraints.MinWidth, edgeConstraints.MaxWidth);
     return model.CalculateEdgeWidthEnergyTerm(edgeIndex, bestWidth, edgeLength);
 }
示例#7
0
        public Shape MutateShape(Shape shape, ShapeModel shapeModel, Size imageSize, double normalizedTemperature)
        {
            if (shape == null)
            {
                throw new ArgumentNullException("shape");
            }
            if (shapeModel == null)
            {
                throw new ArgumentNullException("shapeModel");
            }

            double maxImageSideSize = Math.Max(imageSize.Width, imageSize.Height);
            Shape  mutatedShape;

            double weightSum =
                this.edgeWidthMutationWeight +
                this.edgeLengthMutationWeight +
                this.edgeAngleMutationWeight +
                this.shapeTranslationWeight +
                this.shapeScaleWeight;

            if (weightSum <= 0)
            {
                throw new InvalidOperationException("At least one type of mutation should have non-zero probability weight.");
            }
            double rand = Random.Double(0, weightSum);

            // Shape part mutation
            if (rand < this.edgeWidthMutationWeight + this.edgeLengthMutationWeight + this.edgeAngleMutationWeight)
            {
                ShapeLengthAngleRepresentation representation = shape.GetLengthAngleRepresentation();
                int randomEdge = Random.Int(shape.Structure.Edges.Count);

                // Mutate edge width
                if (rand < this.edgeWidthMutationWeight)
                {
                    double       widthShiftStdDev = maxImageSideSize * this.edgeWidthMutationPower * normalizedTemperature;
                    const double minWidth         = 3;
                    double       widthShift       = Random.Normal(0, widthShiftStdDev, -shape.EdgeWidths[randomEdge] + minWidth);
                    representation.EdgeWidths[randomEdge] += widthShift;
                }
                // Mutate edge length
                else if (rand < this.edgeWidthMutationWeight + this.edgeLengthMutationWeight)
                {
                    double lengthShiftStdDev = maxImageSideSize * this.edgeLengthMutationPower * normalizedTemperature;
                    double lengthShift       = Random.Normal(0, lengthShiftStdDev);
                    representation.EdgeLengths[randomEdge] += lengthShift;
                }
                // Mutate edge angle
                else
                {
                    double angleShiftStdDev = this.edgeAngleMutationPower * normalizedTemperature;
                    double angleShift       = Random.Normal(0, angleShiftStdDev);
                    representation.EdgeAngles[randomEdge] += angleShift;
                }

                mutatedShape = shapeModel.BuildShapeFromLengthAngleRepresentation(representation);
            }
            // Whole shape mutation
            else
            {
                rand        -= this.edgeWidthMutationWeight + this.edgeLengthMutationWeight + this.edgeAngleMutationWeight;
                mutatedShape = shape.Clone();

                // Translate shape
                if (rand < this.shapeTranslationWeight)
                {
                    Vector maxTopLeftShift     = new Vector(Double.NegativeInfinity, Double.NegativeInfinity);
                    Vector minBottomRightShift = new Vector(Double.PositiveInfinity, Double.PositiveInfinity);
                    for (int i = 0; i < mutatedShape.VertexPositions.Count; ++i)
                    {
                        maxTopLeftShift.X     = Math.Max(maxTopLeftShift.X, -mutatedShape.VertexPositions[i].X);
                        maxTopLeftShift.Y     = Math.Max(maxTopLeftShift.Y, -mutatedShape.VertexPositions[i].Y);
                        minBottomRightShift.X = Math.Min(minBottomRightShift.X, imageSize.Width - mutatedShape.VertexPositions[i].X);
                        minBottomRightShift.Y = Math.Min(minBottomRightShift.Y, imageSize.Height - mutatedShape.VertexPositions[i].Y);
                    }

                    double translationStdDev = maxImageSideSize * this.shapeTranslationPower * normalizedTemperature;
                    Vector shift             = new Vector(Random.Normal(0, translationStdDev), Random.Normal(0, translationStdDev));
                    shift = MathHelper.Trunc(shift, maxTopLeftShift, minBottomRightShift);

                    for (int i = 0; i < mutatedShape.VertexPositions.Count; ++i)
                    {
                        mutatedShape.VertexPositions[i] += shift;
                    }
                }
                // Scale shape
                else
                {
                    Vector       shapeCenter = shape.VertexPositions.Aggregate(Vector.Zero, (a, c) => a + c) / shape.VertexPositions.Count;
                    double       scaleStdDev = this.shapeScalePower * normalizedTemperature;
                    const double minScale    = 0.1;
                    double       scale       = Random.Normal(1.0, scaleStdDev, minScale);
                    for (int i = 0; i < mutatedShape.VertexPositions.Count; ++i)
                    {
                        mutatedShape.VertexPositions[i] = shapeCenter + scale * (mutatedShape.VertexPositions[i] - shapeCenter);
                    }
                }
            }

            Debug.Assert(mutatedShape != null);
            return(mutatedShape);
        }
        private void SetTarget(ShapeModel newShapeModel, Size newImageSize)
        {
            if (newShapeModel.Structure.Edges.Count > CacheCapacity)
                throw new InvalidOperationException("Edge count is bigger than cache size. Such shape models are not currently supported.");

            this.freeTermImages = new LinkedList<Image2D<ObjectBackgroundTerm>>();
            this.cachedEdgeTerms = new LruCache<EdgeDescription, Image2D<ObjectBackgroundTerm>>(CacheCapacity);
            this.cachedEdgeTerms.CacheItemDiscarded += (sender, args) => this.DeallocateImage(args.DiscardedValue);
            this.shapeModel = newShapeModel;
            this.imageSize = newImageSize;
        }
        public void CalculateShapeTerms(ShapeModel model, ShapeConstraints constraintsSet, Image2D<ObjectBackgroundTerm> result)
        {
            if (model == null)
                throw new ArgumentNullException("model");
            if (constraintsSet == null)
                throw new ArgumentNullException("constraintsSet");
            if (result == null)
                throw new ArgumentNullException("result");
            if (model.Structure != constraintsSet.ShapeStructure)
                throw new ArgumentException("Shape model and shape constraints correspond to different shape structures.");

            if (model != this.shapeModel || result.Rectangle.Size != this.imageSize)
                this.SetTarget(model, result.Rectangle.Size);

            for (int x = 0; x < imageSize.Width; ++x)
                for (int y = 0; y < imageSize.Height; ++y)
                    result[x, y] = new ObjectBackgroundTerm(Double.PositiveInfinity, 0);

            for (int edgeIndex = 0; edgeIndex < this.shapeModel.Structure.Edges.Count; ++edgeIndex)
            {
                ShapeEdge edge = this.shapeModel.Structure.Edges[edgeIndex];
                VertexConstraints vertexConstraints1 = constraintsSet.VertexConstraints[edge.Index1];
                VertexConstraints vertexConstraints2 = constraintsSet.VertexConstraints[edge.Index2];
                EdgeConstraints edgeConstraints = constraintsSet.EdgeConstraints[edgeIndex];

                Image2D<ObjectBackgroundTerm> edgeTerms;
                EdgeDescription edgeDescription = new EdgeDescription(
                    vertexConstraints1, vertexConstraints2, edgeConstraints);
                if (!this.cachedEdgeTerms.TryGetValue(edgeDescription, out edgeTerms))
                {
                    edgeTerms = this.AllocateImage();
                    this.cachedEdgeTerms.Add(edgeDescription, edgeTerms);

                    Polygon convexHull = constraintsSet.GetConvexHullForVertexPair(edge.Index1, edge.Index2);

                    Parallel.For(
                        0,
                        imageSize.Width,
                        x =>
                        {
                            for (int y = 0; y < imageSize.Height; ++y)
                            {
                                Vector pointAsVec = new Vector(x, y);
                                double minDistanceSqr, maxDistanceSqr;
                                MinMaxDistanceForEdge(
                                    pointAsVec,
                                    convexHull,
                                    vertexConstraints1,
                                    vertexConstraints2,
                                    out minDistanceSqr,
                                    out maxDistanceSqr);

                                edgeTerms[x, y] = new ObjectBackgroundTerm(
                                    this.shapeModel.CalculateObjectPenaltyForEdge(
                                        minDistanceSqr, edgeConstraints.MaxWidth),
                                    this.shapeModel.CalculateBackgroundPenaltyForEdge(
                                        maxDistanceSqr, edgeConstraints.MinWidth));
                            }
                        });
                }

                for (int x = 0; x < imageSize.Width; ++x)
                    for (int y = 0; y < imageSize.Height; ++y)
                        result[x, y] = new ObjectBackgroundTerm(
                            Math.Min(result[x, y].ObjectTerm, edgeTerms[x, y].ObjectTerm),
                            Math.Max(result[x, y].BackgroundTerm, edgeTerms[x, y].BackgroundTerm));
            }
        }
        public Shape MutateShape(Shape shape, ShapeModel shapeModel, Size imageSize, double normalizedTemperature)
        {
            if (shape == null)
                throw new ArgumentNullException("shape");
            if (shapeModel == null)
                throw new ArgumentNullException("shapeModel");

            double maxImageSideSize = Math.Max(imageSize.Width, imageSize.Height);
            Shape mutatedShape;

            double weightSum =
                this.edgeWidthMutationWeight +
                this.edgeLengthMutationWeight +
                this.edgeAngleMutationWeight +
                this.shapeTranslationWeight +
                this.shapeScaleWeight;
            if (weightSum <= 0)
                throw new InvalidOperationException("At least one type of mutation should have non-zero probability weight.");
            double rand = Random.Double(0, weightSum);

            // Shape part mutation
            if (rand < this.edgeWidthMutationWeight + this.edgeLengthMutationWeight + this.edgeAngleMutationWeight)
            {
                ShapeLengthAngleRepresentation representation = shape.GetLengthAngleRepresentation();
                int randomEdge = Random.Int(shape.Structure.Edges.Count);
                
                // Mutate edge width
                if (rand < this.edgeWidthMutationWeight)
                {
                    double widthShiftStdDev = maxImageSideSize * this.edgeWidthMutationPower * normalizedTemperature;
                    const double minWidth = 3;
                    double widthShift = Random.Normal(0, widthShiftStdDev, -shape.EdgeWidths[randomEdge] + minWidth);
                    representation.EdgeWidths[randomEdge] += widthShift;
                }
                // Mutate edge length
                else if (rand < this.edgeWidthMutationWeight + this.edgeLengthMutationWeight)
                {
                    double lengthShiftStdDev = maxImageSideSize * this.edgeLengthMutationPower * normalizedTemperature;
                    double lengthShift = Random.Normal(0, lengthShiftStdDev);
                    representation.EdgeLengths[randomEdge] += lengthShift;
                }
                // Mutate edge angle
                else
                {
                    double angleShiftStdDev = this.edgeAngleMutationPower * normalizedTemperature;
                    double angleShift = Random.Normal(0, angleShiftStdDev);
                    representation.EdgeAngles[randomEdge] += angleShift;
                } 

                mutatedShape = shapeModel.BuildShapeFromLengthAngleRepresentation(representation);
            }
            // Whole shape mutation
            else
            {
                rand -= this.edgeWidthMutationWeight + this.edgeLengthMutationWeight + this.edgeAngleMutationWeight;
                mutatedShape = shape.Clone();

                // Translate shape
                if (rand < this.shapeTranslationWeight)
                {
                    Vector maxTopLeftShift = new Vector(Double.NegativeInfinity, Double.NegativeInfinity);
                    Vector minBottomRightShift = new Vector(Double.PositiveInfinity, Double.PositiveInfinity);
                    for (int i  = 0; i < mutatedShape.VertexPositions.Count; ++i)
                    {
                        maxTopLeftShift.X = Math.Max(maxTopLeftShift.X, -mutatedShape.VertexPositions[i].X);
                        maxTopLeftShift.Y = Math.Max(maxTopLeftShift.Y, -mutatedShape.VertexPositions[i].Y);
                        minBottomRightShift.X = Math.Min(minBottomRightShift.X, imageSize.Width - mutatedShape.VertexPositions[i].X);
                        minBottomRightShift.Y = Math.Min(minBottomRightShift.Y, imageSize.Height - mutatedShape.VertexPositions[i].Y);
                    }

                    double translationStdDev = maxImageSideSize * this.shapeTranslationPower * normalizedTemperature;
                    Vector shift = new Vector(Random.Normal(0, translationStdDev), Random.Normal(0, translationStdDev));
                    shift = MathHelper.Trunc(shift, maxTopLeftShift, minBottomRightShift);

                    for (int i = 0; i < mutatedShape.VertexPositions.Count; ++i)
                        mutatedShape.VertexPositions[i] += shift;
                }
                // Scale shape
                else
                {
                    Vector shapeCenter = shape.VertexPositions.Aggregate(Vector.Zero, (a, c) => a + c) / shape.VertexPositions.Count;
                    double scaleStdDev = this.shapeScalePower * normalizedTemperature;
                    const double minScale = 0.1;
                    double scale = Random.Normal(1.0, scaleStdDev, minScale);
                    for (int i = 0; i < mutatedShape.VertexPositions.Count; ++i)
                        mutatedShape.VertexPositions[i] = shapeCenter + scale * (mutatedShape.VertexPositions[i] - shapeCenter);
                }
            }

            Debug.Assert(mutatedShape != null);
            return mutatedShape;
        }
        public void CalculateShapeTerms(ShapeModel model, ShapeConstraints constraintsSet, Image2D <ObjectBackgroundTerm> result)
        {
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }
            if (constraintsSet == null)
            {
                throw new ArgumentNullException("constraintsSet");
            }
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }
            if (model.Structure != constraintsSet.ShapeStructure)
            {
                throw new ArgumentException("Shape model and shape constraints correspond to different shape structures.");
            }

            if (model != this.shapeModel || result.Rectangle.Size != this.imageSize)
            {
                this.SetTarget(model, result.Rectangle.Size);
            }

            for (int x = 0; x < imageSize.Width; ++x)
            {
                for (int y = 0; y < imageSize.Height; ++y)
                {
                    result[x, y] = new ObjectBackgroundTerm(Double.PositiveInfinity, 0);
                }
            }

            for (int edgeIndex = 0; edgeIndex < this.shapeModel.Structure.Edges.Count; ++edgeIndex)
            {
                ShapeEdge         edge = this.shapeModel.Structure.Edges[edgeIndex];
                VertexConstraints vertexConstraints1 = constraintsSet.VertexConstraints[edge.Index1];
                VertexConstraints vertexConstraints2 = constraintsSet.VertexConstraints[edge.Index2];
                EdgeConstraints   edgeConstraints    = constraintsSet.EdgeConstraints[edgeIndex];

                Image2D <ObjectBackgroundTerm> edgeTerms;
                EdgeDescription edgeDescription = new EdgeDescription(
                    vertexConstraints1, vertexConstraints2, edgeConstraints);
                if (!this.cachedEdgeTerms.TryGetValue(edgeDescription, out edgeTerms))
                {
                    edgeTerms = this.AllocateImage();
                    this.cachedEdgeTerms.Add(edgeDescription, edgeTerms);

                    Polygon convexHull = constraintsSet.GetConvexHullForVertexPair(edge.Index1, edge.Index2);

                    Parallel.For(
                        0,
                        imageSize.Width,
                        x =>
                    {
                        for (int y = 0; y < imageSize.Height; ++y)
                        {
                            Vector pointAsVec = new Vector(x, y);
                            double minDistanceSqr, maxDistanceSqr;
                            MinMaxDistanceForEdge(
                                pointAsVec,
                                convexHull,
                                vertexConstraints1,
                                vertexConstraints2,
                                out minDistanceSqr,
                                out maxDistanceSqr);

                            edgeTerms[x, y] = new ObjectBackgroundTerm(
                                this.shapeModel.CalculateObjectPenaltyForEdge(
                                    minDistanceSqr, edgeConstraints.MaxWidth),
                                this.shapeModel.CalculateBackgroundPenaltyForEdge(
                                    maxDistanceSqr, edgeConstraints.MinWidth));
                        }
                    });
                }

                for (int x = 0; x < imageSize.Width; ++x)
                {
                    for (int y = 0; y < imageSize.Height; ++y)
                    {
                        result[x, y] = new ObjectBackgroundTerm(
                            Math.Min(result[x, y].ObjectTerm, edgeTerms[x, y].ObjectTerm),
                            Math.Max(result[x, y].BackgroundTerm, edgeTerms[x, y].BackgroundTerm));
                    }
                }
            }
        }