본문 바로가기
C#
2015.04.08 16:58

Unsharp Mask Filter 구현방법

다물칸 주소복사
조회 수 237 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄 첨부
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄 첨부
Extra Form
구분 팁&트릭
출처 https://code.msdn.microsoft.com/Image-Unsharp-Mask-3b2a9cdf

마소사이트는 언제 없어질지 몰라서 그대로 복사해서 가져왔습니다.


Introduction

The purpose of this article is to explore and illustrate the concept of . This article implements  in the form of a 3×3 , 5×5 , 3×3 Mean filter and a 5×5 Mean filter.

Building the Sample

There are no special requirements or instructions for building the sample source code.

Using the Sample Application

The sample source code associated with this article includes a  based sample application implementing the concepts explored throughout this article.

When using the Image Unsharp Mask sample application users can select a source/input image from the local  system by clicking the Load Image button. The dropdown  at the bottom of the screen allows the user to select an unsharp masking variation. On the right hand side of the screen users can specify the level/intensity of resulting .

Clicking the Save Image button allows a user to save resulting  to the local file system. The image below is a screenshot of the Image Unsharp Mask sample application in action:

What is Image Unsharp Masking? 

A good definition of  can be found on :

Unsharp masking (USM) is an image manipulation technique, often available in software.

The "unsharp" of the name derives from the fact that the technique uses a blurred, or "unsharp", positive image to create a "mask" of the original image. The unsharped mask is then combined with the negative image, creating an image that is less blurry than the original. The resulting image, although clearer, probably loses accuracy with respect to the image’s subject. In the context of , an unsharp mask is generally a  or filter that amplifies high-frequency components.

In this article we implement  by first creating a blurred copy of a source/input  then subtracting the blurred  from the original , which is known as the mask. Increased  is achieved by adding a factor of the mask to the original .

Applying a Convolution Matrix

The sample source code provides the definition for the ConvolutionFilter  targeting the  class. method is invoked when implementing . The definition of the ConvolutionFilter  as follows:

C#
private static Bitmap ConvolutionFilter(Bitmap sourceBitmap,   
                                     double[,] filterMatrix,   
                                          double factor = 1,   
                                               int bias = 0,   
                                     bool grayscale = false )   
{  
     BitmapData sourceData = sourceBitmap.LockBits(new Rectangle (00,  
                              sourceBitmap.Width, sourceBitmap.Height),  
                                                ImageLockMode.ReadOnly,   
                                          PixelFormat.Format32bppArgb);  
 
   
     byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];  
     byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];  
 
   
     Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);  
     sourceBitmap.UnlockBits(sourceData);  
 
   
     if (grayscale == true)  
     { 
         float rgb = 0;  
 
   
         for (int k = 0; k < pixelBuffer.Length; k += 4)  
         {  
             rgb = pixelBuffer[k] * 0.11f;  
             rgb += pixelBuffer[k + 1] * 0.59f;  
             rgb += pixelBuffer[k + 2] * 0.3f;  
 
   
             pixelBuffer[k] = (byte )rgb;  
             pixelBuffer[k + 1] = pixelBuffer[k];  
             pixelBuffer[k + 2] = pixelBuffer[k];  
             pixelBuffer[k + 3] = 255;  
         }  
     }  
 
   
     double blue = 0.0;  
     double green = 0.0;  
     double red = 0.0;  
 
   
     int filterWidth = filterMatrix.GetLength(1);  
     int filterHeight = filterMatrix.GetLength(0);  
 
   
     int filterOffset = (filterWidth-1) / 2;  
     int calcOffset = 0;  
 
   
     int byteOffset = 0;  
 
   
     for (int offsetY = filterOffset; offsetY <   
         sourceBitmap.Height - filterOffset; offsetY++)  
     {  
         for (int  offsetX = filterOffset; offsetX <   
             sourceBitmap.Width - filterOffset; offsetX++)  
         {  
             blue = 0;  
             green = 0;  
             red = 0;  
 
   
             byteOffset = offsetY *   
                          sourceData.Stride +   
                          offsetX * 4;  
 
    
             for (int  filterY = -filterOffset;   
                 filterY <= filterOffset; filterY++)  
             { 
                 for (int  filterX = -filterOffset;  
                     filterX <= filterOffset; filterX++)  
                 {  
 
    
                     calcOffset = byteOffset +   
                                  (filterX * 4) +   
                                  (filterY * sourceData.Stride);  
 
   
                     blue += (double)(pixelBuffer[calcOffset]) *  
                             filterMatrix[filterY + filterOffset,   
                                          filterX + filterOffset];  
 
   
                     green += (double)(pixelBuffer[calcOffset + 1]) *  
                              filterMatrix[filterY + filterOffset,   
                                           filterX + filterOffset];  
 
    
                     red += (double)(pixelBuffer[calcOffset + 2]) *  
                            filterMatrix[filterY + filterOffset,   
                                         filterX + filterOffset];  
                 } 
             } 
 
    
             blue = factor * blue + bias;  
             green = factor * green + bias;  
             red = factor * red + bias;  
 
   
             if (blue > 255)  
             { blue = 255; }  
             else if (blue < 0)  
             { blue = 0; }  
 
   
             if (green > 255)  
             { green = 255; }  
             else if (green < 0)  
             { green = 0; }  
 
   
             if (red > 255)  
             { red = 255; }  
             else if (red < 0)  
             { red = 0; }  
 
   
             resultBuffer[byteOffset] = (byte )(blue);  
             resultBuffer[byteOffset + 1] = (byte )(green);  
             resultBuffer[byteOffset + 2] = (byte )(red);  
             resultBuffer[byteOffset + 3] = 255;  
        } 
    }  
 
   
     Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, 
                                      sourceBitmap.Height);  
   
     BitmapData resultData = resultBitmap.LockBits(new Rectangle (00,  
                              resultBitmap.Width, resultBitmap.Height),  
                                               ImageLockMode.WriteOnly,  
                                          PixelFormat.Format32bppArgb);  
 
   
     Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);  
     resultBitmap.UnlockBits(resultData);  
 
    
     return resultBitmap;  
} 
 

