Exemple #1
0
        public Spectra ReadSpectra(float x0, float y0, int halfWidth, int bgHalfWidth, int bgGap, PixelCombineMethod bgMethod)
        {
            var rv = new Spectra()
            {
                SignalAreaWidth         = 2 * halfWidth,
                BackgroundAreaHalfWidth = bgHalfWidth,
                BackgroundAreaGap       = bgGap,
                MaxPixelValue           = m_Image.Pixelmap.MaxSignalValue
            };

            int xFrom = int.MaxValue;
            int xTo   = int.MinValue;

            // Find the destination pixel range at the destination horizontal
            PointF p1 = m_Mapper.GetDestCoords(x0, y0);

            rv.ZeroOrderPixelNo = (int)Math.Round(p1.X);

            for (float x = p1.X - m_Mapper.MaxDestDiagonal; x < p1.X + m_Mapper.MaxDestDiagonal; x++)
            {
                PointF p = m_Mapper.GetSourceCoords(x, p1.Y);
                if (m_SourceVideoFrame.Contains(p))
                {
                    int xx = (int)x;

                    if (xx < xFrom)
                    {
                        xFrom = xx;
                    }
                    if (xx > xTo)
                    {
                        xTo = xx;
                    }
                }
            }

            m_BgValues     = new float[xTo - xFrom + 1];
            m_BgPixelCount = new uint[xTo - xFrom + 1];
            m_BgValuesList = new List <float> [xTo - xFrom + 1];
            rv.InitialisePixelArray(xTo - xFrom + 1);

            // Get all readings in the range
            for (int x = xFrom; x <= xTo; x++)
            {
                var point = new SpectraPoint();
                point.PixelNo             = x;
                point.RawSignalPixelCount = 0;

                for (int z = -halfWidth; z <= halfWidth; z++)
                {
                    PointF p  = m_Mapper.GetSourceCoords(x, p1.Y + z);
                    int    xx = (int)Math.Round(p.X);
                    int    yy = (int)Math.Round(p.Y);

                    if (m_SourceVideoFrame.Contains(xx, yy))
                    {
                        float sum       = 0;
                        int   numPoints = 0;
                        for (float kx = -0.4f; kx < 0.5f; kx += 0.2f)
                        {
                            for (float ky = -0.4f; ky < 0.5f; ky += 0.2f)
                            {
                                p = m_Mapper.GetSourceCoords(x + kx, p1.Y + ky + z);
                                int xxx = (int)Math.Round(p.X);
                                int yyy = (int)Math.Round(p.Y);
                                if (m_SourceVideoFrame.Contains(xxx, yyy))
                                {
                                    sum += (m_Image.Pixelmap[xxx, yyy] * m_PixelValueCoeff);
                                    numPoints++;
                                }
                            }
                        }
                        float destPixVal = (sum / numPoints);
                        point.RawValue += destPixVal;
                        point.RawSignalPixelCount++;
                        rv.Pixels[x - xFrom, z + bgHalfWidth + bgGap + halfWidth - 1] = destPixVal;
                    }
                }

                point.RawSignal = point.RawValue;
                rv.Points.Add(point);

                #region Reads background
                if (bgMethod == PixelCombineMethod.Average)
                {
                    ReadAverageBackgroundForPixelIndex(halfWidth, bgHalfWidth, bgGap, x, p1.Y, x - xFrom);
                }
                else if (bgMethod == PixelCombineMethod.Median)
                {
                    ReadMedianBackgroundForPixelIndex(halfWidth, bgHalfWidth, bgGap, x, p1.Y, x - xFrom);
                }
                #endregion
            }

            // Apply background
            for (int i = 0; i < rv.Points.Count; i++)
            {
                SpectraPoint point = rv.Points[i];

                if (bgMethod == PixelCombineMethod.Average)
                {
                    point.RawBackgroundPerPixel = GetAverageBackgroundValue(point.PixelNo, xFrom, xTo, bgHalfWidth);
                }
                else if (bgMethod == PixelCombineMethod.Median)
                {
                    point.RawBackgroundPerPixel = GetMedianBackgroundValue(point.PixelNo, xFrom, xTo, bgHalfWidth);
                }

                for (int z = -halfWidth - bgGap - bgHalfWidth + 1; z < -halfWidth - bgGap; z++)
                {
                    rv.Pixels[i, z + bgHalfWidth + bgGap + halfWidth - 1] = point.RawBackgroundPerPixel;
                }

                for (int z = halfWidth + bgGap + 1; z < halfWidth + bgGap + bgHalfWidth + 1; z++)
                {
                    rv.Pixels[i, z + bgHalfWidth + bgGap + halfWidth - 1] = point.RawBackgroundPerPixel;
                }

                point.RawValue -= point.RawBackgroundPerPixel * point.RawSignalPixelCount;

                if (point.RawValue < 0 && !TangraConfig.Settings.Spectroscopy.AllowNegativeValues)
                {
                    point.RawValue = 0;
                }
            }

            rv.MaxSpectraValue = (uint)Math.Ceiling(rv.Points.Where(x => x.PixelNo > rv.ZeroOrderPixelNo + 20).Select(x => x.RawValue).Max());

            return(rv);
        }
        internal MasterSpectra ComputeResult(
            List <Spectra> allFramesSpectra,
            PixelCombineMethod frameCombineMethod,
            bool useFineAdjustments,
            int?alignmentAbsorptionLinePos,
            int startingFrameIndex  = 1,
            int?frameCountToProcess = null)
        {
            var masterSpectra = new MasterSpectra();

            if (allFramesSpectra.Count > 0)
            {
                masterSpectra.ZeroOrderPixelNo        = allFramesSpectra[0].ZeroOrderPixelNo;
                masterSpectra.ZeroOrderFWHM           = allFramesSpectra[0].ZeroOrderFWHM;
                masterSpectra.SignalAreaWidth         = allFramesSpectra[0].SignalAreaWidth;
                masterSpectra.BackgroundAreaHalfWidth = allFramesSpectra[0].BackgroundAreaHalfWidth;
                masterSpectra.BackgroundAreaGap       = allFramesSpectra[0].BackgroundAreaGap;
                masterSpectra.MaxPixelValue           = allFramesSpectra[0].MaxPixelValue;
                masterSpectra.MaxSpectraValue         = allFramesSpectra[0].MaxSpectraValue;
                for (int i = 0; i < allFramesSpectra[0].Points.Count; i++)
                {
                    masterSpectra.Points.Add(new SpectraPoint(allFramesSpectra[0].Points[i]));
                }

                int pixelArrWidth  = allFramesSpectra[0].Pixels.GetLength(0);
                int pixelArrHeight = allFramesSpectra[0].Pixels.GetLength(1);

                masterSpectra.InitialisePixelArray(pixelArrWidth);
                masterSpectra.CombinedMeasurements = 1;

                var originalMasterPoints = new List <SpectraPoint>();
                originalMasterPoints.AddRange(masterSpectra.Points);

                if (frameCountToProcess == null && startingFrameIndex == 1)
                {
                    frameCountToProcess = allFramesSpectra.Count;
                }

                if (frameCombineMethod == PixelCombineMethod.Average)
                {
                    float fwhmSum   = 0;
                    int   fwhmCount = 0;

                    for (int i = startingFrameIndex; i < startingFrameIndex + frameCountToProcess - 1; i++)
                    {
                        if (i < 0 || i > allFramesSpectra.Count - 1)
                        {
                            continue;
                        }

                        Spectra nextSpectra = allFramesSpectra[i];
                        masterSpectra.RawMeasurements.Add(nextSpectra);
                        if (!float.IsNaN(nextSpectra.ZeroOrderFWHM))
                        {
                            fwhmSum += nextSpectra.ZeroOrderFWHM;
                            fwhmCount++;
                        }

                        int nextSpectraFirstPixelNo = nextSpectra.Points[0].PixelNo;
                        int deltaIndex = nextSpectra.ZeroOrderPixelNo - masterSpectra.ZeroOrderPixelNo + masterSpectra.Points[0].PixelNo - nextSpectraFirstPixelNo;

                        int lineAlignOffset = 0;
                        if (alignmentAbsorptionLinePos.HasValue)
                        {
                            int roughLinePos = deltaIndex + alignmentAbsorptionLinePos.Value;
                            List <SpectraPoint> pointsInRegion = nextSpectra.Points.Where(x => Math.Abs(x.PixelNo - roughLinePos) < 10).ToList();
                            float[]             arrPixelNo     = pointsInRegion.Select(x => (float)x.PixelNo).ToArray();
                            float[]             arrPixelValues = pointsInRegion.Select(x => x.RawValue).ToArray();
                            Array.Sort(arrPixelValues, arrPixelNo);
                            lineAlignOffset = (int)arrPixelNo[0] - roughLinePos;
                        }

                        int bestOffset = 0;

                        if (useFineAdjustments)
                        {
                            float bestOffsetValue = float.MaxValue;

                            for (int probeOffset = -2; probeOffset <= 2; probeOffset++)
                            {
                                float currOffsetValue = 0;
                                for (int j = 0; j < masterSpectra.Points.Count; j++)
                                {
                                    int indexNextSpectra = deltaIndex + j + probeOffset + lineAlignOffset;
                                    if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                                    {
                                        currOffsetValue += Math.Abs(originalMasterPoints[j].RawValue - nextSpectra.Points[indexNextSpectra].RawValue);
                                    }
                                }

                                if (currOffsetValue < bestOffsetValue)
                                {
                                    bestOffsetValue = currOffsetValue;
                                    bestOffset      = probeOffset;
                                }
                            }
                        }

                        for (int j = 0; j < masterSpectra.Points.Count; j++)
                        {
                            int indexNextSpectra = deltaIndex + j + bestOffset + lineAlignOffset;
                            if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                            {
                                masterSpectra.Points[j].RawValue            += nextSpectra.Points[indexNextSpectra].RawValue;
                                masterSpectra.Points[j].RawSignal           += nextSpectra.Points[indexNextSpectra].RawSignal;
                                masterSpectra.Points[j].RawSignalPixelCount += nextSpectra.Points[indexNextSpectra].RawSignalPixelCount;

                                for (int h = 0; h < pixelArrHeight; h++)
                                {
                                    masterSpectra.Pixels[j, h] += nextSpectra.Pixels[indexNextSpectra, h];
                                }
                            }
                        }
                    }

                    masterSpectra.ZeroOrderFWHM = fwhmSum / fwhmCount;

                    // Normalize per row width
                    for (int i = 0; i < masterSpectra.Points.Count; i++)
                    {
                        if (Math.Abs(masterSpectra.Points[i].RawSignalPixelCount) < 0.0001)
                        {
                            masterSpectra.Points[i].RawValue = 0;
                            for (int h = 0; h < pixelArrHeight; h++)
                            {
                                masterSpectra.Pixels[i, h] = 0;
                            }
                        }
                        else
                        {
                            masterSpectra.Points[i].RawValue = masterSpectra.Points[i].RawValue * masterSpectra.SignalAreaWidth / masterSpectra.Points[i].RawSignalPixelCount;
                            for (int h = 0; h < pixelArrHeight; h++)
                            {
                                masterSpectra.Pixels[i, h] /= masterSpectra.Points[i].RawSignalPixelCount;
                            }
                        }
                    }
                }
                else if (frameCombineMethod == PixelCombineMethod.Median)
                {
                    var valueLists = new List <float> [masterSpectra.Points.Count];
                    for (int j = 0; j < masterSpectra.Points.Count; j++)
                    {
                        valueLists[j] = new List <float>();
                    }

                    var signalLists = new List <float> [masterSpectra.Points.Count];
                    for (int j = 0; j < masterSpectra.Points.Count; j++)
                    {
                        signalLists[j] = new List <float>();
                    }

                    var fwhmList = new List <float>();
                    for (int j = 0; j < masterSpectra.Points.Count; j++)
                    {
                        fwhmList.Add(float.NaN);
                    }

                    for (int i = startingFrameIndex; i < startingFrameIndex + frameCountToProcess - 1; i++)
                    {
                        if (i < 0 || i > allFramesSpectra.Count - 1)
                        {
                            continue;
                        }

                        Spectra nextSpectra = allFramesSpectra[i];
                        masterSpectra.RawMeasurements.Add(nextSpectra);

                        int nextSpectraFirstPixelNo = nextSpectra.Points[0].PixelNo;
                        int deltaIndex = nextSpectra.ZeroOrderPixelNo - masterSpectra.ZeroOrderPixelNo + masterSpectra.Points[0].PixelNo - nextSpectraFirstPixelNo;

                        int lineAlignOffset = 0;
                        if (alignmentAbsorptionLinePos.HasValue)
                        {
                            int roughLinePos = deltaIndex + alignmentAbsorptionLinePos.Value;
                            List <SpectraPoint> pointsInRegion = nextSpectra.Points.Where(x => Math.Abs(x.PixelNo - roughLinePos) < 10).ToList();
                            float[]             arrPixelNo     = pointsInRegion.Select(x => (float)x.PixelNo).ToArray();
                            float[]             arrPixelValues = pointsInRegion.Select(x => x.RawValue).ToArray();
                            Array.Sort(arrPixelValues, arrPixelNo);
                            lineAlignOffset = (int)arrPixelNo[0] - roughLinePos;
                        }

                        int bestOffset = 0;

                        if (useFineAdjustments)
                        {
                            float bestOffsetValue = float.MaxValue;

                            for (int probeOffset = -2; probeOffset <= 2; probeOffset++)
                            {
                                float currOffsetValue = 0;
                                for (int j = 0; j < masterSpectra.Points.Count; j++)
                                {
                                    int indexNextSpectra = deltaIndex + j + probeOffset + lineAlignOffset;
                                    if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                                    {
                                        currOffsetValue += Math.Abs(originalMasterPoints[j].RawValue - nextSpectra.Points[indexNextSpectra].RawValue);
                                    }
                                }

                                if (currOffsetValue < bestOffsetValue)
                                {
                                    bestOffsetValue = currOffsetValue;
                                    bestOffset      = probeOffset;
                                }
                            }
                        }

                        for (int j = 0; j < masterSpectra.Points.Count; j++)
                        {
                            fwhmList[j] = nextSpectra.ZeroOrderFWHM;

                            int indexNextSpectra = deltaIndex + j + bestOffset + lineAlignOffset;
                            if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                            {
                                valueLists[j].Add(nextSpectra.Points[indexNextSpectra].RawValue);
                                signalLists[j].Add(nextSpectra.Points[indexNextSpectra].RawValue);

                                for (int h = 0; h < pixelArrHeight; h++)
                                {
                                    masterSpectra.Pixels[j, h] += nextSpectra.Pixels[indexNextSpectra, h];
                                }
                            }
                        }
                    }

                    fwhmList.Sort();
                    masterSpectra.ZeroOrderFWHM = fwhmList.Count == 0 ? float.NaN : fwhmList[fwhmList.Count / 2];

                    for (int i = 0; i < masterSpectra.Points.Count; i++)
                    {
                        valueLists[i].Sort();
                        signalLists[i].Sort();

                        masterSpectra.Points[i].RawValue            = valueLists[i].Count == 0 ? 0 : valueLists[i][valueLists[i].Count / 2];
                        masterSpectra.Points[i].RawSignal           = signalLists[i].Count == 0 ? 0 : signalLists[i][signalLists[i].Count / 2];
                        masterSpectra.Points[i].RawSignalPixelCount = signalLists[i].Count;

                        if (Math.Abs(masterSpectra.Points[i].RawSignalPixelCount) < 0.0001)
                        {
                            for (int h = 0; h < pixelArrHeight; h++)
                            {
                                masterSpectra.Pixels[i, h] = 0;
                            }
                        }
                        else
                        {
                            for (int h = 0; h < pixelArrHeight; h++)
                            {
                                masterSpectra.Pixels[i, h] /= masterSpectra.Points[i].RawSignalPixelCount;
                            }
                        }
                    }
                }
            }

            return(masterSpectra);
        }
