Sharpen on a Bitmap using C#
Solution 1
public static Bitmap sharpen(Bitmap image)
{
Bitmap sharpenImage = new Bitmap(image.Width, image.Height);
int filterWidth = 3;
int filterHeight = 3;
int w = image.Width;
int h = image.Height;
double[,] filter = new double[filterWidth, filterHeight];
filter[0, 0] = filter[0, 1] = filter[0, 2] = filter[1, 0] = filter[1, 2] = filter[2, 0] = filter[2, 1] = filter[2, 2] = -1;
filter[1, 1] = 9;
double factor = 1.0;
double bias = 0.0;
Color[,] result = new Color[image.Width, image.Height];
for (int x = 0; x < w; ++x)
{
for (int y = 0; y < h; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
//=====[REMOVE LINES]========================================================
// Color must be read per filter entry, not per image pixel.
Color imageColor = image.GetPixel(x, y);
//===========================================================================
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth / 2 + filterX + w) % w;
int imageY = (y - filterHeight / 2 + filterY + h) % h;
//=====[INSERT LINES]========================================================
// Get the color here - once per fiter entry and image pixel.
Color imageColor = image.GetPixel(imageX, imageY);
//===========================================================================
red += imageColor.R * filter[filterX, filterY];
green += imageColor.G * filter[filterX, filterY];
blue += imageColor.B * filter[filterX, filterY];
}
int r = Math.Min(Math.Max((int)(factor * red + bias), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + bias), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + bias), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
for (int i = 0; i < w; ++i)
{
for (int j = 0; j < h; ++j)
{
sharpenImage.SetPixel(i, j, result[i, j]);
}
}
return sharpenImage;
}
Solution 2
I took Daniel's answer and modified it for performance, by using BitmapData class, since using GetPixel/SetPixel is very expensive and inappropriate for performance-hungry systems. It works exactly the same as the previous solution and can be used instead.
public static Bitmap Sharpen(Bitmap image)
{
Bitmap sharpenImage = (Bitmap)image.Clone();
int filterWidth = 3;
int filterHeight = 3;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
double[,] filter = new double[filterWidth, filterHeight];
filter[0, 0] = filter[0, 1] = filter[0, 2] = filter[1, 0] = filter[1, 2] = filter[2, 0] = filter[2, 1] = filter[2, 2] = -1;
filter[1, 1] = 9;
double factor = 1.0;
double bias = 0.0;
Color[,] result = new Color[image.Width, image.Height];
// Lock image bits for read/write.
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride * height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth / 2 + filterX + width) % width;
int imageY = (y - filterHeight / 2 + filterY + height) % height;
rgb = imageY * pbits.Stride + 3 * imageX;
red += rgbValues[rgb + 2] * filter[filterX, filterY];
green += rgbValues[rgb + 1] * filter[filterX, filterY];
blue += rgbValues[rgb + 0] * filter[filterX, filterY];
}
int r = Math.Min(Math.Max((int)(factor * red + bias), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + bias), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + bias), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
rgb = y * pbits.Stride + 3 * x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
return sharpenImage;
}
Solution 3
This will create a softer sharpening effect. You can expand the filter array if you need to, or change the 16 to something larger, but I found this isn't as harsh as the one you have.
const int filterWidth = 5;
const int filterHeight = 5;
double[,] filter = new double[filterWidth,filterHeight] {
{ -1, -1, -1, -1, -1 },
{ -1, 2, 2, 2, -1 },
{ -1, 2, 16, 2, -1 },
{ -1, 2, 2, 2, -1 },
{ -1, -1, -1, -1, -1 }
};
double factor = 1.0 / 16.0;
Solution 4
Ok, fixed the problem with distored edges. Here´s the updated one:
/// <summary>
/// Sharpens the specified image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="strength">The strength.</param>
/// <returns></returns>
public static Bitmap Sharpen(Image image, double strength)
{
using (var bitmap = image as Bitmap)
{
if (bitmap != null)
{
var sharpenImage = bitmap.Clone() as Bitmap;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
const int filterSize = 5;
var filter = new double[,]
{
{-1, -1, -1, -1, -1},
{-1, 2, 2, 2, -1},
{-1, 2, 16, 2, -1},
{-1, 2, 2, 2, -1},
{-1, -1, -1, -1, -1}
};
double bias = 1.0 - strength;
double factor = strength/16.0;
const int s = filterSize/2;
var result = new Color[image.Width,image.Height];
// Lock image bits for read/write.
if (sharpenImage != null)
{
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride*height;
var rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = s; x < width - s; x++)
{
for (int y = s; y < height - s; y++)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterSize; filterX++)
{
for (int filterY = 0; filterY < filterSize; filterY++)
{
int imageX = (x - s + filterX + width)%width;
int imageY = (y - s + filterY + height)%height;
rgb = imageY*pbits.Stride + 3*imageX;
red += rgbValues[rgb + 2]*filter[filterX, filterY];
green += rgbValues[rgb + 1]*filter[filterX, filterY];
blue += rgbValues[rgb + 0]*filter[filterX, filterY];
}
rgb = y * pbits.Stride + 3 * x;
int r = Math.Min(Math.Max((int)(factor * red + (bias * rgbValues[rgb + 2])), 0), 255);
int g = Math.Min(Math.Max((int)(factor * green + (bias * rgbValues[rgb + 1])), 0), 255);
int b = Math.Min(Math.Max((int)(factor * blue + (bias * rgbValues[rgb + 0])), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = s; x < width - s; x++)
{
for (int y = s; y < height - s; y++)
{
rgb = y*pbits.Stride + 3*x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
}
return sharpenImage;
}
}
return null;
}
Solution 5
I combined niaher's and David's answer and fixed the "bias" property. Now you can pass a "strength" between 0.0 and 1.0 to the Sharpen() function.
/// <summary>
/// Sharpens the specified image.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="strength">The strength between 0.0 and 1.0.</param>
/// <returns></returns>
public static Bitmap Sharpen(Image image, double strength)
{
using (var bitmap = image as Bitmap)
{
if (bitmap != null)
{
var sharpenImage = bitmap.Clone() as Bitmap;
int width = image.Width;
int height = image.Height;
// Create sharpening filter.
const int filterWidth = 5;
const int filterHeight = 5;
var filter = new double[,]
{
{-1, -1, -1, -1, -1},
{-1, 2, 2, 2, -1},
{-1, 2, 16, 2, -1},
{-1, 2, 2, 2, -1},
{-1, -1, -1, -1, -1}
};
double bias = 1.0 - strength;
double factor = strength/16.0;
var result = new Color[image.Width,image.Height];
// Lock image bits for read/write.
if (sharpenImage != null)
{
BitmapData pbits = sharpenImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = pbits.Stride*height;
var rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(pbits.Scan0, rgbValues, 0, bytes);
int rgb;
// Fill the color array with the new sharpened color values.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
double red = 0.0, green = 0.0, blue = 0.0;
for (int filterX = 0; filterX < filterWidth; filterX++)
{
for (int filterY = 0; filterY < filterHeight; filterY++)
{
int imageX = (x - filterWidth/2 + filterX + width)%width;
int imageY = (y - filterHeight/2 + filterY + height)%height;
rgb = imageY*pbits.Stride + 3*imageX;
red += rgbValues[rgb + 2]*filter[filterX, filterY];
green += rgbValues[rgb + 1]*filter[filterX, filterY];
blue += rgbValues[rgb + 0]*filter[filterX, filterY];
}
rgb = y*pbits.Stride + 3*x;
int r = Math.Min(Math.Max((int) (factor*red + (bias*rgbValues[rgb + 2])), 0), 255);
int g = Math.Min(Math.Max((int) (factor*green + (bias*rgbValues[rgb + 1])), 0), 255);
int b = Math.Min(Math.Max((int) (factor*blue + (bias*rgbValues[rgb + 0])), 0), 255);
result[x, y] = Color.FromArgb(r, g, b);
}
}
}
// Update the image with the sharpened pixels.
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
rgb = y*pbits.Stride + 3*x;
rgbValues[rgb + 2] = result[x, y].R;
rgbValues[rgb + 1] = result[x, y].G;
rgbValues[rgb + 0] = result[x, y].B;
}
}
// Copy the RGB values back to the bitmap.
Marshal.Copy(rgbValues, 0, pbits.Scan0, bytes);
// Release image bits.
sharpenImage.UnlockBits(pbits);
}
return sharpenImage;
}
}
return null;
}
Allek
Student of Faculty of Fundamental Problems of Technology on Wroclaw University of Technology
Updated on December 30, 2020Comments
-
Allek over 3 years
I want to put a sharpen filter on an image. I have found a web with short tutorial. I tried to do it in C# so here is my code. Anyway, I tried to find out why it is not working. I do not know if I am doing something wrong, if yes, please advise me what to do to make it work as it should be. Thanks
public static Bitmap sharpen(Bitmap image) { Bitmap sharpenImage = new Bitmap(image.Width, image.Height); int filterWidth = 3; int filterHeight = 3; int w = image.Width; int h = image.Height; double[,] filter = new double[filterWidth, filterHeight]; filter[0, 0] = filter[0, 1] = filter[0, 2] = filter[1, 0] = filter[1, 2] = filter[2, 0] = filter[2, 1] = filter[2, 2] = -1; filter[1, 1] = 9; double factor = 1.0; double bias = 0.0; Color[,] result = new Color[image.Width, image.Height]; for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { double red = 0.0, green = 0.0, blue = 0.0; Color imageColor = image.GetPixel(x, y); for (int filterX = 0; filterX < filterWidth; filterX++) { for (int filterY = 0; filterY < filterHeight; filterY++) { int imageX = (x - filterWidth / 2 + filterX + w) % w; int imageY = (y - filterHeight / 2 + filterY + h) % h; red += imageColor.R * filter[filterX, filterY]; green += imageColor.G * filter[filterX, filterY]; blue += imageColor.B * filter[filterX, filterY]; } int r = Math.Min(Math.Max((int)(factor * red + bias), 0), 255); int g = Math.Min(Math.Max((int)(factor * green + bias), 0), 255); int b = Math.Min(Math.Max((int)(factor * blue + bias), 0), 255); result[x, y] = Color.FromArgb(r, g, b); } } } for (int i = 0; i < w; ++i) { for (int j = 0; j < h; ++j) { sharpenImage.SetPixel(i, j, result[i, j]); } } return sharpenImage; }