SomeshShuffle SomeshShuffle - 16 days ago 15
Android Question

How to detect bullet holes on the target using Android Opencv

I am working in a project where I have to scan the target and recognize the holes in the target and have to score according to the shots. I am not aware of the exact code how to recognize the holes in the target. I imported the opencv library and gone through a program where if I touch it will recognize the corresponding color. Now I am stuck in the coding part. Here is the screenshot of the target sheet that is given to me.

enter image description here

Could anyone please help me how to proceed further. Thanks in advance.

Answer

To do what You want You should:

1) find white areas with max brightness;
2) find bounding contours of areas with max brightness (from p.1);
3) find bounding boxes for contours from p.2;
4) count bounding boxes.

and also take into account some special cases, like "twin" holes in your image.

To implement that steps on Android easiest way is to use OpenCV. How to add it to your project well described here (You should do some work to do to it: download SDK from here and add it correctly). Then You should take a look at some tutorial about using OpenCV in Android, for example, official. And than, You can use code like this (your image added to drawable folder of demo project as target.png):

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    private ImageView mImageView;
    private Button mProcessButton;

    private Mat mSourceImageMat;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.target_image_view);
        mProcessButton = (Button) findViewById(R.id.process_button);
        mProcessButton.setVisibility(View.INVISIBLE);

        mProcessButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                processImage();
            }
        });
    }

    private void processImage() {
        try {
            mSourceImageMat = Utils.loadResource(this, R.drawable.target);
            Bitmap bm = Bitmap.createBitmap(mSourceImageMat.cols(), mSourceImageMat.rows(),Bitmap.Config.ARGB_8888);

            final Mat mat = new Mat();
            final List<Mat> channels = new ArrayList<>(3);

            mSourceImageMat.copyTo(mat);

            // split image channels: 0-H, 1-S, 2-V
            Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2HSV);
            Core.split(mat, channels);
            final Mat frameV = channels.get(2);

            // find white areas with max brightness
            Imgproc.threshold(frameV, frameV, 245, 255, Imgproc.THRESH_BINARY);

            // find contours
            List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
            Imgproc.findContours(frameV, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

            // find average contour area for "twin" hole detection
            double averageArea = 0;
            int contoursCount = 0;
            Iterator<MatOfPoint> each = contours.iterator();
            while (each.hasNext()) {
                averageArea += Imgproc.contourArea(each.next());
                contoursCount++;
            }
            if (contoursCount != 0) {
                averageArea /= contoursCount;
            }

            int holesCount = 0;
            each = contours.iterator();
            while (each.hasNext()) {
                MatOfPoint contour = each.next();

                MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
                RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);
                Point rect_points[] = new Point[4];

                boundingRect.points(rect_points);
                for(int i=0; i<4; ++i){
                    Imgproc.line(mSourceImageMat, rect_points[i], rect_points[(i+1)%4], new Scalar(255,0,0), 2);
                }
                holesCount++;

                Imgproc.putText(mSourceImageMat, Integer.toString(holesCount), new Point(boundingRect.center.x + 20, boundingRect.center.y),
                        Core.FONT_HERSHEY_PLAIN, 1.5 ,new  Scalar(255, 0, 0));

                // case of "twin" hole (like 9 & 10) on image
                if (Imgproc.contourArea(contour) > 1.3f * averageArea) {
                    holesCount++;
                    Imgproc.putText(mSourceImageMat, ", " + Integer.toString(holesCount), new Point(boundingRect.center.x + 40, boundingRect.center.y),
                            Core.FONT_HERSHEY_PLAIN, 1.5 ,new  Scalar(255, 0, 0));
                }

            }

            // convert to bitmap:
            Utils.matToBitmap(mSourceImageMat, bm);
            mImageView.setImageBitmap(bm);

            // release
            frameV.release();
            mat.release();

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


    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_1_0, this, mOpenCVLoaderCallback);
    }

    private BaseLoaderCallback mOpenCVLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");
                    mProcessButton.setVisibility(View.VISIBLE);
                } break;
                default: {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };
}

And if You press FIND HOLES Button You get result like this

result

For other images You should adjust 245, 255 values in

Imgproc.threshold(frameV, frameV, 245, 255, Imgproc.THRESH_BINARY);

line.

Update: MainActivity layout (activity_main.xml):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/target_image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitCenter"
        app:srcCompat="@drawable/target"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_above="@+id/process_button"/>

    <Button
        android:id="@+id/process_button"
        android:text="Find holes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true"/>

</RelativeLayout>
Comments