Nadesłany przez Tomasz Lubiński, 06 czerwca 2012 16:30
Kod przedstawiony poniżej przedstawia główną część rozwiązania problemu.Pobierz pełne rozwiązanie.
Jeżeli nie odpowiada Ci sposób formatowania kodu przez autora skorzystaj z pretty printer'a i dostosuj go automatycznie do siebie.
tilt_shift/tilt_shift/Form1.cs:
//Tilt Shift //(c) 2012 by Tomasz Lubinski //www.algorytm.org using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing.Imaging; namespace tilt_shift { public partial class Form1 : Form { /// <summary> /// Obraz zrodlowy /// </summary> private Image zrodlo; private int bytesPerPixel; /// <summary> /// Zainicjuj /// </summary> public Form1() { zrodlo = null; InitializeComponent(); } /// <summary> /// Wczytaj obraz /// </summary> private void wczytaj_Click(object sender, EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Otwórz obraz"; dlg.Filter = "pliki jpg (*.jpg)|*.jpg|pliki png (*.png)|*.png|wszystkie pliki (*.*)|*.*"; if (dlg.ShowDialog() == DialogResult.OK) { zrodlo = new Bitmap(dlg.OpenFile()); bytesPerPixel = Image.GetPixelFormatSize(zrodlo.PixelFormat) / 8; obraz.Image = new Bitmap(dlg.OpenFile()); obraz.Height = obraz.Image.Height; obraz.Width = obraz.Image.Width; this.ClientSize = new System.Drawing.Size(Math.Max(obraz.Width + 32, 374), obraz.Height + 155); info.Text = "Wskaż na zdjęciu środek obszaru 'in-focus'"; } dlg.Dispose(); } /// <summary> /// Utworz tablice ze wspolczynnikami do rozmycia Gaussowskiego /// </summary> /// <param name="factor">wspolczynnik rozmycia</param> /// <param name="width">maksymalna wielkosc tablicy</param> /// <returns>tablica ze wspolczynnikami do rozmycia Gaussowskiego</returns> private double[] gaussianArray(double factor, int width) { double[] result = new double[width]; for (var i=0; i<width; i++) { result[i] = (1 / (Math.Sqrt(2.0 * Math.PI) * factor)) * Math.Exp(-(i * i) / (2.0 * factor * factor)); if (result[i] < 0.003) { double[] subArray = new double[i]; for (int j = 0; j < i; j++) { subArray[j] = result[j]; } return subArray; } } return result; } /// <summary> /// Rozmyj pixel w poziomie /// </summary> /// <param name="y">indeks linii do rozmycia</param> /// <param name="blurArray">tablica wspolczynnikow rozmycia</param> /// <param name="pixelValues">tablica z wartosciami pikseli</param> /// <param name="stride">szerokosc linii danych z pikselami</param> /// <param name="width">szerokosc obrazu</param> private void blurHorizontal(int y, double[] blurArray, byte[] pixelValues, int stride, int width) { double[] pixelBytes = new double[bytesPerPixel]; for (var x = 0; x < width; x++) { int index = y * stride + x * bytesPerPixel; for (int i = 0; i < bytesPerPixel; i++) { pixelBytes[i] = pixelValues[index + i] * blurArray[0]; } double sum = blurArray[0]; for (var k = 1; k < blurArray.Length; k++) { if (x + k >= width) { index = y * stride + (x + k + 1 - width) * bytesPerPixel; } else { index = y * stride + (x + k) * bytesPerPixel; } for (int i = 0; i < bytesPerPixel; i++) { pixelBytes[i] += pixelValues[index + i] * blurArray[k]; } if (x - k < 0) { index = y * stride + Math.Abs(x - k) * bytesPerPixel; } else { index = y * stride + (x - k) * bytesPerPixel; } for (int i = 0; i < bytesPerPixel; i++) { pixelBytes[i] += pixelValues[index + i] * blurArray[k]; } sum += 2 * blurArray[k]; } index = y * stride + x * bytesPerPixel; for (int i = 0; i < bytesPerPixel; i++) { pixelValues[index + i] = (byte)(pixelBytes[i] / sum); } } } /// <summary> /// Rozmyj pixel w pionie /// </summary> /// <param name="y">indeks linii do rozmycia</param> /// <param name="blurArray">tablica wspolczynnikow rozmycia</param> /// <param name="pixelValues">tablica z wartosciami pikseli</param> /// <param name="stride">szerokosc linii danych z pikselami</param> /// <param name="width">szerokosc obrazu</param> private void blurVertical(int y, double[] blurArray, byte[] pixelValues, int stride, int width, int height) { double[] pixelBytes = new double[bytesPerPixel]; for (var x = 0; x < width; x++) { int index = y * stride + x * bytesPerPixel; for (int i = 0; i < bytesPerPixel; i++) { pixelBytes[i] = pixelValues[index + i] * blurArray[0]; } double sum = blurArray[0]; for (var k = 1; k < blurArray.Length; k++) { if (y + k >= height) { index = (y + k + 1 - height) * stride + x * bytesPerPixel; } else { index = (y + k) * stride + x * bytesPerPixel; } for (int i = 0; i < bytesPerPixel; i++) { pixelBytes[i] += pixelValues[index + i] * blurArray[k]; } if (y - k < 0) { index = Math.Abs(y - k) * stride + x * bytesPerPixel; } else { index = (y - k) * stride + x * bytesPerPixel; } for (int i = 0; i < bytesPerPixel; i++) { pixelBytes[i] += pixelValues[index + i] * blurArray[k]; } sum += 2 * blurArray[k]; } index = y * stride + x * bytesPerPixel; for (int i = 0; i < bytesPerPixel; i++) { pixelValues[index + i] = (byte)(pixelBytes[i] / sum); } } } /// <summary> /// Efekt Tilt-Shift /// </summary> private void obraz_MouseDown(object sender, MouseEventArgs e) { //Nie rob nic jezeli obraz jest jeszcze niewczytany if (zrodlo == null) { return; } //Kopiuj obrazek zrodlowy Bitmap bitmap = (Bitmap)zrodlo.Clone(); obraz.Image = bitmap; //Pobierz liczbe bajtow na punkt obrazu int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8; double[] pixelBytes = new double[bytesPerPixel]; //Pobierz wartosc wszystkich punktow obrazu BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, obraz.Width, obraz.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); byte[] pixelValues = new byte[Math.Abs(bmpData.Stride) * obraz.Height]; System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, pixelValues, 0, pixelValues.Length); //Dane obszarów int width = obraz.Width; int height = obraz.Height; int inFocusHeight = int.Parse(this.inFocusHeight.Text); int outFocusStartTop = Math.Max(e.Y - inFocusHeight / 2, 0); int outFocusStartDown = Math.Min(e.Y + inFocusHeight / 2, height); double outFocusHeight = e.Y > height / 2 ? outFocusStartTop : (height - outFocusStartDown); double outFocusBlur = double.Parse(blur.Text) - 0.1; //Rozmyj w poziomie obraz nad obszarem ostrym for (int y=0; y<outFocusStartTop; y++) { double factor = (outFocusStartTop-y)/outFocusHeight; double[] blurArray = gaussianArray(factor * outFocusBlur + 0.1, width); blurHorizontal(y, blurArray, pixelValues, bmpData.Stride, width); } //Rozmyj w poziomie obraz pod obszarem ostrym for (int y = outFocusStartDown; y < height; y++) { double factor = (y - outFocusStartDown) / outFocusHeight; double[] blurArray = gaussianArray(factor * outFocusBlur + 0.1, width); blurHorizontal(y, blurArray, pixelValues, bmpData.Stride, width); } //Rozmyj w pionie obraz nad obszarem ostrym for (int y = 0; y < outFocusStartTop; y++) { double factor = (outFocusStartTop - y) / outFocusHeight; double[] blurArray = gaussianArray(factor * outFocusBlur + 0.1, width); blurVertical(y, blurArray, pixelValues, bmpData.Stride, width, height); } //Rozmyj w pionie obraz pod obszarem ostrym for (int y = outFocusStartDown; y < height; y++) { double factor = (y - outFocusStartDown) / outFocusHeight; double[] blurArray = gaussianArray(factor * outFocusBlur + 0.1, width); blurVertical(y, blurArray, pixelValues, bmpData.Stride, width, height); } System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, bmpData.Scan0, pixelValues.Length); bitmap.UnlockBits(bmpData); } } }