Subtracting and Adding Images

An important step required when implementing  comes in the form of creating a mask by subtracting a blurred copy from the original  and then adding a factor of the mask to the original . In order to achieve increased performance the sample source code combines the process of creating the mask and adding the mask to the original .

The SubtractAddFactorImage  iterates every pixel that forms part of an . In a single step the blurred pixel is subtracted from the original pixel, multiplied by a user specified factor and then added to the original pixel. The definition of the SubtractAddFactorImage  as follows:

C#
private static Bitmap SubtractAddFactorImage(  
                              this Bitmap subtractFrom,  
                                  Bitmap subtractValue,  
                                   float factor = 1.0f)  
{  
    BitmapData sourceData =   
               subtractFrom.LockBits(new Rectangle (00,  
               subtractFrom.Width, subtractFrom.Height),  
               ImageLockMode.ReadOnly,  
               PixelFormat.Format32bppArgb);  
 
   
    byte[] sourceBuffer = new byte[sourceData.Stride *   
                                   sourceData.Height];  
 
   
    Marshal.Copy(sourceData.Scan0, sourceBuffer, 0,   
                                sourceBuffer.Length);  
 
   
    byte[] resultBuffer = new byte[sourceData.Stride *   
                                   sourceData.Height];  
 
   
    BitmapData subtractData =   
               subtractValue.LockBits(new Rectangle (00,  
               subtractValue.Width, subtractValue.Height),  
               ImageLockMode.ReadOnly,  
               PixelFormat.Format32bppArgb);  
 
   
    byte[] subtractBuffer = new byte[subtractData.Stride *  
                                     subtractData.Height];  
 
   
    Marshal.Copy(subtractData.Scan0, subtractBuffer, 0,  
                                 subtractBuffer.Length);  
 
   
    subtractFrom.UnlockBits(sourceData);  
    subtractValue.UnlockBits(subtractData);  
 
   
    double blue = 0;  
    double green = 0;  
    double red = 0;  
 
   
    for (int k = 0; k < resultBuffer.Length &&  
                   k < subtractBuffer.Length; k += 4)  
    {  
        blue = sourceBuffer[k] +   
              (sourceBuffer[k] -  
               subtractBuffer[k]) * factor;  
 
   
        green = sourceBuffer[k + 1] +   
               (sourceBuffer[k + 1] -  
                subtractBuffer[k + 1]) * factor;  
 
   
        red = sourceBuffer[k + 2] +   
             (sourceBuffer[k + 2] -  
              subtractBuffer[k + 2]) * factor;  
 
   
        blue = (blue < 0 ? 0 : (blue > 255 ? 255 : blue));  
        green = (green < 0 ? 0 : (green > 255 ? 255 : green));  
        red = (red < 0 ? 0 : (red > 255 ? 255 : red));  
 
   
        resultBuffer[k] = (byte )blue;  
        resultBuffer[k + 1] = (byte )green;  
        resultBuffer[k + 2] = (byte )red;  
        resultBuffer[k + 3] = 255;  
    } 
 
   
    Bitmap resultBitmap = new Bitmap (subtractFrom.Width,   
                                     subtractFrom.Height);  
 
   
    BitmapData resultData =   
               resultBitmap.LockBits(new Rectangle (00,  
               resultBitmap.Width, resultBitmap.Height),  
               ImageLockMode.WriteOnly,  
               PixelFormat.Format32bppArgb);  
 
   
    Marshal.Copy(resultBuffer, 0, resultData.Scan0,   
                               resultBuffer.Length);  
 
   
    resultBitmap.UnlockBits(resultData);  
 
   
    return resultBitmap;  
} 
 

