How To Make Color Image Smoothing And Sharpening – C#
This color image smoothing and sharpening tutorial shows how to apply convolution for blurring and sharpening images with C#.
Filter by Category
Color image smoothing and sharpening are processing techniques, we use for adjusting details in color images. So far, we’ve dealt with color image transformations that change pixel values individually. However, with this guide, we’ll expand our knowledge by using convolution process.
What is this convolution anyway?
Basically, we have a small matrix or a window, which we slide pixel by pixel across the entire image. This way, we can apply more complex transformations on each pixel, because when we’re calculating new pixel value we take into account its surrounding pixels.
We call this spatial filtering, where we use a kernel or a small matrix or window. Furthermore, we use this kernel to multiply its values with pixel values on the input image. We call this process convolution. So, to transform the entire image, we need to slide this kernel pixel by pixel across the image.
When we talk about smoothing images, it basically means we’re going to blur it. When we’re working with a color image, we’ll need to apply this smoothing process on each color channel separately.
Color image smoothing is spatial filtering process where its kernel consists of all ones. This means that we only need our input image pixel values to compute new output values. We just need to get the mean value of the pixel neighborhood and set it as the new output pixel value.
Bonus: We can also convert the image into HSI color space and apply this transformation only on intensity component and put the image back together. It doesn’t change color as we don’t transform hue or saturation. However, the difference isn’t all that noticable, if we compare it to method we described above.
Here’s a function that applies this transformation, which I’ve written in C# programming language.
public static Bitmap ImageSmooth(this Bitmap image)
{
int w = image.Width;
int h = image.Height;
BitmapData image_data = image.LockBits(
new Rectangle(0, 0, w, h),
ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
int bytes = image_data.Stride * image_data.Height;
byte[] buffer = new byte[bytes];
byte[] result = new byte[bytes];
Marshal.Copy(image_data.Scan0, buffer, 0, bytes);
image.UnlockBits(image_data);
for (int i = 2; i < w - 2; i++)
{
for (int j = 2; j < h - 2; j++)
{
int p = i * 3 + j * image_data.Stride;
for (int k = 0; k < 3; k++)
{
List<int> vals = new List<int>();
for (int xkernel = -2; xkernel < 3; xkernel++)
{
for (int ykernel = -2; ykernel < 3; ykernel++)
{
int kernel_p = k + p + xkernel * 3 + ykernel * image_data.Stride;
vals.Add(buffer[kernel_p]);
}
}
result[p + k] = (byte)(vals.Sum() / vals.Count);
}
}
}
Bitmap res_img = new Bitmap(w, h);
BitmapData res_data = res_img.LockBits(
new Rectangle(0, 0, w, h),
ImageLockMode.WriteOnly,
PixelFormat.Format24bppRgb);
Marshal.Copy(result, 0, res_data.Scan0, bytes);
res_img.UnlockBits(res_data);
return res_img;
}
Color image sharpening works quite similarly as smoothing does. We’ll use convolution in both cases, but with sharpening, kernel we’ll use will hold different values.
Filter, we’re going to use for sharpening the image is called Laplacian. We’ve already talked about it in another post when we were processing images in Fourier or frequency domain.
As we already mentioned, methods that we used with smoothing can also be applied for sharpening. I mean that we’ll apply it to each color channel sub-image separately. Also, we can convert the image to HSI color space and transform only the intensity component.
public static Bitmap ImageSharpen(this Bitmap image)
{
int w = image.Width;
int h = image.Height;
BitmapData image_data = image.LockBits(
new Rectangle(0, 0, w, h),
ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
int bytes = image_data.Stride * image_data.Height;
byte[] buffer = new byte[bytes];
byte[] result = new byte[bytes];
Marshal.Copy(image_data.Scan0, buffer, 0, bytes);
image.UnlockBits(image_data);
for (int i = 2; i < w - 2; i++)
{
for (int j = 2; j < h - 2; j++)
{
int p = i * 3 + j * image_data.Stride;
for (int k = 0; k < 3; k++)
{
double val = 0d;
for (int xkernel = -1; xkernel < 2; xkernel++)
{
for (int ykernel = -1; ykernel < 2; ykernel++)
{
int kernel_p = k + p + xkernel * 3 + ykernel * image_data.Stride;
val += buffer[kernel_p] * Kernels.Laplacian[xkernel + 1, ykernel + 1];
}
}
val = val > 0 ? val : 0;
result[p + k] = (byte)((val + buffer[p + k]) > 255 ? 255: (val + buffer[p + k]));
}
}
}
Bitmap res_img = new Bitmap(w, h);
BitmapData res_data = res_img.LockBits(
new Rectangle(0, 0, w, h),
ImageLockMode.WriteOnly,
PixelFormat.Format24bppRgb);
Marshal.Copy(result, 0, res_data.Scan0, bytes);
res_img.UnlockBits(res_data);
return res_img;
}
And here is the class that holds Laplacian kernel values.
public static class Kernels
{
public static double[,] Laplacian
{
get
{
return new double[,]
{
{ 0,-1, 0 },
{-1, 4,-1 },
{ 0,-1, 0 }
};
}
}
}
I hope this guide helped you clarify anything about color image smoothing or sharpening.
You can download the whole project as well and try it out yourself.