Home > Tutorials > Image Processing – Convolution Matrix

Image Processing – Convolution Matrix


It’s kinda hard to explain about Convolution, so I just pick up several articles which would be helpful for you for references.

Convolution on Wikipedia

GIMP Documentation on Convolution Matrix <— really detailed on explanation.

Convolution Study by Ahn Song Ho

VcsKicks.Com – Convolution Application on Box Blur effect

CodeProject – Image Processing for Dummies (Part II – Convolution Filters)

RoboRealms – Short article on Convolution

You can find more on Internet for details. However, you can see that Convolution is applied very widely in image processing.

Some image effects are better to implement using Convolution Matrix method like: Gaussian Blur, Sharpening, Embossing…

Ok, I write this post just to introduce about Convolution and prepare for the next several articles on image processing, cool effects!

At the very first, you need a base for computing Convolution Matrix on Android, don’t you?

This is my implementation:

package pete.android.study;

import android.graphics.Bitmap;
import android.graphics.Color;

public class ConvolutionMatrix
{
    public static final int SIZE = 3;

    public double[][] Matrix;
    public double Factor = 1;
    public double Offset = 1;

    public ConvolutionMatrix(int size) {
        Matrix = new double[size][size];
    }

    public void setAll(double value) {
        for (int x = 0; x < SIZE; ++x) {
            for (int y = 0; y < SIZE; ++y) {
                Matrix[x][y] = value;
            }
        }
    }

    public void applyConfig(double[][] config) {
    	for(int x = 0; x < SIZE; ++x) {
    		for(int y = 0; y < SIZE; ++y) {
    			Matrix[x][y] = config[x][y];
    		}
    	}
    }

    public static Bitmap computeConvolution3x3(Bitmap src, ConvolutionMatrix matrix) {
    	int width = src.getWidth();
    	int height = src.getHeight();
    	Bitmap result = Bitmap.createBitmap(width, height, src.getConfig());

    	int A, R, G, B;
    	int sumR, sumG, sumB;
    	int[][] pixels = new int[SIZE][SIZE];

    	for(int y = 0; y < height - 2; ++y) {
    		for(int x = 0; x < width - 2; ++x) {

    			// get pixel matrix
    			for(int i = 0; i < SIZE; ++i) {
    				for(int j = 0; j < SIZE; ++j) {
    					pixels[i][j] = src.getPixel(x + i, y + j);
    				}
    			}

    			// get alpha of center pixel
    			A = Color.alpha(pixels[1][1]);

    			// init color sum
    			sumR = sumG = sumB = 0;

    			// get sum of RGB on matrix
    			for(int i = 0; i < SIZE; ++i) {
    				for(int j = 0; j < SIZE; ++j) {
    					sumR += (Color.red(pixels[i][j]) * matrix.Matrix[i][j]);
    					sumG += (Color.green(pixels[i][j]) * matrix.Matrix[i][j]);
    					sumB += (Color.blue(pixels[i][j]) * matrix.Matrix[i][j]);
    				}
    			}

    			// get final Red
    			R = (int)(sumR / matrix.Factor + matrix.Offset);
    			if(R < 0) { R = 0; }
    			else if(R > 255) { R = 255; }

    			// get final Green
    			G = (int)(sumG / matrix.Factor + matrix.Offset);
    			if(G < 0) { G = 0; }
    			else if(G > 255) { G = 255; }

    			// get final Blue
    			B = (int)(sumB / matrix.Factor + matrix.Offset);
    			if(B < 0) { B = 0; }
    			else if(B > 255) { B = 255; }

    			// apply new pixel
    			result.setPixel(x + 1, y + 1, Color.argb(A, R, G, B));
    		}
    	}

    	// final image
    	return result;
    }
}

We will move to some nice effects on next articles!

Hope you like it!

