问题
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.
Could anyone please help me how to proceed further. Thanks in advance.
回答1:
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
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>
来源:https://stackoverflow.com/questions/40607554/how-to-detect-bullet-holes-on-the-target-using-android-opencv