Esempio n. 1
0
        public int FindNotch(Chain chain, int tipPosition)
        {
            if (chain == null)
            {
                throw new ArgumentNullException(nameof(chain));
            }

            if (tipPosition < 0 || tipPosition > chain.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(tipPosition));
            }

            // TODO
            //if (_userSetNotch)
            //    throw new Exception("findNotch() [User selected notch in use]");

            // We're only going to be looking on the trailing edge
            // of the fin for the most significant notch, so we'll
            // build a source vector from that area
            int numTrailingEdgePts = chain.Length - tipPosition - 1 - 5;

            double[] src = new double[numTrailingEdgePts];

            //***1.95 - JHS
            // The following original code copies the ABSOLUTE Chain angles into source.
            // These are probably in the range -180 .. 180.  Is this causing problems with
            // angles that cross quadrants between +180 and -180 thus producing a LARGE
            // positive change in angle rather than a small negative one.  Should the
            // angles all be SHIFTED by +180 so the range is 0..360?  NO, this does not
            // work.  However, converting ONLY the negative angles (-91..-180) to positive
            // by adding 360 to them DOES WORK.  This way there is a continuous change in
            // positive outline orientation throughout all leftward extending notches
            // except narrow ones opening up and forward. In effect, we have removed potential
            // discontinuities from the chain signal being sent into the wavelet code.
            //memcpy(src, &((*_chain)[_tipPos + 1]), numTrailingEdgePts * sizeof(double));
            Array.Copy(chain.Data, tipPosition + 1, src, 0, numTrailingEdgePts);

            //***1.95 - code to convert negative angles
            for (int di = 0; di < numTrailingEdgePts; di++)
            {
                if (src[di] < -90)
                {
                    src[di] += 360.0; // force negative angles to be positive
                }
                //printf ("%5.2f\n",src[di]);
            }

            // Now set up the variables needed to perform a wavelet
            // transform on the chain
            double[,] continuousResult = new double[TransformLevels + 1, MathHelper.NextPowerOfTwo(numTrailingEdgePts)];

            // Now perform the transformation
            WIRWavelet.WL_FrwtVector(
                src,
                ref continuousResult,
                numTrailingEdgePts,
                TransformLevels,
                WaveletUtil.MZLowPassFilter,
                WaveletUtil.MZHighPassFilter);

            int i;

            for (i = 1; i <= TransformLevels; i++)
            {
                for (int j = 0; j < numTrailingEdgePts; j++)
                {
                    continuousResult[i, j] *= WaveletUtil.NormalizationCoeff(i);
                }
            }

            double[,] modMax = new double[TransformLevels, numTrailingEdgePts];

            // ..and find its local minima and maxima
            for (i = 0; i < TransformLevels; i++)
            {
                double[] temp = new double[numTrailingEdgePts];

                double[] continousExtract = WaveletUtil.Extract1DArray(continuousResult, i + 1, numTrailingEdgePts);

                WaveletUtil.ModulusMaxima(continousExtract, ref temp, numTrailingEdgePts);

                WaveletUtil.Patch1DArray(temp, ref modMax, i, numTrailingEdgePts);
            }

            int level = TransformLevels / 2;

            if (level < 1)
            {
                level = 1;
            }

            // First, we'll find some local minima at an intermediate level
            // to track.
            int        notchPosition = 0;
            List <int> mins          = new List <int>();

            while (level > 0)
            {
                for (i = 0; i < numTrailingEdgePts; i++)
                {
                    if (modMax[level, i] < 0.0)
                    {
                        mins.Add(i);
                    }
                }

                if (mins.Count <= 0)
                {
                    level--;
                    continue;
                }

                if (mins.Count == 1)
                {
                    notchPosition = 1;

                    if (0 == notchPosition)
                    {
                        notchPosition = chain.Length - tipPosition - 1;
                    }

                    break;
                }

                // yes, bad code
                break;
            }

            if (level == 0)
            {
                // Well, this really shouldn't happen: we've looked through
                // all the fine transform levels and haven't found any local
                // minima.  So, we'll just set the notch Position to the end
                // of the chain.
                notchPosition = chain.Length - tipPosition - 1;
            }

            if (notchPosition == 0)
            {
                // Now, we'll take the lowest few mins, and look at how
                // they change over the transform levels.

                double[] minVals = new double[mins.Count];

                for (i = 0; i < mins.Count; i++)
                {
                    minVals[i] = modMax[level, mins[i]];
                }

                Array.Sort(minVals);

                int numMinsToTrack;

                if ((int)mins.Count < NotchNumMinsToTrack)
                {
                    numMinsToTrack = mins.Count;
                }
                else
                {
                    numMinsToTrack = NotchNumMinsToTrack;
                }

                int[] positions = new int[numMinsToTrack];

                for (int count = 0; count < numMinsToTrack; count++)
                {
                    for (i = 0; i < mins.Count; i++)
                    {
                        if (minVals[count] == modMax[level, mins[i]])
                        {
                            positions[count] = mins[i];
                            break;
                        }
                    }
                }

                // Ok, now that we've got the few lowest mins,
                // let's find their corresponding positions in
                // a coarser level
                int coarserLevel = TransformLevels - 2;

                int correspondingPos;
                //double alphaMax, alpha;
                double difMax = 0.0, dif;
                bool   firstRun = true;

                for (i = 0; i < numMinsToTrack; i++)
                {
                    var extract = WaveletUtil.Extract1DArray(modMax, coarserLevel, numTrailingEdgePts);
                    correspondingPos = FindClosestMin(
                        5,
                        extract,
                        numTrailingEdgePts,
                        positions[i]);

                    // If we found a corresponding min in a coarser
                    // level...
                    if (-1 != correspondingPos)
                    {
                        //alpha = alphaK(
                        //		level + 1,
                        //		coarserLevel + 1,
                        //		modMax[level][positions[i]],
                        //		modMax[coarserLevel][correspondingPos]);
                        dif = Math.Abs(modMax[coarserLevel, correspondingPos]);
                        //+ fabs(modMax[level][positions[i]]);

                        if (firstRun)
                        {
                            firstRun      = false;
                            difMax        = dif;
                            notchPosition = positions[i];
                        }
                        else if (dif > difMax)
                        {
                            difMax        = dif;
                            notchPosition = positions[i];
                        }
                    }
                }

                if (firstRun)
                {
                    notchPosition = chain.Length - 1 - tipPosition;
                }
            }

            /*
             *          list<ZeroCrossing> zeroCrossings =
             *                  findZeroCrossings(continuousResult[level], numTrailingEdgePts);
             *
             *          list<ZeroCrossing>::iterator it = zeroCrossings.begin();
             *
             *          double maxDist = 0.0;
             *
             *          while (it != zeroCrossings.end()) {
             *                  if (it->leftMag < 0.0) {
             *                  double curDist = (fabs(it->leftMag) + fabs(it->rightMag));
             *                  if (curDist > maxDist) {
             *                          maxDist = curDist;
             *                          notchPosition = it->position;
             *                  }
             *                  }
             ++it;
             *          }
             *
             *          zeroCrossings.clear();
             */

            return(notchPosition + tipPosition);
        }