Cheers,
Pete Houston

  1. ravi
    September 21, 2018 at 11:48 am

    This article make me to quick implementation in my project TQ.

  2. October 22, 2016 at 10:40 pm

    //i think this one fast…just modify it for the your needs….

    public static Bitmap DoFX_Blur(Bitmap hBmp) {
    if ((isLoaded = true) & (hBitmap != hBmp)) {
    biData = null;
    hBitmap = null;
    isLoaded = false;
    bWidth = 0;
    bHeight = 0;
    CurFX = “”;
    }
    CurFX = “Blur”;
    hBitmap = hBmp;
    bWidth = hBitmap.getWidth();
    bHeight = hBitmap.getHeight();
    int tmp[] = new int[bWidth * bHeight];
    int[][] biData = new int[bWidth][bHeight];
    hBitmap.getPixels(tmp, 0, bWidth, 0, 0, bWidth, bHeight);
    int offset = 2;
    for (int y = 0; y < biData[1].length ; ++y) {
    for (int x = 0; x < biData.length ; ++x) {
    biData[x][y] = tmp[x + y * biData.length];
    }
    }

    //int iy,iw,iiy,iix;
    int nR, nG, nB;
    int tR, tG, tB;
    for (int iy = 1; iy < biData[1].length – 1; ++iy) {
    for (int ix = 1; ix < biData.length – 1; ++ix) {
    nR = 0;
    nG = 0;
    nB = 0;
    for (int iiy = -1; iiy < 1; ++iiy) {
    for (int iix = -1; iix -1) | ((iy + iiy) > -1)) {
    nB += Color.blue(biData[ix + iix][iy + iiy]);
    nG += Color.green(biData[ix + iix][iy + iiy]);
    nR += Color.red(biData[ix + iix][iy + iiy]);
    }
    }
    }
    tB = Color.blue(biData[ix][iy]);
    tG = Color.green(biData[ix][iy]);
    tR = Color.red(biData[ix][iy]);
    int A = Color.alpha(biData[ix][iy]);
    nB = ((tB + nB) / 5);
    nG = ((tG + nG) / 5);
    nR = ((tR + nR) / 5);
    if (A > 0) {
    biData[ix][iy] = Color.rgb(nR, nG, nB);
    } else {

    biData[ix][iy] = Color.rgb(255, 255, 255);
    }
    }
    }

    for (int y = 0; y < biData[1].length-1 ; ++y) {
    for (int x = 0; x < biData.length-1 ; ++x) {
    tmp[x + y * biData.length] = biData[x+1][y+1];
    }
    }

    hBitmap.setPixels(tmp, 0, bWidth, 0, 0, bWidth, bHeight);
    isLoaded = true;
    return hBitmap;
    }

    • October 22, 2016 at 11:09 pm

      don’t know how to edit post… public class BiFX {
      static int[][] biData;
      static Bitmap hBitmap;
      static int bWidth;
      static int bHeight;
      static boolean isLoaded = false;
      static String CurFX = “”;

      public static Bitmap DoFX_Blur(Bitmap hBmp) {
      if ((isLoaded = true) & (hBitmap != hBmp)) {
      biData = null;
      hBitmap = null;
      isLoaded = false;
      bWidth = 0;
      bHeight = 0;
      CurFX = “”;
      }
      CurFX = “Blur”;
      hBitmap = hBmp;
      bWidth = hBitmap.getWidth();
      bHeight = hBitmap.getHeight();
      int tmp[] = new int[bWidth * bHeight];
      int[][] biData = new int[bWidth][bHeight];
      hBitmap.getPixels(tmp, 0, bWidth, 0, 0, bWidth, bHeight);
      for (int y = 0; y < biData[1].length ; ++y) {
      for (int x = 0; x < biData.length ; ++x) {
      biData[x][y] = tmp[x + y * biData.length];
      }
      }

      //int iy,iw,iiy,iix;
      int nR, nG, nB;
      int tR, tG, tB;
      for (int iy = 1; iy < biData[1].length – 1; ++iy) {
      for (int ix = 1; ix < biData.length – 1; ++ix) {
      nR = 0;
      nG = 0;
      nB = 0;
      for (int iiy = -1; iiy < 1; ++iiy) {
      for (int iix = -1; iix -1) | ((iy + iiy) > -1)) {
      nB += Color.blue(biData[ix + iix][iy + iiy]);
      nG += Color.green(biData[ix + iix][iy + iiy]);
      nR += Color.red(biData[ix + iix][iy + iiy]);
      }
      }
      }
      tB = Color.blue(biData[ix][iy]);
      tG = Color.green(biData[ix][iy]);
      tR = Color.red(biData[ix][iy]);
      int A = Color.alpha(biData[ix][iy]);
      nB = ((tB + nB) / 5);
      nG = ((tG + nG) / 5);
      nR = ((tR + nR) / 5);
      if (A > 0) {
      biData[ix][iy] = Color.rgb(nR, nG, nB);
      } else {

      biData[ix][iy] = Color.rgb(255, 255, 255);
      }
      }
      }

      for (int y = 0; y < biData[1].length-1 ; ++y) {
      for (int x = 0; x < biData.length-1 ; ++x) {
      tmp[x + y * biData.length] = biData[x+1][y+1];
      }
      }

      hBitmap.setPixels(tmp, 0, bWidth, 0, 0, bWidth, bHeight);
      isLoaded = true;
      return hBitmap;
      }
      }

  3. Abdulhamid ALbayoumi
    June 30, 2015 at 1:32 am

    thank you very much

    but why it takes long time to apply the effect

  4. eitosei
    March 28, 2014 at 8:20 am

    hi, is it possible to create depth mapping with the use of convolution matrix?

  5. November 5, 2012 at 11:45 pm
  6. Yuvi
    October 29, 2012 at 5:34 pm

    I use the following kernel matrix for emboss effect ,but not get correct output

    2,0,0
    0,-1,0
    0,0,-1 how can i get the matrix for emboss masking filter .

  7. Ubik
    April 21, 2012 at 6:53 am

    Here is an optimized version of your code, based on android.graphics.Matrix; caching pixels really speeds up the computation. Also, by cloning original pixels, we don’t loose last two rows and columns.

    private static final int MATRIX_SIZE = 3;

    private static int cap(int color) {
    if (color 255)
    return 255;
    else
    return color;
    }

    public static Bitmap convolute(Bitmap bmp, Matrix mat, float factor, int offset) {
    // get matrix values
    float [] mxv = new float[MATRIX_SIZE * MATRIX_SIZE];
    mat.getValues(mxv);
    // cache source pixels
    int width = bmp.getWidth();
    int height = bmp.getHeight();
    int [] scrPxs = new int[width * height];
    bmp.getPixels(scrPxs, 0, width, 0, 0, width, height);

    // clone source pixels in an array
    // here we’ll store results
    int [] rtPxs = scrPxs.clone();

    int r, g, b;
    int rSum, gSum, bSum;
    int idx; // current pixel index
    int pix; // current pixel
    float mv; // current matrix value

    for(int x = 0, w = width – MATRIX_SIZE + 1; x < w; ++x) {
    for(int y = 0, h = height – MATRIX_SIZE + 1; y < h; ++y) {
    idx = (x + 1) + (y + 1) * width;

    rSum = gSum = bSum = 0;
    for(int mx = 0; mx < MATRIX_SIZE; ++mx) {
    for(int my = 0; my < MATRIX_SIZE; ++my) {

    pix = scrPxs[(x + mx) + (y + my) * width];
    mv = mxv[mx + my * MATRIX_SIZE];

    rSum += (Color.red(pix) * mv);
    gSum += (Color.green(pix) * mv);
    bSum += (Color.blue(pix) * mv);
    }
    }

    r = cap((int)(rSum / factor + offset));
    g = cap((int)(gSum / factor + offset));
    b = cap((int)(bSum / factor + offset));
    // store computed pixel
    rtPxs[idx] = Color.argb(Color.alpha(scrPxs[idx]), r, g, b);
    }
    }

    // return bitmap with transformed pixels
    return Bitmap.createBitmap(rtPxs, width, height, bmp.getConfig());
    }

    cheers !

    • April 23, 2012 at 1:15 am

      as the matrix is added to the Matrix object?

    • October 29, 2012 at 10:02 pm

      The cap function is incorrect. It should be:

      private static int cap(int color) {
      if (color > 255)
      return 255;
      else if (color < 0)
      return 0;
      else
      return color;
      }

      Beside this – fantastic algorithm – I was looking for it for a long time!
      Thanks

      • August 9, 2014 at 6:33 pm

        thanks ! I didn’t consider a negative offset

    • Dzung
      July 17, 2014 at 11:59 am

      Ubik,

      You are awesome, you know? Your optimized code helps me alot, my app run faster.

      Many thanks!

    • Anti
      August 4, 2014 at 7:51 pm

      I get an error in eclipse at the first for loop saying:
      “The primitive type int of width does not have a field MATRIX_SIZE”
      How can I solve this?

      • August 9, 2014 at 6:29 pm

        you probably missed the const declaration

        private static final int MATRIX_SIZE = 3;

    • September 15, 2014 at 10:50 pm

      On average how long should this effect take to process. On a 500×700 picture its taking around a minute on my end. Is that right? Would it be possible to see a full example?

    • February 18, 2015 at 1:32 pm

      Though, do pay attention to what convolution you’re using. If you’re doing a box blur or a gaussian blur or basically any of the ones where the direction doesn’t matter. You can more quickly apply the operation as a 1×3 convolution and then a 3×1 convolution without creating a new object at all. Though ideally you’d be feeding all this stuff to the GPU for basically free.

  8. Aniket Bhosale
    April 7, 2012 at 6:53 pm

    How to implement it in Blackberry.
    I want emboss image effects and sketch effect in blackberry.

  9. Aniket Bhosale
    April 7, 2012 at 6:49 pm

    how to implement it in Blackberry.I want to do emboss effect and sketch effect.

  10. Kaori
    December 14, 2011 at 3:23 pm

    /*Gaussian Blur
    1 2 1
    2 4 2
    1 2 1 /16+0 */

    public Bitmap gaussianBlur(Bitmap src) {
    double[][] EmbossConfig = new double[][] {
    { 1 , 2, 1 },
    { 2 , 4, 2 },
    {1 , 2, 1 }
    };
    ConvolutionMatrix convMatrix = new ConvolutionMatrix(3);
    convMatrix.applyConfig(EmbossConfig);
    convMatrix.Factor = 16;
    convMatrix.Offset = 0;
    return ConvolutionMatrix.computeConvolution3x3(src, convMatrix);
    }

    You can apply the upper code. it is ok.

  11. Erez Posner
    August 6, 2011 at 5:29 pm

    I have used your computeConvolution3x3 in my project, have you encounterd delays?

  12. June 22, 2011 at 8:45 pm

    Oooops…my serious mistake, I’ve just fixed a found error on my ConvolutionMatrix.computeConvolution3x3().
    You might want to update the source code and run it again 🙂

  13. Junior
    June 22, 2011 at 8:42 pm

    I am using your function as below, but it seems that i am doing something wrong. I am using this value to get a gaussian blur effect, like in http://www.codeproject.com/KB/GDI-plus/csharpfilters.aspx

    ConvolutionMatrix matrix = new ConvolutionMatrix(3);
    double config[][] = {{1.0,2.0,1.0},{2.0D,4.0D,2.0D},{1.0,2.0,1.0}};
    matrix.applyConfig(config);
    Bitmap tempBitmap = computeConvolution3x3(mBitmap, matrix);

    And I am always getting a white image

  1. No trackbacks yet.

Leave a comment