Files
AgraSandbox/AgraV2/EffectMedianFilter.cs
T

150 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AgraV2
{
internal class EffectMedianFilter : Effect
{
private readonly object balancelock = new object();
private int _Radius;
public int Radius { get => _Radius; set => _Radius = value; }
public EffectMedianFilter(string name, Panel toolBox) : base(name, toolBox)
{
//This is just the default value
Radius = 1;
}
public override void populatePannel()
{
Label label = new Label();
label.Text = "Radius : ";
NumericUpDown nup = new NumericUpDown();
nup.Minimum = 1;
nup.Maximum = 100;
nup.Increment = 1;
nup.ValueChanged += nup_ValueChanged;
nup.Location = new Point(label.Location.X + label.Width,label.Location.Y);
ToolBox.Controls.Add(label);
ToolBox.Controls.Add(nup);
}
private void nup_ValueChanged(object sender, EventArgs e)
{
if (sender is NumericUpDown)
{
NumericUpDown nup = (NumericUpDown)sender;
Radius = (int)nup.Value;
}
}
public override Bitmap[] apply(Bitmap inputBmp)
{
if (inputBmp == null)
return null;
//This is important for when the user wants to re apply an effet
ProcessingDuration = new List<double>();
Bitmap[] result = new Bitmap[] { medianFilter(inputBmp) };
return result;
}
public unsafe Bitmap medianFilter(Bitmap inputBmp)
{
Chrono.Restart();
Size imgSize = new Size(inputBmp.Width,inputBmp.Height);
Bitmap result = new Bitmap(imgSize.Width, imgSize.Height);
BitmapData resultData = result.LockBits(new Rectangle(0, 0, imgSize.Width, imgSize.Height), ImageLockMode.ReadWrite, inputBmp.PixelFormat);
BitmapData inputData = inputBmp.LockBits(new Rectangle(0, 0, imgSize.Width, imgSize.Height), ImageLockMode.ReadWrite, inputBmp.PixelFormat);
int bytesPerPixel = Bitmap.GetPixelFormatSize(inputBmp.PixelFormat) / 8;
int byteCount = resultData.Stride * inputBmp.Height;
int offset = resultData.Stride - bytesPerPixel * imgSize.Width;
lock (balancelock)
{
byte* resultStartPx = (byte*)resultData.Scan0.ToPointer();
byte* resultCursor = resultStartPx;
byte* originalStartPx = (byte*)inputData.Scan0.ToPointer();
byte* originalCursor = originalStartPx;
for (int y = 0; y < inputBmp.Height; y++)
{
for (int x = 0; x < imgSize.Width; x++)
{
//We wont process border pixels
if (x > Radius && y > Radius && x < imgSize.Width - Radius && y < inputBmp.Height - Radius)
{
List<int> Blues = new List<int>();
List<int> Greens = new List<int>();
List<int> Reds = new List<int>();
List<Point> surroundingPixels = getSurroundingPixels(new Point(x, y), Radius, imgSize.Width, inputBmp.Height);
foreach (Point pixel in surroundingPixels)
{
int relativePosition = (pixel.X) * bytesPerPixel + (pixel.Y * (imgSize.Width * bytesPerPixel + offset));
byte* address = (byte*)(relativePosition + originalStartPx);
Blues.Add(address[0]);
Greens.Add(address[1]);
Reds.Add(address[2]);
}
Blues.Sort();
Greens.Sort();
Reds.Sort();
resultCursor[0] = (byte)Blues[Blues.Count / 2];
resultCursor[1] = (byte)Greens[Greens.Count / 2];
resultCursor[2] = (byte)Reds[Reds.Count / 2];
}
originalCursor += bytesPerPixel;
resultCursor += bytesPerPixel;
}
//Taking care of the offset
originalCursor += offset;
resultCursor += offset;
}
}
result.UnlockBits(resultData);
inputBmp.UnlockBits(inputData);
Chrono.Stop();
ProcessingDuration.Add(Chrono.ElapsedMilliseconds / 1000.0);
return result;
}
public List<Point> getSurroundingPixels(Point centerPoint, int Radius, int imageWidth, int imageHeight)
{
List<Point> result = new List<Point>();
int width = (Radius * 2 + 1);
Point cursor = centerPoint;
//we place the cursor on the top left of the matrix
cursor = new Point(cursor.X - Radius, cursor.Y - Radius);
for (int y = 0; y < width; y++)
{
//This scans top to bottom
for (int x = 0; x < width; x++)
{
//This scans left to right
result.Add(cursor);
cursor = new Point(cursor.X + 1, cursor.Y);
}
cursor = new Point(cursor.X - (Radius * 2 + 1), cursor.Y + 1);
}
return result;
}
}
}