Esempio n. 2
0
        /// <summary>
        /// Tries to find the position of the upper lip on the outline.  If there's a mouth dent,
        /// it'll return that (and set the output parameter hasMouthDent to true).  If there isn't,
        /// it'll return the greatest max to the right of the mouth dent.
        /// </summary>
        /// <param name="chain"></param>
        /// <param name="chainPoints"></param>
        /// <param name="underNoseDentPosition"></param>
        /// <param name="hasMouthDent"></param>
        /// <param name="mouthDentPosition"></param>
        /// <returns></returns>
        public int FindUpperLip(Chain chain, FloatContour chainPoints, int underNoseDentPosition, out bool hasMouthDent, out int bottomLipProtrusion)
        {
            if (chain == null)
            {
                throw new ArgumentNullException(nameof(chain));
            }

            if (underNoseDentPosition < 0 || underNoseDentPosition > chain.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(underNoseDentPosition));
            }

            hasMouthDent = false;

            int numPointsAfterNoseDent = (int)Math.Round((chain.Length - underNoseDentPosition) * (1.0f - UpperLipEndPadding));

            // We're going to transform the whole chain
            int numPoints = chain.Length;

            // First, make a copy without the first value in the chain,
            // since the first value skews the rest of the chain and is
            // unnecessary for our purposes here
            double[] src = new double[numPoints - 1];

            Array.Copy(chain.Data, 1, src, 0, numPoints - 1);

            int nextPowOfTwo = MathHelper.NextPowerOfTwo(numPoints - 1);

            // Now set up the variables needed to perform a wavelet transform on the chain
            double[,] continuousResult = new double[UpperLipNumTransformsForMin + 1, nextPowOfTwo];
            WIRWavelet.WL_FrwtVector(src,
                                     ref continuousResult,
                                     numPoints - 1,
                                     UpperLipNumTransformsForMin,
                                     WaveletUtil.MZLowPassFilter,
                                     WaveletUtil.MZHighPassFilter);

            for (int i = 1; i <= UpperLipNumTransformsForMin; i++)
            {
                for (int j = 0; j < numPoints - 1; j++)
                {
                    continuousResult[i, j] *= WaveletUtil.NormalizationCoeff(i);
                }
            }

            double[] modMax = new double[numPoints - 1];

            for (int k = 0; k < numPoints - 1; k++)
            {
                continuousResult[UpperLipNumTransformsForMin, k] *= WaveletUtil.NormalizationCoeff(UpperLipNumTransformsForMin);
            }

            double[] continousExtract = WaveletUtil.Extract1DArray(continuousResult, UpperLipNumTransformsForMin, numPoints - 1);
            WaveletUtil.ModulusMaxima(continousExtract, ref modMax, numPoints - 1);

            // Now, let's see if we have any mins at the top transform level.  If we do, we probably have a mouth dent. If we don't,
            // we might not.
            for (int k = numPointsAfterNoseDent + underNoseDentPosition; k > underNoseDentPosition; k--)
            {
                if (modMax[k] < 0.0)
                {
                    hasMouthDent = true;
                    break;
                }
            }

            int mouthDentPosition = 0;

            if (hasMouthDent)
            {
                mouthDentPosition = FindNotch(chain, underNoseDentPosition + UpperLipTipPadding);

                // Check where it found the mouth dent -- if it's too close to the end, it's likely that it's
                // not the mouth, but something like the neck/etc.
                if (mouthDentPosition < underNoseDentPosition + numPointsAfterNoseDent)
                {
                    bottomLipProtrusion = FindTip(chain, mouthDentPosition + 20, mouthDentPosition - underNoseDentPosition - UpperLipTipPadding, UpperLipTipPadding);
                    return(mouthDentPosition);
                }
            }

            // Fake the mouth dent position as a starting point for finding the biggest max in the area
            mouthDentPosition = underNoseDentPosition + numPointsAfterNoseDent;

            int upperLipPosition = FindTip(chain, mouthDentPosition, mouthDentPosition - underNoseDentPosition - UpperLipTipPadding, UpperLipTipPadding);

            // These are fallback positions, so they're going to be off if we hit this if statement.
            if (upperLipPosition < underNoseDentPosition)
            {
                upperLipPosition = (int)Math.Round(mouthDentPosition + numPointsAfterNoseDent / 2.0f);
            }

            bottomLipProtrusion = upperLipPosition;

            return(upperLipPosition);
        }
