OpenCV is a library for computer vision. This library has many methods using which you can easily work with and manipulate images and videos. In this tutorial I would cover basic setup for OpenCv and we will try to work with an image to add some effect.

 

Download OpenCV

To begin, you would have to download the latest version of OpenCV

Import OpenCv Module into your Android Project

  • Extract the downloaded zip file.
  • Create a new Android project or use any existing one.
  • Then import OpenCV Module using File ->New -> Import Module, now browse to OpenCV-android-sdk/sdk/java path in your extracted folder.
  • Click on Next and Finish to complete importing OpenCV sdk.

 

If studio shows some error at this point. Ignore them.

Set gradle files for app and OpenCv to same sdk versions

Once you import OpenCV module, you would have an additional gradle file in your project. This gradle files belongs to OpenCV module and would have old sdk version. Copy values of compileSdkVersion, buildToolsVersion, minSdkVersion and targetSdkVersion from app gradle file to OpenCV gradle file.

Add OpenCV as module dependency

Right Click on the "app" and select Open Module Settings.

Go to "Dependencies" Tab. Click on "+" icon and add "openCVLibrary310".

Add libopencv_java3.so file to project

We need to copy libopencv_java3.so files from OpenCV sdk to our project.

  1.  Create a new folder jniLibs under app-> src -> main.
  2. In OpenCV SDK directory that we had extracted earlier go to sdk/sdk/native/libs directory.
  3. This directory has listing of various CPU architectures. Copy all the folders into  jniLibs directory. 
  4. Once copied, go inside each directory and remove all files except libopencv_java3.so
  5. Open your gradle.properties file and enter the following code - android.useDeprecatedNdk=true

 

 

Check if OpenCv is loaded

Copy the following code in your MainActivity.java to test whether OpenCV is getting loaded successfully.

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    static {
        if(!OpenCVLoader.initDebug()){
            Log.d(TAG, "OpenCV not loaded");
        } else {
            Log.d(TAG, "OpenCV loaded");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

}

Working With OpenCV

Now that OpenCv is successfully loaded, we will see how we can work on Images using it.

OpenCv handles image as Matrix Object. So in this exercise, we would open an image and convert into Mat Object and manipulate it's pixel values to add effects to it.

Integrate a Image Picker Library

Here I am using a one ImagePicker library from Github. You can choose whatever you are comfortable with.

mImage = (ImageView) findViewById(R.id.image);
mImage.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        //Open Image Picker
        ImagePicker.pickImage(MainActivity.this, "Select your image:");
    }
});

 

Convert Bitmap to Mat Object

onActivityResult() we get the image bitmap. Now we convert the Bitmap to a Mat Object. Since getting pixel values through Mat Object is a costly operation we also create a multi dimensional double array to store values on Mat Object.

 

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK && data != null && data.getData() != null){

        Bitmap bitmap = ImagePicker.getImageFromResult(this, requestCode, resultCode, data);
        String filename = "current.png";
        File sd = Environment.getExternalStorageDirectory();
        File dest = new File(sd, filename);

        FileOutputStream out = null;
        try {
            out = new FileOutputStream(dest);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
            out.flush();
            out.close();
            mImage.setImageBitmap(bitmap);

            mRgba = Imgcodecs.imread(dest.getPath());
            int rowCount = mRgba.rows();
            int colCount = mRgba.cols();

            rgbImageDouble = new double[rowCount][colCount][4];
            for (int i = 0; i < rowCount-1; i++) {
                for (int j = 0; j < colCount; j++) {
                    rgbImageDouble[i][j] = mRgba.get(i,j);
                }
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Working with a Mat Object

You can use mRgba.get(i,j) to get the color value at  pixel i,j and use mRgba.put(i,j,color[]) to put the color value at pixel i, j. This was you can make any kind of manipulations to the image.

 

 

Creating Prisma Like Comic Effect

 

Using above method I am going to make manipulations to the Mat object to create Prisma like comic effect. The idea behind comic effect is to merge similar colors together to as a single plain color. To manage the amount of effect we would use a seekbar. 

This is how it would work, if the value of seekbar is 20, all red values from range 0-20 will fall to 0, from 20-40 will fall to 20 and from 40 to 60 will fall to 40 and so on till 255 (0-255 is range of RGB values).

I'll add some value as brightness so that the image does not turn out too dark.

 

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
    int progress = seekBar.getProgress();
    int rowCount = mRgba.rows();
    int colCount = mRgba.cols();
    int brightness = 20;
    for (int i = 0; i < rowCount-1; i++) {
        for (int j = 0; j < colCount; j++) {
            double[] color = mRgba.get(i, j);
            color[2] = rgbImageDouble[i][j][0] - (rgbImageDouble[i][j][0]%progress) + brightness;
            color[1] = rgbImageDouble[i][j][1] - (rgbImageDouble[i][j][1]%progress) + brightness;
            color[0] = rgbImageDouble[i][j][2] - (rgbImageDouble[i][j][2]%progress) + brightness;
            mRgba.put(i,j,color);
        }
    }
    // convert to bitmap:
    Bitmap bm = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(),Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(mRgba, bm);

    mImage.setImageBitmap(bm);

}

Output (Screenshots)

Keep playing around with the pixels and you can come up with some crazy effects