Angepinnt [Sammelthread] Knobel-Aufgaben, knifflige Algorithmen, elegante Lösungen

    • VB.NET

    Es gibt 178 Antworten in diesem Thema. Der letzte Beitrag () ist von Thunderbolt.

      Wobei es vollkommen egal ist, ob es eine andere Möglichkeit gibt, denn die Aufgabenstellung ist eindeutig - und genau das ist der Sinn hinter diesen Aufgaben.

      Hier noch eine Variante, die relativ sauber und elegant gelöst ist:
      C#

      C-Quellcode

      1. private struct File
      2. {
      3. FileInfo fileInfo;
      4. string extension, name;
      5. int number;
      6. bool valid;
      7. public FileInfo FileInfo { get { return fileInfo; } }
      8. public string Extension { get { return extension; } }
      9. public string Name { get { return name; } }
      10. public int Number { get { return number; } }
      11. public bool Valid { get { return valid; } }
      12. public File(FileInfo file)
      13. {
      14. fileInfo = file;
      15. extension = file.Extension;
      16. name = file.Name.Replace(extension, "");
      17. valid = int.TryParse(name.Split('_').Last(), out number);
      18. }
      19. }
      20. public static string AppendName(FileInfo sourceFile, DirectoryInfo destFolder)
      21. {
      22. File source = new File(sourceFile);
      23. var allFiles = destFolder.EnumerateFiles();
      24. var customStruct = allFiles.Select(item => new File(item));
      25. var customStructFiltered = customStruct.Where(item => item.Valid);
      26. var customStructOrdered = customStructFiltered.OrderByDescending(item => item.Number);
      27. File lastFile = customStructOrdered.FirstOrDefault();
      28. return Path.Combine(destFolder.FullName, source.Name + "_" + (lastFile.Number + 1).ToString("000")) + source.Extension;
      29. }

      VB

      VB.NET-Quellcode

      1. Private Structure File
      2. Private m_fileInfo As FileInfo
      3. Private m_extension As String, m_name As String
      4. Private m_number As Integer
      5. Private m_valid As Boolean
      6. Public ReadOnly Property FileInfo() As FileInfo
      7. Get
      8. Return m_fileInfo
      9. End Get
      10. End Property
      11. Public ReadOnly Property Extension() As String
      12. Get
      13. Return m_extension
      14. End Get
      15. End Property
      16. Public ReadOnly Property Name() As String
      17. Get
      18. Return m_name
      19. End Get
      20. End Property
      21. Public ReadOnly Property Number() As Integer
      22. Get
      23. Return m_number
      24. End Get
      25. End Property
      26. Public ReadOnly Property Valid() As Boolean
      27. Get
      28. Return m_valid
      29. End Get
      30. End Property
      31. Public Sub New(file__1 As FileInfo)
      32. m_fileInfo = file__1
      33. m_extension = file__1.Extension
      34. m_name = file__1.Name.Replace(m_extension, "")
      35. m_valid = Integer.TryParse(m_name.Split("_"C).Last(), m_number)
      36. End Sub
      37. End Structure
      38. Public Function AppendName(sourceFile As FileInfo, destFolder As DirectoryInfo) As String
      39. Dim source As New File(sourceFile)
      40. Dim allFiles = destFolder.EnumerateFiles()
      41. Dim customStruct = allFiles.[Select](Function(item) New File(item))
      42. Dim customStructFiltered = customStruct.Where(Function(item) item.Valid)
      43. Dim customStructOrdered = customStructFiltered.OrderByDescending(Function(item) item.Number)
      44. Dim lastFile As File = customStructOrdered.FirstOrDefault()
      45. Return Path.Combine(destFolder.FullName, source.Name & "_" & (lastFile.Number + 1).ToString("000")) & source.Extension
      46. End Function

      VB Code nicht getestet, nur durch einen Konverter gejagt, sollte allerdings funktionieren.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „AliveDevil“ ()

      Image Convolution

      Hey,
      falls die vorherige Knobelei gelöst wurde, hätt' ich etwas das wohl in knifflige Algorithmen passen würde.
      Es geht darum mithilfe einer 3x3 Matrix ein Image zu verändern. Um so schöne Effekte wie Blur, Pixelation und Co zu bekommen. Hierzu bekommt man nun ein float-Array mit 9 Membern (die Matrix) und die Bitmap. Man hohlt sich die Bits per Bitmap.LockBits() im Format 32bppPARGB und soll sie mit der Matrix multiplizieren. Dieses wird hier sehr schön dargestellt . Das wird mit jedem R, G und B Wert gemacht und mit jedem Pixel. Soweit ists also vorgegeben

      C-Quellcode

      1. public unsafe void ProcessImage(float[] matrix3x3, Bitmap bmp)
      2. {
      3. BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
      4. // Bitmap ändern
      5. bmp.UnlockBits(data);
      6. }


      Das eigentliche Problem sind die Ränder und die Ecken, da hier die Pixel erweitern werden sollen
      um auch diese Pixel verarbeiten zu können. Die Möglichkeit die Bitmap um einen Pixel an jedem Rand zu erweitern steht nicht zur Auswahl. Desweiteren gibts kein Linq, es soll möglichst simpel und performant ablaufen. Die Bitmap darf aber gern kopiert werden (oder muss), da die Bitmap nicht geändert werden darf während es noch nicht abgearbeitete Pixel gibt.

      Grüße :thumbup:
      Braucht man garnicht, du nimmst die kopierte Bitmap zum Lesen und die orginale zum Schreiben, so wie im Bild. Klar kann man da schnell ne Klasse drum schreiben (hatte gedacht grade das würde verwirren :rolleyes: ). Die Reihenfolge wäre ganz normal also so
      11 12 13
      21 22 23
      31 32 33

      Wobei im Array dann 11 - 0 wäre, 12 -1, 13- 3, 21 - 4 bis 33 - 8. Die Elemente werden mit den entsprechenden Pixeln (gucke Bild) multipliziert und die Summe bildet den neuen Pixel in der Mitte.
      Es erscheint mir nur sinnvoller, nicht das Ausgangsbild zu verändern. Und mit ner Klasse (bzw. ich würds als Struct designen) wärs schon einfacher.
      Ich mach das einfach mal so und leg dir nen Wrapper drumrum, damit die Anforderungen erfüllt sind aber trotzdem ne ordentliche Variante existiert. ;)
      Die Lösung für die Ränder steht doch in dem Wikipedia Artikel:

      Edge Handling
      Kernel convolution usually requires values from pixels outside of the image boundaries. There are a variety of methods for handling image edges.

      Extend
      The nearest border pixels are conceptually extended as far as necessary to provide values for the convolution. Corner pixels are extended in 90° wedges. Other edge pixels are extended in lines.
      Wrap
      The image is conceptually wrapped (or tiled) and values are taken from the opposite edge or corner.
      Crop
      Any pixel in the output image which would require values from beyond the edge is skipped. This method can result in the output image being slightly smaller, with the edges having been cropped.
      Endlich hab ichs geschafft (Vorsicht, langer Code):
      Spoiler anzeigen

      C-Quellcode

      1. public static class BitmapExtensions
      2. {
      3. /// <summary>
      4. /// Gets the pixel at the specified coordinates for this BitmapData object.
      5. /// </summary>
      6. public static unsafe Color GetPixel(this BitmapData data, int x, int y)
      7. {
      8. byte* dataPointer = (byte*)data.Scan0;
      9. dataPointer += y * data.Stride + x * 4;
      10. return Color.FromArgb(dataPointer[3], dataPointer[2], dataPointer[1], dataPointer[0]);
      11. }
      12. private static int Clamp(int value, int min, int max)
      13. {
      14. if (min > value) return min;
      15. if (max < value) return max;
      16. return value;
      17. }
      18. private static float Clamp(float value, float min, float max)
      19. {
      20. if (min > value) return min;
      21. if (max < value) return max;
      22. return value;
      23. }
      24. /// <summary>
      25. /// Applies a kernel to a bitmap.
      26. /// </summary>
      27. public static unsafe Bitmap Process(this Bitmap sourceImage, ProcessingKernel kernel)
      28. {
      29. int width = sourceImage.Width;
      30. int height = sourceImage.Height;
      31. var destinationImage = new Bitmap(width, height);
      32. var lockRect = new Rectangle(0, 0, width, height);
      33. BitmapData sourceData = sourceImage.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
      34. BitmapData destinationData = destinationImage.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
      35. Parallel.For(0, width * height, i =>
      36. {
      37. int x;
      38. int y = Math.DivRem(i, width, out x);
      39. float alpha = 0;
      40. float red = 0;
      41. float green = 0;
      42. float blue = 0;
      43. for (int kernelX = 0, sourceX = x - 1; kernelX < 3; kernelX++, sourceX++)
      44. {
      45. for (int kernelY = 0, sourceY = y - 1; kernelY < 3; kernelY++, sourceY++)
      46. {
      47. Color color = sourceData.GetPixel(Clamp(sourceX, 0, width - 1), Clamp(sourceY, 0, height - 1));
      48. float factor = kernel[kernelY, kernelX];
      49. alpha += (float)color.A / byte.MaxValue * factor;
      50. red += (float)color.R / byte.MaxValue * factor;
      51. green += (float)color.G / byte.MaxValue * factor;
      52. blue += (float)color.B / byte.MaxValue * factor;
      53. }
      54. }
      55. byte* destinationPointer = (byte*)destinationData.Scan0 + i * 4;
      56. destinationPointer[0] = (byte)(Clamp(blue, 0, 1) * byte.MaxValue);
      57. destinationPointer[1] = (byte)(Clamp(green, 0, 1) * byte.MaxValue);
      58. destinationPointer[2] = (byte)(Clamp(red, 0, 1) * byte.MaxValue);
      59. destinationPointer[3] = (byte)(Clamp(alpha, 0, 1) * byte.MaxValue);
      60. });
      61. sourceImage.UnlockBits(sourceData);
      62. destinationImage.UnlockBits(destinationData);
      63. return destinationImage;
      64. }
      65. }
      66. /// <summary>
      67. /// Represents a kernel for image processing.
      68. /// </summary>
      69. public unsafe struct ProcessingKernel : IEnumerable<float>
      70. {
      71. fixed float matrix[9];
      72. /// <summary>
      73. /// A kernel don't applying any changes to the image.
      74. /// </summary>
      75. public static readonly ProcessingKernel Default;
      76. static ProcessingKernel()
      77. {
      78. Default = default(ProcessingKernel);
      79. Default[4] = 1;
      80. }
      81. /// <summary>
      82. /// Gets or sets the value at a specific index.
      83. /// </summary>
      84. public float this[int index]
      85. {
      86. get { fixed (float* matrix = this.matrix) return matrix[index]; }
      87. set { fixed (float* matrix = this.matrix) matrix[index] = value; }
      88. }
      89. /// <summary>
      90. /// Gets or sets the value at a specific row an column.
      91. /// </summary>
      92. public float this[int row, int column]
      93. {
      94. get
      95. {
      96. if (row < 0 || row > 2)
      97. throw new ArgumentOutOfRangeException("row");
      98. if (column < 0 || column > 2)
      99. throw new ArgumentOutOfRangeException("column");
      100. fixed (float* matrix = this.matrix)
      101. return matrix[row * 3 + column];
      102. }
      103. set
      104. {
      105. if (row < 0 || row > 2)
      106. throw new ArgumentOutOfRangeException("row");
      107. if (column < 0 || column > 2)
      108. throw new ArgumentOutOfRangeException("column");
      109. fixed (float* matrix = this.matrix)
      110. matrix[row * 3 + column] = value;
      111. }
      112. }
      113. /// <summary>
      114. /// Creates a new image processing kernel.
      115. /// </summary>
      116. /// <param name="values">A one-dimensional array holding the values for this kernel.</param>
      117. public ProcessingKernel(float[] values)
      118. {
      119. fixed (float* matrix = this.matrix)
      120. Marshal.Copy(values, 0, (IntPtr)matrix, 9);
      121. }
      122. /// <summary>
      123. /// Creates a new image processing kernel.
      124. /// </summary>
      125. /// <param name="values">A two-dimensional array holding the values for this kernel.</param>
      126. public ProcessingKernel(float[,] values)
      127. {
      128. if (values.GetUpperBound(0) < 2 || values.GetUpperBound(1) < 2)
      129. throw new ArgumentException("values");
      130. fixed (float* matrix = this.matrix)
      131. for (int row = 0; row < 3; row++)
      132. for (int column = 0; column < 3; column++)
      133. matrix[row * 3 + column] = values[row, column];
      134. }
      135. public IEnumerator<float> GetEnumerator()
      136. {
      137. var result = new float[9];
      138. fixed (float* matrix = this.matrix) Marshal.Copy((IntPtr)matrix, result, 0, 9);
      139. return result.AsEnumerable().GetEnumerator();
      140. }
      141. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
      142. {
      143. var result = new float[9];
      144. fixed (float* matrix = this.matrix) Marshal.Copy((IntPtr)matrix, result, 0, 9);
      145. return result.GetEnumerator();
      146. }
      147. }
      @ErfinderDesRades
      Ja, kann ich, aber sei gewarnt, dass ist mein Testprojekt für alles nur erdenklich. :D

      @~blaze~
      Ich fands mit der Color-Strukt halt einfacher, sonst hätte ich vier out-Parameter gebraucht. Es steht dir natürlich frei, das nach deinen Wünschen anzupassen. ;)
      Dateien
      @artentus:

      Ich hab dein Teil nochmal bisserl gehübscht. Und von mir füge ich ein älteres Werk an - mit Dataset natürlich.
      Das ist auf meinem 2-Kernel immer noch ca. 1,5 mal schneller als deine (total interessante :thumbup: ) Parallel.For - Übung.

      Bei meim optimierte ich v.a. 2 Sachen:
      • Randpunkte werden gesondert verarbeitet. Das vereinfacht die Schleife für 99,5% der Pixel erheblich.
      • ich mach nix mit Colors, sondern orgel auf rohen Bytes herum
      Dateien
      So ich hab @Artentus: Code mal überarbeitet und Offset + Faktor hinzugefügt. Hab das ganze Colorzeugg rausgenommn, jetzt ists schon um einiges schneller.
      Dateien