private async void btnApplyFilter_Click(object sender, EventArgs e) { if (!_imageProcessService.ImageLoaded) { MessageBox.Show("Please select an image before applying a filter", "No Image Loaded", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (!_imageProcessService.IsRunning) { SetFilterParameters(); EdgeHandling edgeHandling = rbExtend.Checked ? EdgeHandling.Extend : EdgeHandling.Wrap; btnApplyFilter.Text = "Cancel"; await _imageProcessService.ApplyFilter(_selectedImageFilter, edgeHandling); btnApplyFilter.Text = "Apply Filter"; } else { await _imageProcessService.CancelApplyFilter(); btnApplyFilter.Text = "Apply Filter"; } }
private KernelFunction GetKernelFunction(Kernel kernel, EdgeHandling edgeHandling, int range) { if (edgeHandling == EdgeHandling.MIRROR_EXTENSION) { return((channel, ppx, ppy, action) => { int middleX = (int)ppx; int middleY = (int)ppy; for (int x = 0; x < kernel.Size; x++) { for (int y = 0; y < kernel.Size; y++) { int pixelX = middleX - range + x; int pixelY = middleY - range + y; int finalX, finalY; if (pixelX < 0) { finalX = -pixelX; } else if (pixelX >= Width) { finalX = (int)(Width - (pixelX - Width + 1)); } else { finalX = pixelX; } if (pixelY < 0) { finalY = -pixelY; } else if (pixelY >= Height) { finalY = (int)(Height - (pixelY - Height + 1)); } else { finalY = pixelY; } action(channel.GetMultipliedValue((uint)finalX, (uint)finalY, kernel[(uint)x, (uint)y])); } } }); } else { return((channel, ppx, ppy, action) => { int middleX = (int)ppx; int middleY = (int)ppy; for (int x = 0; x < kernel.Size; x++) { for (int y = 0; y < kernel.Size; y++) { int pixelX = middleX - range + x; int pixelY = middleY - range + y; if (pixelX < 0 || pixelX >= Width || pixelY < 0 || pixelY >= Height) { action(default);
public SourceDataBase(Bitmap source, EdgeHandling edgeHandling) { _imageSize = source.Size; _edgeHandling = edgeHandling; _imageDataBytes = GetImageBytes(source, out _stride); ImageWidth = source.Width; ImageHeight = source.Height; NumberOfPixels = source.Height * source.Width; }
public async Task ApplyFilter(IImageFilter imageFilter, EdgeHandling edgeHandling) { if (IsRunning || _sourceBitmap == null) { return; } IsRunning = true; await Task.Run(() => { ApplyFilterMultithreaded(imageFilter, edgeHandling); }); IsRunning = false; }
private void GetFilterArea(EdgeHandling edgeHandling, uint range, out uint lowerX, out uint upperX, out uint lowerY, out uint upperY) { if (edgeHandling == EdgeHandling.SKIP_UNDEFINED) { lowerX = range; upperX = Width - range; lowerY = range; upperY = Height - range; } else { lowerX = 0; upperX = Width; lowerY = 0; upperY = Height; } }
public static Convolution <T> Create <T>( Matrix <double> kernel, Accumulator <T> accumulate, EdgeHandling edgeHandling) where T : struct, IEquatable <T>, IFormattable { if (kernel.RowCount % 2 == 0) { const string err = "Kernel must have an odd number of rows."; throw new ArgumentException(err, nameof(kernel)); } if (kernel.ColumnCount % 2 == 0) { const string err = "Kernel must have an odd number of columns."; throw new ArgumentException(err, nameof(kernel)); } IAccumulationStrategy <T> strategy; switch (edgeHandling) { case EdgeHandling.Crop: strategy = new CroppingStrategy <T>(accumulate); break; case EdgeHandling.Extend: strategy = new ExtendingStrategy <T>(accumulate); break; case EdgeHandling.Mirror: strategy = new MirroringStrategy <T>(accumulate); break; case EdgeHandling.Wrap: strategy = new WrappingStrategy <T>(accumulate); break; default: const string err = "Please specify an edge handling strategy."; throw new ArgumentException(err, nameof(edgeHandling)); } return(new Convolution <T>(kernel, strategy)); }
public void Convolution(ConvolutionKernel kernel, EdgeHandling edgeHandling) { unsafe { var _workingBitmap = new Bitmap(Image); var _rect = new Rectangle(0, 0, Image.Width, Image.Height); BitmapData _originalBitmapData = Image.LockBits(_rect, ImageLockMode.ReadWrite, Image.PixelFormat); var _workingBitmapData = _workingBitmap.LockBits(_rect, ImageLockMode.ReadWrite, Image.PixelFormat); int _bytesPerPixel = Bitmap.GetPixelFormatSize(Image.PixelFormat) / 8; int _heightInPixels = _originalBitmapData.Height; int _widthInBytes = _originalBitmapData.Width * _bytesPerPixel; int _offset = kernel.Offset; int _imageOffset = 0; //edge position byte * _firstPixelPointerOriginal = (byte *)_originalBitmapData.Scan0; byte * _firstPixelPointerWorking = (byte *)_workingBitmapData.Scan0; byte *[] _rowsOriginal = new byte *[_heightInPixels]; Func <int, int, int> _clamping = null; switch (edgeHandling) { case EdgeHandling.Extend: _clamping = ClampExtend; break; case EdgeHandling.Wrap: _clamping = ClampWrap; break; case EdgeHandling.Mirror: _clamping = ClampMirror; break; case EdgeHandling.Crop: default: _imageOffset = kernel.Offset; _clamping = ClampCrop; break; } for (int i = 0; i < _heightInPixels; i++) { _rowsOriginal[i] = _firstPixelPointerOriginal + (i * _originalBitmapData.Stride); } //BGR format for (int y = _imageOffset; y < _heightInPixels - _imageOffset; y++) { for (int x = _imageOffset * _bytesPerPixel; x < _widthInBytes - _imageOffset * _bytesPerPixel; x = x + _bytesPerPixel) { double _r = 0.0; double _g = 0.0; double _b = 0.0; byte * _workingScanLine = _firstPixelPointerWorking + (y * _workingBitmapData.Stride); for (int ky = -_offset; ky <= _offset; ky++) { int _yOffset = _clamping(y + ky, _heightInPixels - 1); // byte *_originalScanLine = _rowsOriginal[_yOffset]; for (int kx = -_offset; kx <= _offset; kx++) { double _k = kernel[kx, ky]; int _xOffset = _clamping(x + kx * _bytesPerPixel, _widthInBytes - 3); _b += _originalScanLine[_xOffset] * _k; _g += _originalScanLine[_xOffset + 1] * _k; _r += _originalScanLine[_xOffset + 2] * _k; } } _workingScanLine[x] = Clamp(_b / kernel.NormalizationFactor + kernel.Bias); _workingScanLine[x + 1] = Clamp(_g / kernel.NormalizationFactor + kernel.Bias); _workingScanLine[x + 2] = Clamp(_r / kernel.NormalizationFactor + kernel.Bias); } } _workingBitmap.UnlockBits(_workingBitmapData); Image.UnlockBits(_originalBitmapData); Image = _workingBitmap; //originalBitmap = _workingBitmap.Clone(_rect, originalBitmap.PixelFormat); //_workingBitmap.Save(@"C:\Users\rokr\Desktop\aaa_no.bmp"); } }
private void ApplyFilterMultithreaded(IImageFilter imageFilter, EdgeHandling edgeHandling) { int numberOfThreads = Environment.ProcessorCount; // ReSharper disable once InconsistentlySynchronizedField _updateProgressDictionary.Clear(); try { _sourceDataBase = new SourceDataBase(_sourceBitmap, edgeHandling); int imgPixelSize = _sourceDataBase.NumberOfPixels; int workSize = Convert.ToInt32(Math.Floor(imgPixelSize / (double)numberOfThreads)); int remaningPixels = imgPixelSize % numberOfThreads; //Split work into number of threads awailable for this system var doneEvents = new ManualResetEvent[numberOfThreads]; processFilterArray = new ProcessFilter[numberOfThreads]; int startIndex = 0; for (int i = 0; i < numberOfThreads; i++) { doneEvents[i] = new ManualResetEvent(false); ProcessFilter processFilter; var sourceData = new SourceData(_sourceDataBase); if (remaningPixels > 0 && i == numberOfThreads - 1) { processFilter = new ProcessFilter(sourceData, imageFilter, startIndex, startIndex + workSize + remaningPixels, doneEvents[i]); } else { processFilter = new ProcessFilter(sourceData, imageFilter, startIndex, startIndex + workSize, doneEvents[i]); } processFilter.OnProgressUpdate += processFilter_OnProgressUpdate; startIndex += workSize; processFilterArray[i] = processFilter; ThreadPool.QueueUserWorkItem(processFilter.ThreadPoolCallback, i); } waitHandleArray = new WaitHandle[doneEvents.Length]; for (int i = 0; i < doneEvents.Length; i++) { waitHandleArray[i] = doneEvents[i]; } // Wait for all threads in pool to complete. WaitHandle.WaitAll(waitHandleArray); _outputBitmap = _sourceDataBase.GetOutputBitmap(); } catch (Exception ex) { string message = ex.Message; Console.WriteLine(message); } processFilterArray = null; waitHandleArray = null; IsRunning = false; if (OnProcessComplete != null) { OnProcessComplete.Invoke(this, new EventArgs()); } }
public override Image Filter(Kernel kernel, FilterType filter = FilterType.AVERAGE, EdgeHandling edgeHandling = EdgeHandling.MIRROR_EXTENSION) { uint size = kernel.Size; uint range = size / 2; if (Width < range + 1 || Height < range + 1) { throw new IndexOutOfRangeException("Neighborhood range cannot be greater than image size."); } Image result; if (edgeHandling == EdgeHandling.SKIP_UNDEFINED) { result = Clone(); } else { result = ImageFactory.Create(Width, Height, GetColorModel(), GetDataType()); } uint lowerX, upperX, lowerY, upperY; GetFilterArea(edgeHandling, range, out lowerX, out upperX, out lowerY, out upperY); KernelFunction kernelFunction = GetKernelFunction(kernel, edgeHandling, (int)range); FilterOperation filterOperation = GetFilterOperation(filter); for (uint x = lowerX; x < upperX; x++) { for (uint y = lowerY; y < upperY; y++) { filterOperation(this, kernelFunction, size, x, y, result); } } return(result); }
/// <summary> /// Convolves the image using a specified kernel edge handling technique. /// </summary> /// <param name="image">Image to convolve</param> /// <param name="kernel">Convolution kernel</param> /// <param name="edgeHandling">Convolution Edge Handling Technique</param> /// <returns>Convolved Image</returns> public static IImage <double> Convolve(this IImage <double> image, double[][] kernel, EdgeHandling edgeHandling) { // Kernel var kernel_height = kernel.Length; var kernel_width = kernel[0].Length; // Mid Point var k_mid_h = kernel_height % 2 == 0 ? (kernel_height >> 1) - 1 : (kernel_height >> 1); var k_mid_w = kernel_width % 2 == 0 ? (kernel_width >> 1) - 1 : (kernel_width >> 1); // Dimensions for the new Image int width = image.Width; int height = image.Height; Func <int, int> edgeX; Func <int, int> edgeY; switch (edgeHandling) { // Wrap Pixels case EdgeHandling.Wrap: edgeX = x => x >= width ? x - width : (x < 0 ? width + x : x); edgeY = y => y >= height ? y - width : (y < 0 ? height + y : y); break; // Start with offset and insert into cropped image case EdgeHandling.Crop: width -= kernel_width; height -= kernel_height; edgeX = x => x + k_mid_w; edgeY = y => y + k_mid_h; break; // Extend case EdgeHandling.Extend: default: edgeX = x => x < 0 ? 0 : (x >= width ? width - 1 : x); edgeY = y => y < 0 ? 0 : (y >= height ? height - 1 : y); break; } IImage <double> conv = new Image(width, height); // Convolution is a general purpose filter effect for images // Is a matrix applied to an image and a mathematical operation // comprised of integers // It works by determining the value of a central pixel by adding the // weighted values of all its neighbors together // The output is a new modified filtered image // for each image row in output image, Parallel.For(0, height - 1, y => { // for each pixel in image row: for (int x = 0; x < width; x++) { // set accumulator to zero double accumulator = 0.0; // for each kernel row in kernel: for (int k_x = 0; k_x < kernel_width; k_x++) { // for each element in kernel row: for (int k_y = 0; k_y < kernel_height; k_y++) { // int index1 = image.Width * Mod(y + kcol - w, image.Height) + Mod(x + krow - h, image.Width); int index1 = image.Width * edgeY(y + k_y - k_mid_h) + edgeX(x + k_x - k_mid_w); int kernelX = kernel_width - k_x - 1; int kernelY = kernel_height - k_y - 1; accumulator += image[index1] * kernel[kernelY][kernelX]; } } // set output image pixel to accumulator conv[y * width + x] = accumulator; } }); return(conv); }
public abstract Image Filter(Kernel kernel, FilterType filter = FilterType.AVERAGE, EdgeHandling edgeHandling = EdgeHandling.MIRROR_EXTENSION);