Esempio n. 3
0
        //*******************************************************************
        //
        // int Outline::findTip()
        //
        //    Finds the tip as the index into the chain array
        //
        public int FindTip(Chain chain, int highPointId, int highPointPaddingLeft = DefaultTipHighPointPadding, int highPointPaddingRight = DefaultTipHighPointPadding)
        {
            if (chain == null)
            {
                throw new Exception("findTip() [*_chain]");
            }

            // TODO
            //if (_userSetTip)
            //    throw new Exception("findNotch() [User selected tip in use]");

            int numPoints = chain.Length;

            // First, make a copy without the first value in the chain,
            // since the first value skews the rest of the chain and is
            // unnecessary for our purposes here
            double[] src = new double[numPoints - 1];

            Array.Copy(chain.Data, 1, src, 0, numPoints - 1);

            int nextPowOfTwo = MathHelper.NextPowerOfTwo(numPoints - 1);

            // Now set up the variables needed to perform a wavelet transform on the chain
            double[,] continuousResult = new double[TransformLevels + 1, nextPowOfTwo];

            // Now perform the transformation
            WIRWavelet.WL_FrwtVector(src,
                                     ref continuousResult,
                                     numPoints - 1,
                                     TransformLevels,
                                     WaveletUtil.MZLowPassFilter,
                                     WaveletUtil.MZHighPassFilter);

            int
                tipPosition = 0,
                level       = TransformLevels;

            /*
             * while (!tipPosition && level > 0) { // Find the maxima of the
             * coefficients double *modMax = new double[numPoints - 1];
             *
             * for (int k = 0; k < numPoints - 1; k++) continuousResult[level][k]
             * *= normalizationCoeff(level);
             *
             * modulusMaxima(continuousResult[level], modMax, numPoints - 1);
             *
             * // Now, find the largest positive max, which we'll // assume is
             * the tip of the fin.
             *
             * double max = modMax[0]; for (int i = 1; i < numPoints - 1; i++) {
             * if (modMax[i] > max) { max = modMax[i]; tipPosition = i; } }
             *
             * level--; delete[] modMax; }
             */

            while (level > 1)
            {
                // Find the maxima of the coefficients
                double[] modMax = new double[numPoints - 1];

                for (int k = 0; k < numPoints - 1; k++)
                {
                    continuousResult[level, k] *= WaveletUtil.NormalizationCoeff(level);
                }

                double[] continousExtract = WaveletUtil.Extract1DArray(continuousResult, level, numPoints - 1);
                WaveletUtil.ModulusMaxima(continousExtract, ref modMax, numPoints - 1);

                // Now, find the largest positive max, which we'll
                // assume is the tip of the fin.

                if (tipPosition == 0)
                {
                    // original loop control is two line below
                    //double max = modMax[0]; //***0041TIP-JHS restricting range of search
                    //for (int i = 1; i < numPoints - 1; i++) { //***0041TIP-JHS restricting range of search
                    //***0041TIP - we now restrict the range for initial detection of the tip
                    // start at high point on fin and search within 150 point range along fin
                    //***1.6 - NOTE: there is an unintended consequence of the limmits imposed
                    // below.  If outlines are shortened to 50% or so of full size by having
                    // leading or trailing edges missing, the range of +/- 75 points is not
                    // a large enough range to capture tips of "MissingTip" fins in a consistent
                    // manner.  We need to rethink this, and possibly go back to the original
                    // and retest against a database of fins traced without this limit.
                    // The new "Iterative, Tip Shifting" mapping approach probably conpensates
                    // for tip placement better than this limit on detection does. -- JHS (8/2/2006)

                    int initialIndex = highPointId - highPointPaddingLeft;

                    if (initialIndex < 0)
                    {
                        initialIndex = 0;
                    }

                    double max = modMax[initialIndex]; //***1.6 - removed temporarily for tests

                    int endIndex = highPointId + highPointPaddingRight;

                    if (endIndex >= modMax.Length)
                    {
                        endIndex = modMax.Length - 1;
                    }

                    for (int i = initialIndex; i < endIndex; i++)
                    { //***1.6 - removed temporarily for tests
                        if (modMax[i] > max)
                        {
                            max         = modMax[i];
                            tipPosition = i;
                        }
                    }
                }
                else
                {
                    int maxSearchPoints = numPoints - 1;

                    if (highPointPaddingRight != DefaultTipHighPointPadding)
                    {
                        maxSearchPoints = highPointId + highPointPaddingRight;
                    }

                    // TODO: Left padding?

                    tipPosition = FindClosestMax(modMax, maxSearchPoints, tipPosition);
                }
                level--;
            }

            // Note that the actual tip position is +1 because we
            // ignored the first value
            Trace.WriteLine("Tip position: " + (tipPosition + 1));

            if ((tipPosition + 1) < 5)
            {
                Trace.WriteLine("Probable bad tip position.");
            }

            return(tipPosition + 1);
        }
