Ejemplo n.º 1
0
        private static void EdgesOnlyOutputToStringBuilder(LineDetector marks, int profileIndex)
        {
            List <double> leftEdges  = marks.LeftEdgePositions;
            List <double> rightEdges = marks.RightEdgePositions;

            leftEdges.Sort();
            rightEdges.Sort();
            edgesOnlyOutput.AppendLine($"Profile {profileIndex}");
            edgesOnlyOutput.AppendLine($"   Right edges ({rightEdges.Count}), position in µm");
            foreach (var edge in rightEdges)
            {
                double deltaL = ThermalCorrection(edge);
                edgesOnlyOutput.AppendLine($"      {edge + deltaL:F3}");
            }
            edgesOnlyOutput.AppendLine($"   Left edges ({leftEdges.Count}), position in µm");
            foreach (var edge in leftEdges)
            {
                double deltaL = ThermalCorrection(edge);
                edgesOnlyOutput.AppendLine($"      {edge + deltaL:F3}");
            }
        }
Ejemplo n.º 2
0
        public static void Main(string[] args)
        {
            CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

            // parse command line arguments
            if (!CommandLine.Parser.Default.ParseArgumentsStrict(args, options))
            {
                Console.WriteLine("*** ParseArgumentsStrict returned false");
            }
            // consume the verbosity option
            if (options.BeQuiet == true)
            {
                ConsoleUI.BeSilent();
            }
            else
            {
                ConsoleUI.BeVerbatim();
            }
            // print a welcome message
            ConsoleUI.Welcome();
            ConsoleUI.WriteLine();
            // get the filename(s)
            fileNames = options.ListOfFileNames.ToArray();
            if (fileNames.Length == 0)
            {
                ConsoleUI.ErrorExit("!Missing input file name", 1);
            }

            // read all relevant scan data
            ConsoleUI.StartOperation("Reading NMM scan files");
            nmmFileNameObject = new NmmFileName(fileNames[0]);
            nmmFileNameObject.SetScanIndex(options.ScanIndex);
            theData = new NmmScanData(nmmFileNameObject);
            ConsoleUI.Done();

            // check if data present
            if (theData.MetaData.ScanStatus == ScanDirectionStatus.Unknown)
            {
                ConsoleUI.ErrorExit("!Unknown scan type", 4);
            }
            if (theData.MetaData.ScanStatus == ScanDirectionStatus.NoData)
            {
                ConsoleUI.ErrorExit("!No scan data present", 5);
            }

            // Check if requested channels are present in raw data
            if (!theData.ColumnPresent(options.XAxisDesignation))
            {
                ConsoleUI.ErrorExit($"!Requested channel {options.XAxisDesignation} not in data files", 2);
            }
            if (!theData.ColumnPresent(options.ZAxisDesignation))
            {
                ConsoleUI.ErrorExit($"!Requested channel {options.ZAxisDesignation} not in data files", 3);
            }

            // one must avoid referencing to an line outside of expected number of line marks
            if (options.RefLine < 0)
            {
                options.RefLine = 0;
            }
            if (options.RefLine >= options.ExpectedTargets)
            {
                options.RefLine = options.ExpectedTargets - 1;
            }

            // output scan files peculiaritues
            ConsoleUI.WriteLine();
            ConsoleUI.WriteLine($"SpuriousDataLines: {theData.MetaData.SpuriousDataLines}");
            ConsoleUI.WriteLine($"NumberOfGlitchedDataPoints: {theData.MetaData.NumberOfGlitchedDataPoints}");

            // some screen output
            ConsoleUI.WriteLine();
            ConsoleUI.WriteLine($"{theData.MetaData.NumberOfDataPoints} data lines with {theData.MetaData.NumberOfColumnsInFile} channels, organized in {theData.MetaData.NumberOfProfiles} profiles");
            ConsoleUI.WriteLine($"x-axis channel: {options.XAxisDesignation}");
            ConsoleUI.WriteLine($"z-axis channel: {options.ZAxisDesignation}");
            ConsoleUI.WriteLine($"Threshold: {options.Threshold}");
            ConsoleUI.WriteLine($"Morphological filter parameter: {options.Morpho}");
            if (options.LineScale)
            {
                ConsoleUI.WriteLine($"Expected number of line marks: {options.ExpectedTargets}");
                ConsoleUI.WriteLine($"Nominal scale division: {options.NominalDivision} um");
            }
            ConsoleUI.WriteLine();

            // evaluate the intensities for ALL profiles == the whole scan field
            ConsoleUI.StartOperation("Classifying intensity data");
            double[]           luminanceField = theData.ExtractProfile(options.ZAxisDesignation, 0, TopographyProcessType.ForwardOnly);
            IntensityEvaluator eval           = new IntensityEvaluator(luminanceField);

            ConsoleUI.Done();
            ConsoleUI.WriteLine();
            ConsoleUI.WriteLine($"Intensity range from {eval.MinIntensity} to {eval.MaxIntensity}");
            ConsoleUI.WriteLine($"Estimated bounds from {eval.LowerBound} to {eval.UpperBound}");
            double relativeSpan = (double)(eval.UpperBound - eval.LowerBound) / (double)(eval.MaxIntensity - eval.MinIntensity) * 100.0;

            ConsoleUI.WriteLine($"({relativeSpan:F1} % of full range)");
            ConsoleUI.WriteLine();

            // prepare object for the overall dimensional result
            LineScale result = new LineScale(options.ExpectedTargets);

            result.SetNominalValues(options.NominalDivision, options.RefLine);

            // wraps profiles extracted from NMM files to a more abstract structure: profiles
            List <IntensityProfile> profilesList = new List <IntensityProfile>();

            WarpNmmProfiles(TopographyProcessType.ForwardOnly, profilesList);
            if (theData.MetaData.ScanStatus == ScanDirectionStatus.ForwardAndBackward || theData.MetaData.ScanStatus == ScanDirectionStatus.ForwardAndBackwardJustified)
            {
                WarpNmmProfiles(TopographyProcessType.BackwardOnly, profilesList);
            }
            IntensityProfile[] profiles = profilesList.ToArray();

            // the loop over all profiles
            for (int profileIndex = 0; profileIndex < profiles.Length; profileIndex++)
            {
                if (!profiles[profileIndex].IsValid)
                {
                    ConsoleUI.ErrorExit($"!Profile {profileIndex} invalid", 6);
                }
                Classifier   classifier = new Classifier(profiles[profileIndex].Zvalues);
                int[]        skeleton   = classifier.GetSegmentedProfile(options.Threshold, eval.LowerBound, eval.UpperBound);
                MorphoFilter filter     = new MorphoFilter(skeleton);
                skeleton = filter.FilterWithParameter(options.Morpho);
                LineDetector marks = new LineDetector(skeleton, profiles[profileIndex].Xvalues);
                if (options.LineScale)
                {
                    ConsoleUI.WriteLine($"profile: {profileIndex+1,3} with {marks.LineCount} line marks {(marks.LineCount != options.ExpectedTargets ? "*" : " ")}");
                }
                result.UpdateSample(marks.LineMarks, options.RefLine);
                if (options.EdgeOnly)
                {
                    ConsoleUI.WriteLine($"profile: {profileIndex+1,3} with L:{marks.LeftEdgePositions.Count} R:{marks.RightEdgePositions.Count}");
                    EdgesOnlyOutputToStringBuilder(marks, profileIndex);
                }
            }

            #region output
            // prepare output
            string        outFormater = $"F{options.Precision}";
            StringBuilder sb          = new StringBuilder();
            sb.AppendLine($"{ConsoleUI.WelcomeMessage}");
            sb.AppendLine($"InputFile            = {theData.MetaData.BaseFileName}");
            // section for sample specific metadata
            sb.AppendLine($"SampleIdentifier     = {theData.MetaData.SampleIdentifier}");
            sb.AppendLine($"SampleSpecies        = {theData.MetaData.SampleSpecies}");
            sb.AppendLine($"SampleSpecification  = {theData.MetaData.SampleSpecification}");
            if (options.LineScale)
            {
                sb.AppendLine($"ExpectedLineMarks    = {options.ExpectedTargets}");
                sb.AppendLine($"NominalDivision      = {options.NominalDivision} µm");
                sb.AppendLine($"ScaleType            = {result.ScaleType}");
            }
            sb.AppendLine($"ThermalExpansion     = {options.Alpha.ToString("E2")} 1/K");
            // scan file specific data
            sb.AppendLine($"NumberOfScans        = {theData.MetaData.NumberOfScans}");
            sb.AppendLine($"ScanIndex            = {theData.MetaData.ScanIndex}");
            sb.AppendLine($"PointsPerProfile     = {theData.MetaData.NumberOfDataPoints}");
            sb.AppendLine($"Profiles             = {theData.MetaData.NumberOfProfiles}");
            sb.AppendLine($"InputChannels        = {theData.MetaData.NumberOfColumnsInFile}");
            sb.AppendLine($"PointSpacing         = {(theData.MetaData.ScanFieldDeltaX * 1e6).ToString("F4")} µm");
            sb.AppendLine($"ProfileSpacing       = {(theData.MetaData.ScanFieldDeltaY * 1e6).ToString("F4")} µm");
            sb.AppendLine($"ScanFieldCenterX     = {theData.MetaData.ScanFieldCenterX * 1000:F1} mm");
            sb.AppendLine($"ScanFieldCenterY     = {theData.MetaData.ScanFieldCenterY * 1000:F1} mm");
            sb.AppendLine($"ScanFieldCenterZ     = {theData.MetaData.ScanFieldCenterZ * 1000:F1} mm");
            sb.AppendLine($"AngularOrientation   = {theData.MetaData.ScanFieldRotation:F2}°");
            sb.AppendLine($"ScanSpeed            = {theData.MetaData.ScanSpeed} µm/s");
            sb.AppendLine($"GlitchedDataPoints   = {theData.MetaData.NumberOfGlitchedDataPoints}");
            sb.AppendLine($"SpuriousDataLines    = {theData.MetaData.SpuriousDataLines}");
            sb.AppendLine($"Probe                = {theData.MetaData.ProbeDesignation}");
            // evaluation parameters, user supplied
            sb.AppendLine($"X-AxisChannel        = {options.XAxisDesignation}");
            sb.AppendLine($"Z-AxisChannel        = {options.ZAxisDesignation}");
            sb.AppendLine($"Threshold            = {options.Threshold}");
            sb.AppendLine($"FilterParameter      = {options.Morpho}");
            if (options.LineScale)
            {
                sb.AppendLine($"ReferencedToLine     = {options.RefLine}");
                double maximumThermalCorrection = ThermalCorrection(result.LineMarks.Last().NominalPosition) - ThermalCorrection(result.LineMarks.First().NominalPosition);
                sb.AppendLine($"MaxThermalCorrection = {maximumThermalCorrection:F3} µm");
            }
            // auxiliary values
            sb.AppendLine($"MinimumIntensity     = {eval.MinIntensity}");
            sb.AppendLine($"MaximumIntensity     = {eval.MaxIntensity}");
            sb.AppendLine($"LowerPlateau         = {eval.LowerBound}");
            sb.AppendLine($"UpperPlateau         = {eval.UpperBound}");
            sb.AppendLine($"RelativeSpan         = {relativeSpan:F1} %");
            if (options.LineScale)
            {
                sb.AppendLine($"EvaluatedProfiles    = {result.SampleSize}");
            }
            // environmental data
            sb.AppendLine($"SampleTemperature    = {theData.MetaData.SampleTemperature.ToString("F3")} °C");
            sb.AppendLine($"AirTemperature       = {theData.MetaData.AirTemperature.ToString("F3")} °C");
            sb.AppendLine($"AirPressure          = {theData.MetaData.BarometricPressure.ToString("F0")} Pa");
            sb.AppendLine($"AirHumidity          = {theData.MetaData.RelativeHumidity.ToString("F1")} %");
            sb.AppendLine("======================");
            if (options.LineScale)
            {
                sb.AppendLine("1 : Line number (tag)");
                sb.AppendLine("2 : Nominal value / µm");
                sb.AppendLine("3 : Position deviation / µm");
                sb.AppendLine("4 : StdDev of line position values / µm");
                sb.AppendLine("5 : Range of line position values / µm");
                sb.AppendLine("6 : Line width / µm");
                sb.AppendLine("7 : StdDev of line widths / µm");
                sb.AppendLine("8 : Range of line widths / µm");
                sb.AppendLine("@@@@");
                if (result.SampleSize == 0)
                {
                    sb.AppendLine("*** No matching intensity pattern found ***");
                }
                else
                {
                    foreach (var line in result.LineMarks)
                    {
                        double deltaL = ThermalCorrection(line.NominalPosition);
                        sb.AppendLine($"{line.Tag.ToString().PadLeft(5)}" +
                                      $"{line.NominalPosition.ToString("F0").PadLeft(10)}" +
                                      $"{(line.Deviation + deltaL).ToString(outFormater).PadLeft(10)}" +
                                      $"{line.LineCenterStdDev.ToString(outFormater).PadLeft(10)}" +
                                      $"{line.LineCenterRange.ToString(outFormater).PadLeft(10)}" +
                                      $"{line.AverageLineWidth.ToString(outFormater).PadLeft(10)}" +
                                      $"{line.LineWidthStdDev.ToString(outFormater).PadLeft(10)}" +
                                      $"{(line.LineWidthRange).ToString(outFormater).PadLeft(10)}");
                    }
                }
            }
            if (options.EdgeOnly)
            {
                sb.Append(edgesOnlyOutput);
            }
            #endregion

            #region File output
            string outFileName;
            if (fileNames.Length >= 2)
            {
                outFileName = fileNames[1]; // path and extension must be explicitely given
            }
            else
            {
                outFileName = nmmFileNameObject.GetFreeFileNameWithIndex(".prn"); // extension will be added by WriteToFile()
            }
            ConsoleUI.WriteLine();
            ConsoleUI.WritingFile(outFileName);
            StreamWriter hOutFile = File.CreateText(outFileName);
            hOutFile.Write(sb);
            hOutFile.Close();
            ConsoleUI.Done();
            #endregion
        }