private static Bitmap DrawSample(ShapeModelSample sample)
 {
     return ImageHelpers.DrawShape(
         sample.ShapeModel.GridWidth,
         sample.ShapeModel.GridHeight,
         sample.ShapePartLocations,
         sample.ShapePartOrientations);
 }
        public ShapeModelSample[] Sample(double[][] traits, bool withNoise, bool ignoreLocationPrior)
        {
            Debug.Assert(traits[0].Length == this.TraitCount);

            int sampleCount = traits.Length;

            ShapeModelSample[] result = new ShapeModelSample[sampleCount];

            double[] shapeLocationMean = this.ShapeLocationMean.Sample();
            double[] shapeLocationPrecision = this.ShapeLocationPrecision.Sample();
            double[][][] shapePartOffsetMeanWeights = GetMean(this.ShapePartOffsetWeights); //this.ShapePartOffsetWeights.Sample();
            double[][][] shapePartLogScaleMeanWeights = GetMean(this.ShapePartLogScaleWeights);//this.ShapePartScaleWeights.Sample();
            double[][] shapePartAngleMeanWeights = GetMean(this.ShapePartAngleWeights);//this.ShapePartAngleWeights.Sample();
            double[][] shapePartOffsetPrecisions = GetMean(this.ShapePartOffsetPrecisions);//this.ShapePartOffsetPrecisions.Sample();
            double[][] shapePartLogScalePrecisions = GetMean(this.ShapePartLogScalePrecisions);//this.ShapePartScalePrecisions.Sample();
            double[] shapePartAnglePrecisions = GetMean(this.ShapePartAnglePrecisions);//this.ShapePartAnglePrecisions.Sample();

            for (int sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex)
            {
                // Sample locations from priors
                double locationX, locationY;
                if (ignoreLocationPrior)
                {
                    locationX = 0.5;
                    locationY = 0.5;
                }
                else
                {
                    locationX = Gaussian.Sample(shapeLocationMean[0], shapeLocationPrecision[0]);
                    locationY = Gaussian.Sample(shapeLocationMean[1], shapeLocationPrecision[1]);
                }

                // Sample shape part locations
                result[sampleIndex] = new ShapeModelSample() { ShapeModel = this, ShapePartLocations = new Vector[this.ShapePartCount], ShapePartOrientations = new PositiveDefiniteMatrix[this.ShapePartCount] };

                for (int shapePartIndex = 0; shapePartIndex < this.ShapePartCount; ++shapePartIndex)
                {
                    Vector traitVector = Vector.FromArray(traits[sampleIndex]);
                    double meanOffsetX = Vector.InnerProduct(traitVector, Vector.FromArray(shapePartOffsetMeanWeights[shapePartIndex][0]));
                    double meanOffsetY = Vector.InnerProduct(traitVector, Vector.FromArray(shapePartOffsetMeanWeights[shapePartIndex][1]));
                    double meanLogScaleX = Vector.InnerProduct(traitVector, Vector.FromArray(shapePartLogScaleMeanWeights[shapePartIndex][0]));
                    double meanLogScaleY = Vector.InnerProduct(traitVector, Vector.FromArray(shapePartLogScaleMeanWeights[shapePartIndex][1]));
                    double meanAngle = Vector.InnerProduct(traitVector, Vector.FromArray(shapePartAngleMeanWeights[shapePartIndex]));

                    double x = locationX + Gaussian.FromMeanAndPrecision(meanOffsetX, shapePartOffsetPrecisions[shapePartIndex][0]).Sample();
                    double y = locationY + Gaussian.FromMeanAndPrecision(meanOffsetY, shapePartOffsetPrecisions[shapePartIndex][1]).Sample();
                    double logScaleX = Gaussian.FromMeanAndPrecision(meanLogScaleX, shapePartLogScalePrecisions[shapePartIndex][0]).Sample();
                    double logScaleY = Gaussian.FromMeanAndPrecision(meanLogScaleY, shapePartLogScalePrecisions[shapePartIndex][1]).Sample();
                    double angle = Gaussian.FromMeanAndPrecision(meanAngle, shapePartAnglePrecisions[shapePartIndex]).Sample();

                    // Truncate samples to match the model constraints (not entirely correct)
                    angle = Math.Min(Math.PI / 4 - 0.01, angle);
                    angle = Math.Max(-Math.PI / 4 + 0.01, angle);

                    PositiveDefiniteMatrix orientation = ShapeFactors.MatrixFromAngleScale(logScaleX, logScaleY, angle);

                    result[sampleIndex].ShapePartLocations[shapePartIndex] = Vector.FromArray(x, y);
                    result[sampleIndex].ShapePartOrientations[shapePartIndex] = orientation;
                }

                var labels = new bool[this.GridWidth, this.GridHeight];
                var noisyLabels = new bool[this.GridWidth, this.GridHeight];
                for (int i = 0; i < this.GridWidth; ++i)
                {
                    for (int j = 0; j < this.GridHeight; ++j)
                    {
                        labels[i, j] = false;
                        for (int k = 0; k < this.ShapePartCount; ++k)
                        {
                            labels[i, j] |= ShapeFactors.LabelFromShape(
                                Vector.FromArray((i + 0.5) / this.GridWidth, (j + 0.5) / this.GridHeight),
                                result[sampleIndex].ShapePartLocations[k][0],
                                result[sampleIndex].ShapePartLocations[k][1],
                                result[sampleIndex].ShapePartOrientations[k]);
                        }

                        noisyLabels[i, j] = Rand.Double() >= this.ObservationNoiseProb ? labels[i, j] : !labels[i, j];
                    }
                }

                result[sampleIndex].Labels = withNoise ? noisyLabels : labels;
            }

            return result;
        }