[Quiz#14] Alexander Schuc (C#)


#1
Hoi.. :)

Auch von mir gibts eine kleine Lösung.


(Bild von DeviantArt: Dice by suicidegirls)

Kurze Anmerkung zum Ablauf meines Programms:
Nach Laden des Bildes wird es in ein Graustufenbild (bzw. Luma) umgewandelt, größter und kleinster Helligkeitswert
gesucht, und daraufhin in ein "Würfelbild" umgewandelt. Die Ausgabe ist immer ein (ungefähr) gleich großes Bild, da
ich mir pro Würfel einen gleich großen Bereich im originalen Bild ansehe und davon den Durchschnittswert nehme.

Damit die Laufzeit des Programms nicht zu lang ist, hab ich auf unsafe Code zurück gegriffen und mit BitmapData
gearbeitet, anstatt mit den langsamen Methoden der Graphics Klasse. Abgesehen vom Zusammenstückeln der
Würfelgrafiken, das klappt damit bequemer. ;)

Ein Dank geht an Matthias.. hab deine Würfel verwendet, war zu faul selber welche zu basteln. ;)

Verwendung
Es wurde eine Extension Method namens "GetDiced" erstellt, welche die Umwandlung vornimmt.
C#:
Bitmap original = Bitmap.FromFile(...);
Bitmap diced = original.GetDiced(12);
Wenn man das Beispielprogramm verwendet, kann man durch Klick auf die erste Box ein Bild laden. Dieses wird
automatisch in ein Würfelbild gewandelt. Einstellungen also vor dem Laden vornehmen. Aktiviert man "Luma" bekommt
man das Luma Bild angezeigt.
Zum Speichern des Ergebnisses klickt man auf die 2. PictureBox. Gespeichert wird immer als PNG Datei, deswegen
Dateierweiterung entsprechend selber angeben. :)

Anhänge
Projektdateien für VisualStudio 2010
(bei anderen Versionen einfach die Codedateien in ein WindowsForms Projekte übernehmen)
Anhang anzeigen CodingQuiz14.zip

Ausführbare Dateien
Anhang anzeigen CodingQuiz14-Bin.zip

Code
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace CodingQuiz14
{
	public static class DiceGraphics
	{
		public static Bitmap GetDiced(this Bitmap source, int size)
		{
			try
			{
				Image[] parts = LoadDices(size);

				if (parts == null)
					return null;

				Bitmap luma = source.GetLuma();

				int[] range = FindRange(luma);

				byte[,] diceData = GetDices(luma, range, size);

				int dw = diceData.GetLength(0);
				int dy = diceData.GetLength(1);

				Bitmap diceBmp = new Bitmap(dw * size, dy * size);

				using (Graphics gfx = Graphics.FromImage(diceBmp))
				{
					for (int x = 0; x < dw; x++)
						for (int y = 0; y < dy; y++)
						{
							gfx.DrawImageUnscaled(parts[diceData[x, y]], x * size, y * size, size, size);
						}
				}


				return diceBmp;
			}
			catch (Exception ex)
			{
				Console.Write(ex.Message);
			}
			return null;
		}

		private static unsafe byte[,] GetDices(Bitmap source, int[] range, int size)
		{
			int dynamic = range[1] - range[0];
			double stepsize = (dynamic+1) / 6.0;
			byte[,] data = new byte[(source.Width / size) + 1, (source.Height / size) + 1];

			BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height),
				ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);

			unsafe
			{
				byte avg, d;
				for (int y = 0; y < source.Height; y += size)
					for (int x = 0; x < source.Width; x += size)
					{
						avg = Average(sourceData, x, y, size);
						d = (byte)((avg - range[0]) / stepsize);

						data[x / size, y / size] = (byte)((byte)5 - d);
					}
			}

			source.UnlockBits(sourceData);

			return data;
		}
		
		private static unsafe byte Average(BitmapData data, int x, int y, int size)
		{
			int sum = 0;
			int c = 0;
			byte* row;

			for (int yc = y; yc < y + size; yc++)
			{
				if (yc >= data.Height)
					break;

				row = (byte*)data.Scan0 + (yc * data.Stride);
				for (int xc = x; xc < x + size; xc++)
				{
					if (xc >= data.Width)
						break;

					sum += *(row + xc);

					c++;
				}

				
			}
			return (byte)(sum / c);
		}

		public static Bitmap GetLuma(this Bitmap source)
		{
			Bitmap lumaBmp = new Bitmap(source.Width, source.Height, PixelFormat.Format8bppIndexed);

			ColorPalette p = lumaBmp.Palette;
			for (int i = 0; i < 256; i++)
			{
				p.Entries[i] = Color.FromArgb(i, i, i);
			}
			lumaBmp.Palette = p;

			BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height),
				ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

			BitmapData lumaData = lumaBmp.LockBits(new Rectangle(0, 0, source.Width, source.Height),
				ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

			unsafe
			{
				byte r, g, b, luma;
				byte* pixel, luminaPixel, row;

				for (int y = 0; y < source.Height; y++)
				{
					row = (byte*) (sourceData.Scan0 + (y * sourceData.Stride));
					for (int x = 0; x < source.Width; x++)
					{
						pixel = row + x*3;

						r = pixel[2];
						g = pixel[1];
						b = pixel[0];

						luma = (byte)(0.2126 * (double)r + 0.7152 * (double)g + 0.0722 * (double)b);

						luminaPixel = (byte*)lumaData.Scan0 + (y * lumaData.Stride) + x;
						(*luminaPixel) = luma;
					}
				}
			}

			source.UnlockBits(sourceData);
			lumaBmp.UnlockBits(lumaData);
			return lumaBmp;
		}

		private static int[] FindRange(Bitmap source)
		{
			if (source.PixelFormat != PixelFormat.Format8bppIndexed)
				return new int[] { 0, 255 };

			BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height),
				ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

			int[] data = new int[] { int.MaxValue, int.MinValue };

			unsafe
			{
				byte pixel;
				byte* row;
				for (int y = 0; y < source.Height; y++)
				{
					row = (byte*)sourceData.Scan0 + (y * sourceData.Stride);
					for (int x = 0; x < source.Width; x++)
					{
						pixel = *(row + x);

						if (pixel < data[0])
							data[0] = pixel;
						if (pixel > data[1])
							data[1] = pixel;
					}
				}
			}
			
			source.UnlockBits(sourceData);

			return data;
		}

		private static Image[] LoadDices(int size)
		{
			string dicefile = string.Format(".\\dices\\dice{0}.png", size);

			if (!File.Exists(dicefile))
			{
				return null;
			}

			try
			{
				using (Image dices = Bitmap.FromFile(dicefile))
				{
					Image[] parts = new Image[6];

					for (int i = 0; i < 6; i++)
					{
						parts[i] = new Bitmap(size, size);
						using (Graphics gfx = Graphics.FromImage(parts[i]))
						{
							gfx.DrawImage(dices,
								new Rectangle(0, 0, size, size),
								new Rectangle(0, i*size, size, size), GraphicsUnit.Pixel);
						}
					}

					return parts;
				}
			}
			catch (Exception ex)
			{
				return null;
			}
		}

	}
}
 

Anhänge

Neue Beiträge