Exemple #3
0
        public Spectra ReadSpectra(float x0, float y0, int halfWidth, int bgHalfWidth, int bgGap, PixelCombineMethod bgMethod)
        {
            var rv = new Spectra()
            {
                SignalAreaWidth = 2 * halfWidth,
                BackgroundAreaHalfWidth = bgHalfWidth,
                BackgroundAreaGap = bgGap,
                MaxPixelValue = m_Image.Pixelmap.MaxSignalValue
            };

            int xFrom = int.MaxValue;
            int xTo = int.MinValue;

            // Find the destination pixel range at the destination horizontal
            PointF p1 = m_Mapper.GetDestCoords(x0, y0);
            rv.ZeroOrderPixelNo = (int)Math.Round(p1.X);

            for (float x = p1.X - m_Mapper.MaxDestDiagonal; x < p1.X + m_Mapper.MaxDestDiagonal; x++)
            {
                PointF p = m_Mapper.GetSourceCoords(x, p1.Y);
                if (m_SourceVideoFrame.Contains(p))
                {
                    int xx = (int) x;

                    if (xx < xFrom) xFrom = xx;
                    if (xx > xTo) xTo = xx;
                }
            }

            m_BgValues = new float[xTo - xFrom + 1];
            m_BgPixelCount = new uint[xTo - xFrom + 1];
            m_BgValuesList = new List<float>[xTo - xFrom + 1];
            rv.InitialisePixelArray(xTo - xFrom + 1);

            // Get all readings in the range
            for (int x = xFrom; x <= xTo; x++)
            {
                var point = new SpectraPoint();
                point.PixelNo = x;
                point.RawSignalPixelCount = 0;

                for (int z = -halfWidth; z <= halfWidth; z++)
                {
                    PointF p = m_Mapper.GetSourceCoords(x, p1.Y +z);
                    int xx = (int)Math.Round(p.X);
                    int yy = (int)Math.Round(p.Y);

                    if (m_SourceVideoFrame.Contains(xx, yy))
                    {
                        float sum = 0;
                        int numPoints = 0;
                        for (float kx = -0.4f; kx < 0.5f; kx+=0.2f)
                        for (float ky = -0.4f; ky < 0.5f; ky += 0.2f)
                        {
                            p = m_Mapper.GetSourceCoords(x + kx, p1.Y + ky + z);
                            int xxx = (int)Math.Round(p.X);
                            int yyy = (int)Math.Round(p.Y);
                            if (m_SourceVideoFrame.Contains(xxx, yyy))
                            {
                                sum += (m_Image.Pixelmap[xxx, yyy] * m_PixelValueCoeff);
                                numPoints++;
                            }
                        }
                        float destPixVal = (sum/numPoints);
                        point.RawValue += destPixVal;
                        point.RawSignalPixelCount++;
                        rv.Pixels[x - xFrom, z + bgHalfWidth + bgGap + halfWidth - 1] = destPixVal;
                    }
                }

                point.RawSignal = point.RawValue;
                rv.Points.Add(point);

                #region Reads background
                if (bgMethod == PixelCombineMethod.Average)
                {
                    ReadAverageBackgroundForPixelIndex(halfWidth, bgHalfWidth, bgGap, x, p1.Y, x - xFrom);
                }
                else if (bgMethod == PixelCombineMethod.Median)
                {
                    ReadMedianBackgroundForPixelIndex(halfWidth, bgHalfWidth, bgGap, x, p1.Y, x - xFrom);
                }
                #endregion
            }

            // Apply background
            for (int i = 0; i < rv.Points.Count; i++)
            {
                SpectraPoint point = rv.Points[i];

                if (bgMethod == PixelCombineMethod.Average)
                {
                    point.RawBackgroundPerPixel = GetAverageBackgroundValue(point.PixelNo, xFrom, xTo, bgHalfWidth);
                }
                else if (bgMethod == PixelCombineMethod.Median)
                {
                    point.RawBackgroundPerPixel = GetMedianBackgroundValue(point.PixelNo, xFrom, xTo, bgHalfWidth);
                }

                for (int z = -halfWidth - bgGap - bgHalfWidth + 1; z < -halfWidth - bgGap; z++)
                    rv.Pixels[i, z + bgHalfWidth + bgGap + halfWidth - 1] = point.RawBackgroundPerPixel;

                for (int z = halfWidth + bgGap + 1; z < halfWidth + bgGap + bgHalfWidth + 1; z++)
                    rv.Pixels[i, z + bgHalfWidth + bgGap + halfWidth - 1] = point.RawBackgroundPerPixel;

                point.RawValue -= point.RawBackgroundPerPixel * point.RawSignalPixelCount;

                if (point.RawValue < 0 && !TangraConfig.Settings.Spectroscopy.AllowNegativeValues)
                    point.RawValue = 0;
            }

            rv.MaxSpectraValue = (uint)Math.Ceiling(rv.Points.Where(x => x.PixelNo > rv.ZeroOrderPixelNo + 20).Select(x => x.RawValue).Max());

            return rv;
        }
        internal MasterSpectra ComputeResult(
            List<Spectra> allFramesSpectra, 
            PixelCombineMethod frameCombineMethod, 
            bool useFineAdjustments, 
            int? alignmentAbsorptionLinePos,
            int startingFrameIndex = 1,
            int? frameCountToProcess = null)
        {
            var masterSpectra = new MasterSpectra();

            if (allFramesSpectra.Count > 0)
            {
                masterSpectra.ZeroOrderPixelNo = allFramesSpectra[0].ZeroOrderPixelNo;
                masterSpectra.ZeroOrderFWHM = allFramesSpectra[0].ZeroOrderFWHM;
                masterSpectra.SignalAreaWidth = allFramesSpectra[0].SignalAreaWidth;
                masterSpectra.BackgroundAreaHalfWidth = allFramesSpectra[0].BackgroundAreaHalfWidth;
                masterSpectra.BackgroundAreaGap = allFramesSpectra[0].BackgroundAreaGap;
                masterSpectra.MaxPixelValue = allFramesSpectra[0].MaxPixelValue;
                masterSpectra.MaxSpectraValue = allFramesSpectra[0].MaxSpectraValue;
                for (int i = 0; i < allFramesSpectra[0].Points.Count; i++)
                    masterSpectra.Points.Add(new SpectraPoint(allFramesSpectra[0].Points[i]));

                int pixelArrWidth = allFramesSpectra[0].Pixels.GetLength(0);
                int pixelArrHeight = allFramesSpectra[0].Pixels.GetLength(1);

                masterSpectra.InitialisePixelArray(pixelArrWidth);
                masterSpectra.CombinedMeasurements = 1;

                var originalMasterPoints = new List<SpectraPoint>();
                originalMasterPoints.AddRange(masterSpectra.Points);

                if (frameCountToProcess == null && startingFrameIndex == 1)
                    frameCountToProcess = allFramesSpectra.Count;

                if (frameCombineMethod == PixelCombineMethod.Average)
                {
                    float fwhmSum = 0;
                    int fwhmCount = 0;

                    for (int i = startingFrameIndex; i < startingFrameIndex + frameCountToProcess - 1; i++)
                    {
                        if (i < 0 || i > allFramesSpectra.Count - 1) continue;

                        Spectra nextSpectra = allFramesSpectra[i];
                        masterSpectra.RawMeasurements.Add(nextSpectra);
                        if (!float.IsNaN(nextSpectra.ZeroOrderFWHM))
                        {
                            fwhmSum += nextSpectra.ZeroOrderFWHM;
                            fwhmCount++;
                        }

                        int nextSpectraFirstPixelNo = nextSpectra.Points[0].PixelNo;
                        int deltaIndex = nextSpectra.ZeroOrderPixelNo - masterSpectra.ZeroOrderPixelNo + masterSpectra.Points[0].PixelNo - nextSpectraFirstPixelNo;

                        int lineAlignOffset = 0;
                        if (alignmentAbsorptionLinePos.HasValue)
                        {
                            int roughLinePos = deltaIndex + alignmentAbsorptionLinePos.Value;
                            List<SpectraPoint> pointsInRegion = nextSpectra.Points.Where(x => Math.Abs(x.PixelNo - roughLinePos) < 10).ToList();
                            float[] arrPixelNo = pointsInRegion.Select(x => (float)x.PixelNo).ToArray();
                            float[] arrPixelValues = pointsInRegion.Select(x => x.RawValue).ToArray();
                            Array.Sort(arrPixelValues, arrPixelNo);
                            lineAlignOffset = (int)arrPixelNo[0] - roughLinePos;
                        }

                        int bestOffset = 0;

                        if (useFineAdjustments)
                        {
                            float bestOffsetValue = float.MaxValue;

                            for (int probeOffset = -2; probeOffset <= 2; probeOffset++)
                            {
                                float currOffsetValue = 0;
                                for (int j = 0; j < masterSpectra.Points.Count; j++)
                                {
                                    int indexNextSpectra = deltaIndex + j + probeOffset + lineAlignOffset;
                                    if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                                    {
                                        currOffsetValue += Math.Abs(originalMasterPoints[j].RawValue - nextSpectra.Points[indexNextSpectra].RawValue);
                                    }
                                }

                                if (currOffsetValue < bestOffsetValue)
                                {
                                    bestOffsetValue = currOffsetValue;
                                    bestOffset = probeOffset;
                                }
                            }
                        }

                        for (int j = 0; j < masterSpectra.Points.Count; j++)
                        {
                            int indexNextSpectra = deltaIndex + j + bestOffset + lineAlignOffset;
                            if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                            {
                                masterSpectra.Points[j].RawValue += nextSpectra.Points[indexNextSpectra].RawValue;
                                masterSpectra.Points[j].RawSignal += nextSpectra.Points[indexNextSpectra].RawSignal;
                                masterSpectra.Points[j].RawSignalPixelCount += nextSpectra.Points[indexNextSpectra].RawSignalPixelCount;

                                for (int h = 0; h < pixelArrHeight; h++)
                                {
                                    masterSpectra.Pixels[j, h] += nextSpectra.Pixels[indexNextSpectra, h];
                                }
                            }
                        }
                    }

                    masterSpectra.ZeroOrderFWHM = fwhmSum / fwhmCount;

                    // Normalize per row width
                    for (int i = 0; i < masterSpectra.Points.Count; i++)
                    {
                        if (Math.Abs(masterSpectra.Points[i].RawSignalPixelCount) < 0.0001)
                        {
                            masterSpectra.Points[i].RawValue = 0;
                            for (int h = 0; h < pixelArrHeight; h++) masterSpectra.Pixels[i, h] = 0;
                        }
                        else
                        {
                            masterSpectra.Points[i].RawValue = masterSpectra.Points[i].RawValue * masterSpectra.SignalAreaWidth / masterSpectra.Points[i].RawSignalPixelCount;
                            for (int h = 0; h < pixelArrHeight; h++) masterSpectra.Pixels[i, h] /= masterSpectra.Points[i].RawSignalPixelCount;
                        }

                    }
                }
                else if (frameCombineMethod == PixelCombineMethod.Median)
                {
                    var valueLists = new List<float>[masterSpectra.Points.Count];
                    for (int j = 0; j < masterSpectra.Points.Count; j++) valueLists[j] = new List<float>();

                    var signalLists = new List<float>[masterSpectra.Points.Count];
                    for (int j = 0; j < masterSpectra.Points.Count; j++) signalLists[j] = new List<float>();

                    var fwhmList = new List<float>();
                    for (int j = 0; j < masterSpectra.Points.Count; j++) fwhmList.Add(float.NaN);

                    for (int i = startingFrameIndex; i < startingFrameIndex + frameCountToProcess - 1; i++)
                    {
                        if (i < 0 || i > allFramesSpectra.Count - 1) continue;

                        Spectra nextSpectra = allFramesSpectra[i];
                        masterSpectra.RawMeasurements.Add(nextSpectra);

                        int nextSpectraFirstPixelNo = nextSpectra.Points[0].PixelNo;
                        int deltaIndex = nextSpectra.ZeroOrderPixelNo - masterSpectra.ZeroOrderPixelNo + masterSpectra.Points[0].PixelNo - nextSpectraFirstPixelNo;

                        int lineAlignOffset = 0;
                        if (alignmentAbsorptionLinePos.HasValue)
                        {
                            int roughLinePos = deltaIndex + alignmentAbsorptionLinePos.Value;
                            List<SpectraPoint> pointsInRegion = nextSpectra.Points.Where(x => Math.Abs(x.PixelNo - roughLinePos) < 10).ToList();
                            float[] arrPixelNo = pointsInRegion.Select(x => (float)x.PixelNo).ToArray();
                            float[] arrPixelValues = pointsInRegion.Select(x => x.RawValue).ToArray();
                            Array.Sort(arrPixelValues, arrPixelNo);
                            lineAlignOffset = (int)arrPixelNo[0] - roughLinePos;
                        }

                        int bestOffset = 0;

                        if (useFineAdjustments)
                        {
                            float bestOffsetValue = float.MaxValue;

                            for (int probeOffset = -2; probeOffset <= 2; probeOffset++)
                            {
                                float currOffsetValue = 0;
                                for (int j = 0; j < masterSpectra.Points.Count; j++)
                                {
                                    int indexNextSpectra = deltaIndex + j + probeOffset + lineAlignOffset;
                                    if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                                    {
                                        currOffsetValue += Math.Abs(originalMasterPoints[j].RawValue - nextSpectra.Points[indexNextSpectra].RawValue);
                                    }
                                }

                                if (currOffsetValue < bestOffsetValue)
                                {
                                    bestOffsetValue = currOffsetValue;
                                    bestOffset = probeOffset;
                                }
                            }
                        }

                        for (int j = 0; j < masterSpectra.Points.Count; j++)
                        {
                            fwhmList[j] = nextSpectra.ZeroOrderFWHM;

                            int indexNextSpectra = deltaIndex + j + bestOffset + lineAlignOffset;
                            if (indexNextSpectra >= 0 && indexNextSpectra < nextSpectra.Points.Count)
                            {
                                valueLists[j].Add(nextSpectra.Points[indexNextSpectra].RawValue);
                                signalLists[j].Add(nextSpectra.Points[indexNextSpectra].RawValue);

                                for (int h = 0; h < pixelArrHeight; h++)
                                {
                                    masterSpectra.Pixels[j, h] += nextSpectra.Pixels[indexNextSpectra, h];
                                }
                            }
                        }
                    }

                    fwhmList.Sort();
                    masterSpectra.ZeroOrderFWHM = fwhmList.Count == 0 ? float.NaN : fwhmList[fwhmList.Count / 2];

                    for (int i = 0; i < masterSpectra.Points.Count; i++)
                    {
                        valueLists[i].Sort();
                        signalLists[i].Sort();

                        masterSpectra.Points[i].RawValue = valueLists[i].Count == 0 ? 0 : valueLists[i][valueLists[i].Count / 2];
                        masterSpectra.Points[i].RawSignal = signalLists[i].Count == 0 ? 0 : signalLists[i][signalLists[i].Count / 2];
                        masterSpectra.Points[i].RawSignalPixelCount = signalLists[i].Count;

                        if (Math.Abs(masterSpectra.Points[i].RawSignalPixelCount) < 0.0001)
                            for (int h = 0; h < pixelArrHeight; h++) masterSpectra.Pixels[i, h] = 0;
                        else
                            for (int h = 0; h < pixelArrHeight; h++) masterSpectra.Pixels[i, h] /= masterSpectra.Points[i].RawSignalPixelCount;
                    }
                }
            }

            return masterSpectra;
        }