Esempio n. 4
0
        /// <summary>
        /// Finds the Nasion, or the point on the head between the eyes.  It does this by
        /// finding the most significant minimum before the tip of the nose.
        /// </summary>
        /// <param name="chain"></param>
        /// <param name="tipPosition">Index of the tip of the nose</param>
        /// <returns>Integer index representing the position of the nasion</returns>
        public int FindNasion(Chain chain, int tipPosition)
        {
            if (chain == null)
            {
                throw new ArgumentNullException(nameof(chain));
            }

            if (tipPosition < 0 || tipPosition > chain.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(tipPosition));
            }

            int padding = (int)Math.Round(NasionPaddingPercentage * tipPosition);

            int numPoints = chain.Length;

            int numLeadingEdgePts = tipPosition - 2 * padding;

            double[] src = new double[numLeadingEdgePts];

            Array.Copy(chain.Data, padding, src, 0, numLeadingEdgePts);

            int nextPowOfTwo = MathHelper.NextPowerOfTwo(numPoints - 1);

            // Now set up the variables needed to perform a wavelet transform on the chain
            double[,] continuousResult = new double[NasionTransformLevels + 1, nextPowOfTwo];

            // Now perform the transformation
            WIRWavelet.WL_FrwtVector(src,
                                     ref continuousResult,
                                     numLeadingEdgePts,
                                     NasionTransformLevels,
                                     WaveletUtil.MZLowPassFilter,
                                     WaveletUtil.MZHighPassFilter);

            int
                nasionPosition = 0,
                level          = NasionTransformLevels;

            while (level > 1)
            {
                // Find the maxima of the coefficients
                double[] modMax = new double[numLeadingEdgePts];

                for (int k = 0; k < numPoints - 1; k++)
                {
                    continuousResult[level, k] *= WaveletUtil.NormalizationCoeff(level);
                }

                double[] continousExtract = WaveletUtil.Extract1DArray(continuousResult, level, numLeadingEdgePts);
                WaveletUtil.ModulusMaxima(continousExtract, ref modMax, numLeadingEdgePts);

                // Now, find the largest positive max, which we'll
                // assume is the tip of the fin.

                if (nasionPosition == 0)
                {
                    // Find the greatest min
                    double min = double.MaxValue; //***1.6 - removed temporarily for tests
                    for (int i = 0; i < numLeadingEdgePts; i++)
                    {                             //***1.6 - removed temporarily for tests
                        if (modMax[i] < min)
                        {
                            min            = modMax[i];
                            nasionPosition = i;
                        }
                    }
                }
                else
                {
                    var closestPosition = FindClosestMin(modMax, numLeadingEdgePts, nasionPosition);

                    if (closestPosition >= 0)
                    {
                        nasionPosition = closestPosition;
                    }
                }

                level--;
            }

            var correctedNasionPosition = nasionPosition + padding;

            Trace.WriteLine("Nasion position: " + correctedNasionPosition);

            if (correctedNasionPosition < 5)
            {
                Trace.WriteLine("Probable bad nasion position.");
            }

            return(correctedNasionPosition);
        }