Чтобы сделать то, что Вы хотите, Вы должны:
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.
, а также принимать во внимание некоторые особые случаи, как «близнецы» дырками в изображении.
Чтобы реализовать эти шаги на Android, проще всего использовать OpenCV. Как добавить его в свой проект, хорошо описанный here (вам нужно сделать некоторую работу, чтобы сделать с ним: загрузить SDK от here и добавить его правильно). Затем вы должны взглянуть на некоторый учебник об использовании OpenCV в Android, например, official. И чем, вы можете использовать код, как это (ваш образ добавлен в drawable
папку демонстрационного проекта как 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;
}
}
};
}
И если Вы нажмете FIND HOLES
Button
Вы получаете результат, как этот

Для других изображений Вы должны отрегулировать значения 245, 255
в
Imgproc.threshold(frameV, frameV, 245, 255, Imgproc.THRESH_BINARY);
линия.
Обновление: MainActivity
макет (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>
я мог бы найти такую же проблему в другом вопросе, но он использует в питона. http://stackoverflow.com/questions/33321303/how-to-detect-bullet-holes-on-the-target-using-python – SomeshShuffle