Advertisement
NPSF3000

TextureFromPoints5 - Low Memory [Repost]

Jul 26th, 2012
198
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.82 KB | None | 0 0
  1. //This was taken down?
  2.  
  3. //This version demonstrates using a bit of trickery to write directly to file
  4. //- particularily useful in low memory situations.
  5. //However it's hard to paralelise andimprove beyond the base speed.
  6.  
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. using System.Drawing;
  11. using System.Drawing.Imaging;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16.  
  17. namespace TextureFromPoints5
  18. {
  19.     class Program
  20.     {
  21.         static void Main(string[] args)
  22.         {
  23.             //Introduction
  24.             if (false)
  25.             {
  26.                 //Let us explore the BMP Format  [http://en.wikipedia.org/wiki/BMP_file_format]
  27.                 var bmp = new Bitmap(1024, 1024, PixelFormat.Format32bppArgb);
  28.                 var ms = new MemoryStream();
  29.                 bmp.Save(ms, ImageFormat.Bmp);
  30.  
  31.                 ms.Position = 0;
  32.                 var br = new BinaryReader(ms);
  33.  
  34.                 //Bit Map Header:
  35.                 {
  36.                     //Identification - e.g. 'BM'
  37.                     Console.WriteLine(new String(br.ReadChars(2)));
  38.                     //Size of BMP in bytes:
  39.                     Console.WriteLine(br.ReadInt32());  //54 bytes more than raw data - intesting
  40.                     //reserved
  41.                     Console.WriteLine(br.ReadInt16());
  42.                     Console.WriteLine(br.ReadInt16());
  43.                     //Offset
  44.                     Console.WriteLine(br.ReadInt32());  //54 - we might have a winner!
  45.                 }
  46.  
  47.                 //What we are going to do:
  48.                 //Create a dummy image, and steal the header info for our own use.
  49.                 //This is easier than manually writing out own, and as long as we keep things relatively simple will work.
  50.                 //Just ensure we have no padding, or ICC color profile - and we can use any other format options we desire!
  51.             }
  52.  
  53.             while (true) RunExperiment();
  54.         }
  55.  
  56.         #region Settings
  57.         const int numSensors = 70000;
  58.         const int textureSize = 4096;
  59.         const int intervals = 100;
  60.         const int maxThreads = -1;
  61.         const string directory = @"C:\TextureFromPoints5\";
  62.         #endregion
  63.  
  64.         static Random rnd = new Random();
  65.         static void RunExperiment()
  66.         {
  67.             var swTotal = Stopwatch.StartNew();
  68.             var sw = Stopwatch.StartNew();
  69.             Console.WriteLine("\n Set-Up \n");
  70.  
  71.             #region Setups
  72.             var pOptions = new ParallelOptions();
  73.             if (maxThreads > 0) pOptions.MaxDegreeOfParallelism = maxThreads;
  74.  
  75.             var magic = Math.Sqrt(textureSize * textureSize / numSensors) / 2;  //Magic Formula
  76.             var hash = (int)Math.Pow(2, (int)(Math.Log(magic - 1, 2)) + 1);  //Rounds up to nearest power of 2.
  77.             if (hash < 1) hash = 1;
  78.             Console.WriteLine("Rough Hash Generated: {0}  [Magic Density : {1}]", hash, magic);
  79.  
  80.             //We now use existing files as preallocation.
  81.             foreach (FileInfo file in Directory.CreateDirectory(directory).GetFiles())
  82.                 if (Int32.Parse(file.Name.Split('.')[0]) >= intervals) file.Delete();
  83.  
  84.             #endregion
  85.  
  86.             Console.WriteLine("\n Generating Data \n");
  87.             sw.Restart();
  88.             var sensors = GenerateRandomSensors();
  89.             Console.WriteLine("Completed in {0}ms\n", sw.ElapsedMilliseconds);
  90.  
  91.             Console.WriteLine("\n Generating Spacial Collection \n");
  92.             sw.Restart();
  93.             var spacialHash = new SimpleSpacialCollection(textureSize, hash);
  94.             foreach (var sensor in sensors) spacialHash.AddSensor(sensor);
  95.             Console.WriteLine("Completed in {0}ms\n", sw.ElapsedMilliseconds);
  96.  
  97.             Console.WriteLine("\n Get Header Data \n");
  98.             sw.Restart();
  99.             byte[] header = GenerateHeaderData();
  100.             Console.WriteLine("Completed in {0}ms\n", sw.ElapsedMilliseconds);
  101.  
  102.  
  103.             Console.WriteLine("\n Initialising Images \n");
  104.             sw.Restart();
  105.             var images = new BinaryWriter[intervals];
  106.  
  107.  
  108.             Parallel.For(0, images.Length, i =>
  109.             {
  110.                 //Just Plain Slow:
  111.                 //var stream = MemoryMappedFile.CreateFromFile(directory + i + ".bmp", FileMode.Create, i.ToString(), (textureSize * textureSize + header.Length) * 4).CreateViewStream();
  112.  
  113.                 //The forced buffer significantly helps with HDD writes according to simple testing.
  114.                 //We will reuse existing files as preallocation if the exist.
  115.                 var stream = new FileStream(directory + i + ".bmp", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 1024 * 1024, false);
  116.                 stream.SetLength(textureSize * textureSize * 4 + header.Length);
  117.                 stream.Position = 0;
  118.  
  119.                 var br = new BinaryWriter(stream);
  120.                 br.Write(header);
  121.                 images[i] = br;
  122.             });
  123.             Console.WriteLine("Completed in {0}ms\n", sw.ElapsedMilliseconds);
  124.  
  125.             Console.WriteLine("\n Writing Image Data \n");
  126.             sw.Restart();
  127.  
  128.             /*
  129.              * for (int x = 0; x < textureSize; x++)
  130.                 for (int y = 0; y < textureSize; y++)
  131.                 {
  132.                     var sensor = spacialHash.GetNearest(x, y);
  133.                     for (int i = 0; i < intervals; i++)
  134.                         images[i].Write((int)(int.MaxValue * sensor.data[i]));
  135.                 }*/
  136.  
  137.             //Threading only appears to make sence when data is already preallocated -=otherwise it actually takes longer.
  138.             var splitCount = Environment.ProcessorCount * 2;
  139.             Parallel.For(0, splitCount, threadID =>
  140.              {
  141.                  Thread.Sleep(threadID * 10);
  142.  
  143.                  int offsetSize = textureSize / splitCount;
  144.  
  145.                  var min = offsetSize * threadID;
  146.                  var max = (threadID == (splitCount - 1)) ? textureSize : min + offsetSize;
  147.  
  148.                  int rowsToBuffer = 128;
  149.                  var buffer = new float[textureSize * rowsToBuffer][];
  150.                  for (int x = min; x < max; x += rowsToBuffer)
  151.                  {
  152.                      for (int r = 0; r < rowsToBuffer; r++)
  153.                          for (int y = 0; y < textureSize; y++)
  154.                              buffer[r * textureSize + y] = spacialHash.GetNearest(x + r, y).data;
  155.                      for (int i = 0; i < intervals; i++)
  156.                      {
  157.                          var image = images[i];
  158.                          lock (image)
  159.                          {
  160.                              image.BaseStream.Position = header.Length + x * textureSize * 4;
  161.                              for (int y = 0; y < buffer.Length; y++)
  162.                                  image.Write((int)(int.MaxValue * buffer[y][i]));
  163.                          }
  164.                      }
  165.                  };
  166.              });
  167.  
  168.             Console.WriteLine("Completed in {0}ms\n", sw.ElapsedMilliseconds);
  169.  
  170.             Console.WriteLine("\n Clean Up \n");
  171.             sw.Restart();
  172.             for (int i = 0; i < images.Length; i++)
  173.             {
  174.                 images[i].Flush();
  175.                 images[i].Dispose();
  176.             }
  177.             Console.WriteLine("Completed in {0}ms\n", sw.ElapsedMilliseconds);
  178.  
  179.             Console.WriteLine("\n Finished  in  {0}ms \n", swTotal.ElapsedMilliseconds);
  180.             Console.ReadLine();
  181.             /* */
  182.  
  183.             /*
  184.             int splitCount = Environment.ProcessorCount;
  185.  
  186.             var spacialHash = new SimpleSpacialCollection(textureSize, hash);
  187.             foreach (var sensor in sensors) spacialHash.AddSensor(sensor);
  188.  
  189.             var results = new ConcurrentDictionary<int, int[,]>();
  190.             new Thread(() =>
  191.                  {
  192.                      while (results != null)
  193.                      {
  194.                          foreach (var key in results.Keys)
  195.                          {
  196.                              int[,] data;
  197.                              if (results.TryRemove(key, out data))
  198.                                  for (int i = 0; i < intervals; i++)
  199.                                  {
  200.                                      var image = images[i];
  201.                                      image.BaseStream.Position = header.Length + key * textureSize * 4;
  202.                                      for (int y = 0; y < textureSize; y++)
  203.                                          image.Write((int)(int.MaxValue * data[y, i]));
  204.                                  }
  205.                          }
  206.                      }
  207.                      Console.WriteLine("FileWriter:  All Done!");
  208.                  }).Start();
  209.  
  210.             var sw2 = Stopwatch.StartNew();
  211.             Parallel.For(0, textureSize, x =>
  212.                 {
  213.  
  214.                     var buffer = new int[textureSize, intervals];
  215.                     for (int y = 0; y < textureSize; y++)
  216.                     {
  217.                         var temp = spacialHash.GetNearest(x, y).data;
  218.                         for (int i = 0; i < intervals; i++)
  219.                             buffer[y, i] = (int)(int.MaxValue * temp[i]);
  220.                     }
  221.                     results[x] = buffer;
  222.                 });
  223.             Console.WriteLine("Calculation Time = {0}ms", sw2.ElapsedMilliseconds);
  224.  
  225.             while (results.Count > 0) Thread.Yield();
  226.             results = null;
  227.  
  228.             /*Parallel.For(0, splitCount, threadID =>
  229.             //for (int threadID = 0; threadID < splitCount; threadID++)
  230.             {
  231.                 Thread.Sleep(threadID * 10);
  232.  
  233.                 int offsetSize = textureSize / splitCount;
  234.  
  235.                 var min = offsetSize * threadID;
  236.                 var max = (threadID == (splitCount - 1)) ? textureSize : min + offsetSize;
  237.  
  238.                 int rowsToBuffer = 64;
  239.                 var buffer = new float[textureSize * rowsToBuffer][];
  240.                 for (int x = min; x < max; x += rowsToBuffer)
  241.                 {
  242.                     for (int r = 0; r < rowsToBuffer; r++)
  243.                         for (int y = 0; y < textureSize; y++)
  244.                             buffer[r * textureSize + y] = spacialHash.GetNearest(x + rowsToBuffer, y).data;
  245.  
  246.                     for (int i = 0; i < intervals; i++)
  247.                     {
  248.                         var image = images[i];
  249.                         lock (image)
  250.                         {
  251.                             image.BaseStream.Position = header.Length + x * textureSize * 4;
  252.                             for (int y = 0; y < buffer.Length; y++)
  253.                                 image.Write((int)(int.MaxValue * buffer[y][i]));
  254.                         }
  255.                     }
  256.                 };
  257.             });
  258.             /* */
  259.  
  260.             /*int splitCount = Environment.ProcessorCount * 4;
  261.  
  262.  
  263.             var spacialHash = new SimpleSpacialCollection(textureSize, hash);
  264.  
  265.             foreach (var sensor in sensors) spacialHash.AddSensor(sensor);
  266.  
  267.             //Threading this is a little more complicated, but doable.
  268.             //Throughtput increased from 74.5MB/s [90s] to 150MB/s [42s]
  269.             //[4096x4096, 100 images - 6.77GB]
  270.             Parallel.For(0, splitCount, threadID =>
  271.             {
  272.                 int offsetSize = intervals / splitCount;
  273.  
  274.                 var min = offsetSize * threadID;
  275.                 var max = (threadID == splitCount - 1) ? intervals : min + offsetSize;
  276.  
  277.                 for (int x = 0; x < textureSize; x++)
  278.                     for (int y = 0; y < textureSize; y++)
  279.                     {
  280.                         var sensor = spacialHash.GetNearest(x, y);
  281.  
  282.                         for (int i = min; i < max; i++)
  283.                             images[i].Write((int)(int.MaxValue * sensor.data[i]));
  284.                     }
  285.             }); /* */
  286.         }
  287.  
  288.         private static byte[] GenerateHeaderData()
  289.         {
  290.             byte[] header;
  291.             using (var ms = new MemoryStream())
  292.             {
  293.                 new Bitmap(textureSize, textureSize, PixelFormat.Format32bppArgb).Save(ms, ImageFormat.Bmp);
  294.                 using (var br = new BinaryReader(ms))
  295.                 {
  296.                     br.BaseStream.Position = 10;
  297.                     header = new byte[br.ReadInt32()];
  298.                     ms.Position = 0;
  299.                     ms.Read(header, 0, header.Length);
  300.                 }
  301.             }
  302.             return header;
  303.         }
  304.         private static Sensor[] GenerateRandomSensors()
  305.         {
  306.             var sensors = new Sensor[numSensors];
  307.             for (int i = 0; i < numSensors; i++)
  308.             {
  309.                 var sensor = new Sensor();
  310.                 sensor.position = new Vector2(textureSize);
  311.                 sensor.data = Enumerable.Range(0, intervals).Select(_ => (float)rnd.NextDouble()).ToArray();
  312.                 sensors[i] = sensor;
  313.             }
  314.             return sensors;
  315.         }
  316.  
  317.         public struct Sensor
  318.         {
  319.             public Vector2 position;
  320.             public float[] data;
  321.         }
  322.  
  323.         public struct Vector2
  324.         {
  325.             public float x;
  326.             public float y;
  327.  
  328.             public Vector2(float x, float y)
  329.             {
  330.                 this.x = x;
  331.                 this.y = y;
  332.             }
  333.  
  334.             public Vector2(float randomDistance)
  335.             {
  336.                 this.x = (float)rnd.NextDouble() * randomDistance;
  337.                 this.y = (float)rnd.NextDouble() * randomDistance;
  338.             }
  339.  
  340.             public static Vector2 operator -(Vector2 a, Vector2 b)
  341.             {
  342.                 return new Vector2(a.x - b.x, a.y - b.y);
  343.             }
  344.  
  345.             public float sqrMagnitude()
  346.             {
  347.                 return x * x + y * y;
  348.             }
  349.  
  350.             public float sqrDistanceToPoint(Vector2 point)
  351.             {
  352.                 var x = this.x - point.x;
  353.                 var y = this.y - point.y;
  354.                 return x * x + y * y;
  355.             }
  356.         }
  357.  
  358.         public class SimpleSpacialCollection
  359.         {
  360.             List<Sensor>[,] buckets;
  361.             public readonly int bounds;
  362.             public readonly int hash;
  363.  
  364.             public int count { get; private set; }
  365.  
  366.             public SimpleSpacialCollection(int bounds, int hash)
  367.             {
  368.                 this.bounds = bounds;
  369.                 this.hash = hash;
  370.                 buckets = new List<Sensor>[bounds / hash + 1, bounds / hash + 1];
  371.                 count = 0;
  372.             }
  373.  
  374.             public void AddSensor(Sensor sensor)
  375.             {
  376.                 GetBucket(sensor.position).Add(sensor);
  377.                 count++;
  378.             }
  379.  
  380.             List<Sensor> GetBucket(Vector2 v2)
  381.             {
  382.                 return GetBucket((int)v2.x, (int)v2.y);
  383.             }
  384.  
  385.             List<Sensor> GetBucket(int x, int y)
  386.             {
  387.                 var bucket = buckets[x / hash, y / hash];
  388.                 if (bucket == null)
  389.                 {
  390.                     bucket = new List<Sensor>();
  391.                     buckets[x / hash, y / hash] = bucket;
  392.                 }
  393.                 return bucket;
  394.             }
  395.  
  396.             public Sensor GetNearest(int pointX, int pointY, int number = 1)
  397.             {
  398.                 if (count < number) throw new Exception("Not enough elements!");
  399.  
  400.                 var candidates = new List<Sensor>();
  401.  
  402.                 int maxX, minX, maxY, minY;
  403.  
  404.                 maxX = minX = pointX / hash;
  405.                 maxY = minY = pointY / hash;
  406.  
  407.                 do
  408.                 {
  409.                     maxX++; maxY++; minX--; minY--;
  410.  
  411.                     if (minX < 0) minX = 0;
  412.                     if (minY < 0) minY = 0;
  413.                     if (maxX > buckets.GetLength(0)) maxX = buckets.GetLength(0);
  414.                     if (maxY > buckets.GetLength(1)) maxY = buckets.GetLength(1);
  415.  
  416.                     for (int x = minX; x < maxX; x++)
  417.                         for (int y = minY; y < maxY; y++)
  418.                         {
  419.                             var bucket = buckets[x, y];
  420.                             if (bucket != null) candidates.AddRange(bucket);
  421.                         }
  422.                 } while (candidates.Count < number);
  423.  
  424.                 var point = new Vector2(pointX, pointY);
  425.  
  426.                 Sensor nearest = candidates[0];
  427.                 float nearestDistance = nearest.position.sqrDistanceToPoint(point);
  428.  
  429.                 for (int i = 1; i < candidates.Count; i++)
  430.                 {
  431.                     var candidate = candidates[i];
  432.                     if (candidate.position.sqrDistanceToPoint(point) < nearestDistance)
  433.                     {
  434.                         nearest = candidate;
  435.                         nearestDistance = candidate.position.sqrDistanceToPoint(point);
  436.                     }
  437.                 }
  438.                 return nearest;
  439.             }
  440.         }
  441.     }
  442. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement