com.github.mbillingr.correlationcheck.ImageProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.github.mbillingr.correlationcheck.ImageProcessor.java

Source

/*
Copyright (c) 2015-2016 Martin Billinger
    
This file is part of the "Correlation Check" App.
    
The "Correlation Check" App is free software: you can redistribute it and/or modifyit under the
terms of the GNU General Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
    
You should have received a copy of the GNU General Public License along with this program.  If not,
see <http://www.gnu.org/licenses/>.
*/

package com.github.mbillingr.correlationcheck;

import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;

import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.utils.Converters;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ImageProcessor {
    int work_width = 512;
    int work_height = 512;

    int raw_width, raw_height;

    private int debugcounter = 0;
    private boolean enable_debug = true;

    private static ImageProcessor mInstance = null;

    private File mImageFile = null;

    private Mat perspective_transform = null;
    private Mat working_image = null;

    File storageDir;

    public static ImageProcessor getInstance() {
        if (mInstance == null) {
            mInstance = new ImageProcessor();
        }
        return mInstance;
    }

    private ImageProcessor() {
        OpenCVLoader.initDebug();

        storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        mImageFile = new File(storageDir, "scatter.jpg");
    }

    public File getmImageFile() {
        return mImageFile;
    }

    Bitmap matToBitmap(Mat input) {
        if (input == null) {
            return Bitmap.createBitmap(0, 0, Bitmap.Config.ARGB_8888);
        }
        Mat tmp = new Mat();
        if (input.channels() == 1) {
            Imgproc.cvtColor(input, tmp, Imgproc.COLOR_GRAY2RGB);
        } else {
            Imgproc.cvtColor(input, tmp, Imgproc.COLOR_BGR2RGB);
        }
        Core.transpose(tmp, tmp);
        Core.flip(tmp, tmp, 1);

        Bitmap bm = Bitmap.createBitmap(tmp.cols(), tmp.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(tmp, bm);
        return bm;
    }

    public Bitmap getRawBitmap(int w, int h) {
        Log.i("info", "reading " + mImageFile.getAbsolutePath());
        Mat image = Imgcodecs.imread(mImageFile.getAbsolutePath());

        raw_width = image.width();
        raw_height = image.height();

        Imgproc.resize(image, image, new Size(h, w));
        return matToBitmap(image);
    }

    public Bitmap getImageBitmap() {
        return matToBitmap(working_image);
    }

    public Bitmap getImageBitmap(int w, int h) {
        Mat tmp = new Mat();
        Imgproc.resize(working_image, tmp, new Size(h, w));
        return matToBitmap(tmp);
    }

    public List<Point> extractPoints() {
        Mat gray = new Mat();//work_width, work_height, CvType.CV_8UC1);
        Mat binary = new Mat();

        Mat kernel = Mat.ones(3, 3, CvType.CV_8UC1);

        debugreset();

        Mat image = load_transformed();
        working_image = image.clone();
        debugsave(image, "source");

        Imgproc.cvtColor(image, gray, Imgproc.COLOR_RGB2GRAY);
        debugsave(gray, "grayscale");

        Imgproc.GaussianBlur(gray, gray, new Size(15, 15), 0);
        debugsave(gray, "blurred");

        //Imgproc.equalizeHist(gray, gray);
        //debugsave(gray, "equalized");

        Imgproc.adaptiveThreshold(gray, binary, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV,
                129, 5);
        //Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_OTSU);
        //Imgproc.threshold(gray, binary, 128, 255, Imgproc.THRESH_BINARY_INV);
        debugsave(binary, "binary");

        Imgproc.morphologyEx(binary, binary, Imgproc.MORPH_CLOSE, kernel);
        debugsave(binary, "closed");

        Imgproc.morphologyEx(binary, binary, Imgproc.MORPH_OPEN, kernel);
        debugsave(binary, "opened");

        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); // is binary is now changed
        Imgproc.drawContours(image, contours, -1, new Scalar(0, 0, 255), 3);
        debugsave(image, "contours");

        List<PointAndArea> points = new ArrayList<>();

        for (MatOfPoint cnt : contours) {
            MatOfPoint2f c2f = new MatOfPoint2f();
            c2f.fromArray(cnt.toArray());
            RotatedRect rr = Imgproc.minAreaRect(c2f);

            double area = Imgproc.contourArea(cnt);

            if (rr.size.width / rr.size.height < 3 && rr.size.height / rr.size.width < 3 && rr.size.width < 64
                    && rr.size.height < 64 && area > 9 && area < 10000) {
                points.add(new PointAndArea((int) area, rr.center));
            }
        }

        List<Point> final_points = new ArrayList<>();

        Collections.sort(points);
        Collections.reverse(points);
        int prev = -1;
        for (PointAndArea p : points) {
            Log.i("area", Integer.toString(p.area));
            if (prev == -1 || p.area >= prev / 2) {
                prev = p.area;
                Imgproc.circle(image, p.point, 10, new Scalar(0, 255, 0), 5);
                final_points.add(new Point(1 - p.point.y / work_height, 1 - p.point.x / work_width));
            }
        }
        debugsave(image, "circles");

        return final_points;
    }

    private void debugreset() {
        debugcounter = 0;
    }

    private void debugsave(Mat image, String name) {
        if (!enable_debug)
            return;
        debugcounter++;
        File file = new File(storageDir, String.format("scatter_%02d_%s.jpg", debugcounter, name));
        Imgcodecs.imwrite(file.getAbsolutePath(), image);
    }

    private Mat load_transformed() {
        Mat image = Imgcodecs.imread(mImageFile.getAbsolutePath());
        Imgproc.warpPerspective(image, image, perspective_transform, new Size(work_width, work_height),
                Imgproc.INTER_CUBIC);
        return image;
    }

    void setPerspectiveCorrection(float ax, float ay, float bx, float by, float cx, float cy, float dx, float dy) {

        List<Point> pts_in = new ArrayList<>();
        pts_in.add(new Point(ax, ay));
        pts_in.add(new Point(bx, by));
        pts_in.add(new Point(cx, cy));
        pts_in.add(new Point(dx, dy));

        setPerspectiveCorrection(pts_in);
    }

    void setPerspectiveCorrection(List<Point> refpoints) {
        List<Point> pts_in = new ArrayList<>();
        for (Point p : refpoints) {
            pts_in.add(new Point(p.x * raw_width, (1 - p.y) * raw_height));
        }
        Mat mat_src = Converters.vector_Point2f_to_Mat(pts_in);

        List<Point> pts_out = new ArrayList<>();
        pts_out.add(new Point(0, work_height));
        pts_out.add(new Point(0, 0));
        pts_out.add(new Point(work_width, work_height));
        pts_out.add(new Point(work_width, 0));
        Mat mat_dst = Converters.vector_Point2f_to_Mat(pts_out);

        perspective_transform = Imgproc.getPerspectiveTransform(mat_src, mat_dst);
    }
}

class PointAndArea implements Comparable<PointAndArea> {

    public int area;
    public Point point;

    PointAndArea(int area, Point point) {
        this.area = area;
        this.point = point;
    }

    @Override
    public int compareTo(PointAndArea other) {
        return this.area - other.area;
    }
}