Search code examples
c#image-processingimagesharp

Apply mask to image


I need to apply mask (3x3) to image manually by iterating through it and calculating new values for each pixel. My code seems to work, but there is an issue - new value might not be between 0 and 255, it can be higher or lower. Such values are not acceptable. And now I don't know how to correctly solve it.

Apparently normalization to 0-255 will work, but here I need to normalize whole set of values which are known after loop is finished. This means I need to iterate through entire image twice.

You can also rate my code, maybe something can be improved, and maybe it actually doesn't work correctly despite compiling and creating processed images.

        public Image<Rgba32> ApplyMask3x3(int[,] mask, Image<Rgba32> image)
        {
            Image<Rgba32> result = new Image<Rgba32>(image.Width, image.Height);

            for(int row = 0; row < image.Height; row++)
            {
                for(int col = 0; col < image.Width; col++)
                {
                    byte R = 0, G = 0, B = 0;

                    //temporary - forcefully throw new values to 0-255
                    int valR = ApplyMaskR3x3(col, row, image, mask); //code below
                    int valG = ApplyMaskG3x3(col, row, image, mask); //identical code as R but uses G attribute
                    int valB = ApplyMaskB3x3(col, row, image, mask); //identical code as R but uses B attribute
                    if (valR > 255) valR = 255;
                    if (valR < 0) valR = 0;
                    if (valG > 255) valG = 255;
                    if (valG < 0) valG = 0;
                    if (valB > 255) valB = 255;
                    if (valB < 0) valB = 0;

                    R = Convert.ToByte(valR);
                    G = Convert.ToByte(valG);
                    B = Convert.ToByte(valB); ;

                    result[col, row] = new Rgba32(R,G,B);
                }
            }

            return result;
        }
private int ApplyMaskR3x3(int col, int row, Image<Rgba32> image, int[,] mask)
        {
            int a, b, c;
            int d, e, f;
            int g, h, i;

            if (col == 0 || row == 0)
                a = 0;
            else
                a = image[col - 1, row - 1].R * mask[0, 0];

            if (row == 0)
                b = 0;
            else
                b = image[col, row - 1].R * mask[0, 1];

            if (row == 0 || col == image.Width - 1)
                c = 0;
            else
                c = image[col + 1, row - 1].R * mask[0, 2];


            if (col == 0)
                d = 0;
            else
                d = image[col - 1, row].R * mask[1, 0];

            e = image[col, row].R * mask[1, 1];

            if (col == image.Width - 1)
                f = 0;
            else
                f = image[col + 1, row].R * mask[1, 2];


            if (col == 0 || row == image.Height - 1)
                g = 0;
            else
                g = image[col - 1, row + 1].R * mask[2, 0];

            if (row == image.Height - 1)
                h = 0;
            else
                h = image[col, row + 1].R * mask[2, 1];

            if (col == image.Width - 1 || row == image.Height - 1)
                i = 0;
            else
                i = image[col + 1, row + 1].R * mask[2, 2];

            return a + b + c + d + e + f + g + h + i;
        }

Solution

  • You should be able to make use of the Filter processor built in to ImageSharp

    From your code sample you should be able to do this

    public Image<Rgba32> ApplyMask3x3(int[,] mask, Image<Rgba32> image)
    {
        var matrix = new ColorMatrix(
            mask[0,0],
            mask[0,1],
            mask[0,2],
            mask[1,0],
            mask[1,1],
            mask[1,2],
            mask[2,0],
            mask[2,1],
            mask[2,2]
        );
        image.Mutate(x=>x.Filter(matrix));
        return image;
    }
    

    replace .Mutate() with .Clone() if you actually need a 2nd image and don't want to mutate in place.

    If you Clone you need to make sure that both source and output images are disposed of correctly otherwise you can end up causing memory issues.