Matrix Definition

The image blurring  filters implemented by the sample source code relies on static / values defined in the Matrix class. The variants of  implemented are: 3×3 , 5×5 Gaussian, 3×3 Mean and 5×5 Mean. The definition of the Matrix class is detailed by the following code snippet:

C#
public static class Matrix 
{ 
    public static double[,] Gaussian3x3 
    { 
        get 
        { 
            return new double[,] 
            { { 121, },  
              { 242, },  
              { 121, }, }; 
        } 
    } 
 
   
    public static double[,] Gaussian5x5Type1 
    { 
        get 
        { 
            return new double[,] 
            { { 20405042 },  
              { 40912094 }, 
              { 51215125 }, 
              { 40912094 }, 
              { 20405042 }, }; 
        } 
    } 
 
   
    public static double[,] Mean3x3 
    { 
        get 
        { 
            return new double[,] 
            { { 111, },  
              { 111, },  
              { 111, }, }; 
        } 
    } 
 
   
    public static double[,] Mean5x5 
    { 
        get 
        { 
            return new double[,] 
            { { 11111 },  
              { 11111 }, 
              { 11111 }, 
              { 11111 }, 
              { 11111 }, }; 
        } 
    } 
}
 

Implementing Image Unsharpening

This article explores four variants of , relating to the four types of image blurring discussed in the previous section. The sample source code defines the following  :UnsharpGaussian3x3UnsharpGaussian5x5UnsharpMean3x3 and UnsharpMean5x5. All four methods are defined as targeting the  class. When looking at the sample images in the following section you will notice the correlation between increased  and enhanced . The definition as follows:

C#
public static Bitmap UnsharpGaussian3x3(  
                                 this Bitmap sourceBitmap,   
                                 float factor = 1.0f)  
{ 
    Bitmap blurBitmap = ExtBitmap.ConvolutionFilter(  
                                  sourceBitmap,   
                                  Matrix.Gaussian3x3,   
                                  1.0 / 16.0);  
 
    
    Bitmap resultBitmap =   
           sourceBitmap.SubtractAddFactorImage(  
                        blurBitmap, factor);  
 
    
    return resultBitmap;  
 }  
 
    
public static Bitmap UnsharpGaussian5x5(  
                                 this Bitmap sourceBitmap,  
                                 float factor = 1.0f)  
{ 
    Bitmap blurBitmap = ExtBitmap.ConvolutionFilter(  
                                  sourceBitmap,   
                                  Matrix.Gaussian5x5Type1,   
                                  1.0 / 159.0);  
 
    
    Bitmap resultBitmap =  
           sourceBitmap.SubtractAddFactorImage(  
                        blurBitmap, factor);  
 
    
    return resultBitmap;  
}  
  
public static Bitmap UnsharpMean3x3(  
                                 this Bitmap sourceBitmap,  
                                 float factor = 1.0f)  
{  
    Bitmap blurBitmap = ExtBitmap.ConvolutionFilter(  
                                  sourceBitmap,   
                                  Matrix.Mean3x3,   
                                  1.0 / 9.0);  
 
    
    Bitmap resultBitmap =  
           sourceBitmap.SubtractAddFactorImage(  
                        blurBitmap, factor);  
 
    
    return resultBitmap;  
} 
 
    
public static Bitmap UnsharpMean5x5(  
                                 this Bitmap sourceBitmap,  
                                 float factor = 1.0f)  
{  
    Bitmap blurBitmap = ExtBitmap.ConvolutionFilter(  
                                  sourceBitmap,   
                                  Matrix.Mean5x5,   
                                  1.0 / 25.0);  
 
    
    Bitmap resultBitmap =  
           sourceBitmap.SubtractAddFactorImage(  
                        blurBitmap, factor);  
 
    
    return resultBitmap;  
}
 

Sample Images

The  used in rendering the sample images shown in this article is licensed under the Creative CommonsAttribution-Share Alike 3.0 Unported license and can be  from :

The Original Image

Unsharp Gaussian 3×3

Unsharp Gaussian 5×5

Unsharp Mean 3×3

Unsharp Gaussian 5×5

 

Source Code Files

  • ExtBitmap.cs - Contains the definition of the ConvolutionFilter and SubtractAddFactorImage extension methods.
  • Matrix.cs - Provides the definition of convolution matrix/kernel values.
  • MainForm.cs - Windows Forms sample application which implements various forms of Image Unsharp Masking.

More Information

This article is based on an article originally posted on my bloghttp://softwarebydefault.com/2013/05/18/image-unsharp-mask/If you have any questions/comments please feel free to make use of the Q&A section on this page, also please remember to rate this article.