/// <summary> /// Prepares the result of the operation. /// </summary> /// <returns>The resulting geometry.</returns> protected override ISpectralGeometry PrepareResult() { Dictionary <Segment, Dictionary <Int32, Int32> > segmentToClassDictionary = new Dictionary <Segment, Dictionary <Int32, Int32> >(); Dictionary <Int32, Int32> classToIndexDictionary = new Dictionary <Int32, Int32>(); IRaster referenceRaster = _referenceGeometry.Raster; Int32 sourceRowIndex, sourceColumnIndex; // map all values (and indices) to segments for (Int32 rowIndex = 0; rowIndex < referenceRaster.NumberOfRows; rowIndex++) { for (Int32 columnIndex = 0; columnIndex < referenceRaster.NumberOfColumns; columnIndex++) { Int32 referenceHashCode = 0; switch (referenceRaster.Format) { case RasterFormat.Floating: Double[] referenceFloatValues = referenceRaster.GetFloatValues(rowIndex, columnIndex); if (referenceFloatValues.All(value => value == 0)) { continue; } referenceHashCode = referenceFloatValues.Select(value => value.GetHashCode()).Aggregate((x, y) => (x << 1) ^ y); break; case RasterFormat.Integer: UInt32[] referenceValues = referenceRaster.GetValues(rowIndex, columnIndex); if (referenceValues.All(value => value == 0)) { continue; } referenceHashCode = referenceValues.Select(value => value.GetHashCode()).Aggregate((x, y) => (x << 1) ^ y); break; } // match the source coordinates if (!referenceRaster.IsMapped || !Source.Raster.IsMapped || Source.Raster.Coordinates.SequenceEqual(referenceRaster.Coordinates)) { sourceColumnIndex = columnIndex; sourceRowIndex = rowIndex; } else { Coordinate coordinate = referenceRaster.Mapper.MapCoordinate(rowIndex, columnIndex); Source.Raster.Mapper.MapRaster(coordinate, out sourceRowIndex, out sourceColumnIndex); } // check if the reference location is available in the source if (sourceRowIndex < 0 || sourceRowIndex >= Source.Raster.NumberOfRows || sourceColumnIndex < 0 || sourceColumnIndex >= Source.Raster.NumberOfColumns) { continue; } Segment segment = _segmentCollection.GetSegment(sourceRowIndex, sourceColumnIndex); // check if the segment was already mapped if (!segmentToClassDictionary.ContainsKey(segment)) { segmentToClassDictionary.Add(segment, new Dictionary <Int32, Int32>()); } if (!segmentToClassDictionary[segment].ContainsKey(referenceHashCode)) { segmentToClassDictionary[segment].Add(referenceHashCode, 0); } segmentToClassDictionary[segment][referenceHashCode]++; if (!classToIndexDictionary.ContainsKey(referenceHashCode)) { classToIndexDictionary.Add(referenceHashCode, rowIndex * referenceRaster.NumberOfColumns + columnIndex); } } } _segmentToIndexDictionary = new Dictionary <Segment, Int32>(); // filter indices for most frequent value foreach (Segment segment in segmentToClassDictionary.Keys) { Int32 maxValueHashCode = 0; foreach (Int32 referenceHashCode in segmentToClassDictionary[segment].Keys) { if (maxValueHashCode == 0 || segmentToClassDictionary[segment][referenceHashCode] > segmentToClassDictionary[segment][maxValueHashCode]) { maxValueHashCode = referenceHashCode; } } _segmentToIndexDictionary.Add(segment, classToIndexDictionary[maxValueHashCode]); } SetResultProperties(_referenceGeometry.Raster.Format, _referenceGeometry.Raster.NumberOfBands, Source.Raster.NumberOfRows, Source.Raster.NumberOfColumns, _referenceGeometry.Raster.RadiometricResolution, Source.Raster.Mapper, _referenceGeometry.Presentation, _referenceGeometry.Imaging); return(base.PrepareResult()); }
/// <summary> /// This will sample randomly from the raster, preventing duplicates. /// If the sampleSize is larger than this raster, this returns all of the values from the raster. /// If a "Sample" has been prefetched and stored in the Sample array, then this will return that. /// </summary> /// <param name="raster">The raster to obtain the values from.</param> /// <param name="sampleSize">Number of values to get.</param> /// <returns>List of random double values contained in the raster.</returns> public static List <double> GetRandomValues(this IRaster raster, int sampleSize) { if (raster.Sample != null) { return(raster.Sample.ToList()); } int numRows = raster.NumRows; int numCols = raster.NumColumns; List <double> result = new List <double>(); double noData = raster.NoDataValue; if (numRows * numCols < sampleSize) { for (int row = 0; row < numRows; row++) { for (int col = 0; col < numCols; col++) { double val = raster.Value[row, col]; if (val != noData) { result.Add(raster.Value[row, col]); } } } return(result); } Random rnd = new Random(DateTime.Now.Millisecond); if (numRows * (long)numCols < (long)sampleSize * 5 && numRows * (long)numCols < int.MaxValue) { // When the raster is only just barely larger than the sample size, // we want to prevent lots of repeat guesses that fail (hit the same previously sampled values). // We create a copy of all the values and sample from this reservoir while removing sampled values. List <double> resi = new List <double>(); for (int row = 0; row < numRows; row++) { for (int col = 0; col < numCols; col++) { double val = raster.Value[row, col]; if (val != noData) { resi.Add(val); } } } // int count = numRows * numCols; // this could failed if there's lot of noDataValues long longcount = raster.NumValueCells; int count = numRows * numCols; if (count < int.MaxValue) { count = (int)longcount; } for (int i = 0; i < sampleSize; i++) { if (resi.Count == 0) { break; } int indx = rnd.Next(count); result.Add(resi[indx]); resi.RemoveAt(indx); count--; } raster.Sample = result; return(result); } // Use a HashSet here, because it has O(1) lookup for preventing duplicates HashSet <long> exclusiveResults = new HashSet <long>(); int remaining = sampleSize; while (remaining > 0) { int row = rnd.Next(numRows); int col = rnd.Next(numCols); long index = (row * numCols) + col; if (exclusiveResults.Contains(index)) { continue; } exclusiveResults.Add(index); remaining--; } // Sorting is O(n ln(n)), but sorting once is better than using a SortedSet for previous lookups. List <long> sorted = exclusiveResults.ToList(); sorted.Sort(); // Sorted values are much faster to read than reading values in at random, since the file actually // is reading in a whole line at a time. If we can get more than one value from a line, then that // is better than getting one value, discarding the cache and then comming back later for the value // next to it. result = raster.GetValues(sorted); raster.Sample = result; return(result); }
/// <summary> /// Returns all spectral values at a specified row and column index. /// </summary> /// <param name="rowIndex">The zero-based row index of the values.</param> /// <param name="columnIndex">The zero-based column index of the values.</param> /// <returns>The array containing the spectral values for each band at the specified index.</returns> protected override UInt32[] ApplyGetValues(Int32 rowIndex, Int32 columnIndex) { return(_source.GetValues(_rowIndex + rowIndex, _columnIndex + columnIndex)); }